Guest User

Untitled

a guest
Jul 30th, 2015
374
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/usr/bin/python
  2. import codecs
  3. import json, re
  4. import random
  5. import sys
  6. import bitcoin as b
  7. import sys
  8. import json
  9. import os
  10. try:
  11.     from urllib.request import build_opener
  12. except:
  13.     from urllib2 import build_opener
  14.  
  15. # Timestamp of sale start: midnight CET Jul 22
  16. start = 1406066400
  17. # Initial sale rate
  18. initial_rate = 2000
  19. # Initial sale rate duration
  20. initial_period = 14 * 86400
  21. # step size for declining rate phase
  22. rate_decline = 30
  23. # Length of step
  24. rate_period = 86400
  25. # Number of declining periods
  26. rate_periods = 22
  27. # Final rate
  28. final_rate = 1337
  29. # Period during which final rate is effective
  30. final_period = 6 * 86400 + 3600  # 1h of slack
  31. # Accept post-sale purchases?
  32. post_rate = 0
  33. # Exodus address
  34. exodus = '36PrZ1KHYMpqSyAQXSG8VwbUiq2EogxLo2'
  35. # Minimum satoshis accepted
  36. minimum = 1000000
  37. # Maximum satoshis accepted
  38. maximum = 150000000000
  39.  
  40. # Create caches directory
  41. caches = {}
  42.  
  43. # Foundation address
  44. foundation_address = '5abfec25f74cd88437631a7731906932776356f9'
  45.  
  46. try:
  47.     os.mkdir('caches')
  48. except:
  49.     pass
  50.  
  51.  
  52.  
  53. INSIGHT_ADDR = 'http://178.19.221.38:3000'
  54.  
  55.  
  56. # Makes a request to a given URL (first arg) and optional params (second arg)
  57. def make_request(*args):
  58.     opener = build_opener()
  59.     opener.addheaders = [('User-agent',
  60.                           'Mozilla/5.0'+str(random.randrange(1000000)))]
  61.     try:
  62.         return opener.open(*args).read().strip()
  63.     except Exception as e:
  64.         try:
  65.             p = e.read().strip()
  66.         except:
  67.             p = e
  68.         raise Exception(p)
  69.  
  70.  
  71. # Grab history from an insight server (if desired)
  72. def insight_history(a):
  73.     hashes = json.loads(make_request(INSIGHT_ADDR + '/api/addr/'+a))["transactions"]
  74.     o = []
  75.     for i in range(0, len(hashes), 10):
  76.         h = hashes[i:i+10]
  77.         t = json.loads(make_request(INSIGHT_ADDR + '/api/multitx/'+','.join(h)))
  78.         sys.stderr.write('Getting txs: %d\n' % i)
  79.         if isinstance(t, dict):
  80.             t = [t]
  81.         for tee in t:
  82.             for i, out in enumerate(tee["vout"]):
  83.                 if a in out["scriptPubKey"]["addresses"]:
  84.                     o.append({"output": tee["txid"]+':'+str(i),
  85.                               "block_height": tee["confirmedIn"],
  86.                               "value": out["valueSat"]})
  87.     return o
  88.  
  89.  
  90. # Grab a block timestamp from an insight server (if desired)
  91. def insight_get_block_timestamp(a):
  92.     addrtail = ','.join([str(x) for x in a]) if isinstance(a, list) else str(a)
  93.     o = json.loads(make_request(INSIGHT_ADDR + '/api/blockheader-by-index/'+addrtail))
  94.     if isinstance(o, list):
  95.         return [x['time'] for x in o]
  96.     else:
  97.         return o['time']
  98.  
  99.  
  100. # Fetch a transaction from an insight server (if desired)
  101. def insight_fetchtx(a):
  102.     addrtail = ','.join(a) if isinstance(a, list) else a
  103.     return json.loads(make_request(INSIGHT_ADDR + '/api/rawmultitx/'+addrtail))
  104.  
  105. # Get our network data grabbing methods either from BCI/blockr or from insight,
  106. # depending on which one the user prefers. Use --insight to use insight or
  107. # --insight 1.2.3.4:30303 to use one's own insight server (need the custom
  108. # batch-query-compatible version from http://github.com/vbuterin/insight-api )
  109. if '--insight' in sys.argv:
  110.     ipport = (sys.argv+[None])[sys.argv.index('--insight') + 1]
  111.     if ipport:
  112.         INSIGHT_ADDR = 'http://'+ipport
  113.     _fetchtx = insight_fetchtx
  114.     _history = insight_history
  115.     _get_block_timestamp = insight_get_block_timestamp
  116. else:
  117.     _fetchtx = b.blockr_fetchtx
  118.     _history = b.history
  119.     _get_block_timestamp = b.get_block_timestamp
  120.  
  121.  
  122. # Grab the extra data command line argument
  123. if '--extradata' in sys.argv:
  124.     d = (sys.argv+[None])[sys.argv.index('--extradata') + 1]
  125.     EXTRADATA = (d[2:] if d[:2] == '0x' else d)
  126.     EXTRADATA = codecs.decode(EXTRADATA,'hex_codec')
  127. else:
  128.     EXTRADATA = ''
  129.  
  130.  
  131. # Cache methods that get networking data. Important since this script takes
  132. # a very long time, and will almost certainly be interrupted multiple times
  133. # while in progress
  134. def cache_method_factory(method, filename):
  135.     def new_method(arg):
  136.         if filename not in caches:
  137.             try:
  138.                 caches[filename] = json.load(open(filename, 'r'))
  139.             except:
  140.                 caches[filename] = {}
  141.         c = caches[filename]
  142.         if str(arg) not in c:
  143.             # Try to get the result five times
  144.             have_problem = False
  145.             for tried in range(4):
  146.                 try:
  147.                     c[str(arg)] = method(arg)
  148.                     tried = 'done'
  149.                     break
  150.                 except Exception:
  151.                     sys.stderr.write('API not returning data. Retrying\n')
  152.                     have_problem = True
  153.                     pass
  154.             if tried != 'done':
  155.                 c[str(arg)] = method(arg)
  156.             elif have_problem:
  157.                 sys.stderr.write('Data received, all good\n')
  158.             json.dump(c, open(filename, 'w'))
  159.         return c[str(arg)]
  160.     return new_method
  161.  
  162. # Cached versions of the BCI/blockr or insight methods that we need
  163. get_block_timestamp = cache_method_factory(_get_block_timestamp,
  164.                                            'caches/blocktimestamps.json')
  165. fetchtx = cache_method_factory(_fetchtx, 'caches/fetchtx.json')
  166. history = cache_method_factory(_history, 'caches/history.json')
  167.  
  168.  
  169. # Get a dictionary of the transactions and block heights, taking as input
  170. # a history produced by pybitcointools
  171. def get_txs_and_heights(outs):
  172.     txs = {}
  173.     heights = {}
  174.     for i in range(0, len(outs), 20):
  175.         txhashes = []
  176.         fetched_heights = []
  177.         for j in range(i, min(i + 20, len(outs))):
  178.             if outs[j]['output'][65:] == '0':
  179.                 txhashes.append(outs[j]['output'][:64])
  180.                 fetched_heights.append(outs[j]['block_height'])
  181.             else:
  182.                 sys.stderr.write("Non-purchase tx found (genesis output index not zero): %s\n" %
  183.                                  outs[j]['output'][:64])
  184.         fetched_txs = fetchtx(txhashes)
  185.         assert len(fetched_txs) == len(txhashes) == len(fetched_heights)
  186.         for h, tx, ht in zip(txhashes, fetched_txs, fetched_heights):
  187.             assert b.txhash(str(tx)) == h
  188.             txs[h] = tx
  189.             heights[h] = ht
  190.         sys.stderr.write('Processed transactions: %d\n' % len(txs))
  191.     return {"txs": txs, "heights": heights}
  192.  
  193.  
  194. # Produce a json list of purchases, taking as input a dictionary of
  195. # transactions and heights
  196. def list_purchases(obj):
  197.     txs, heights = obj['txs'], obj['heights']
  198.     process_queue = []
  199.     for h in txs:
  200.         txhex = str(txs[h])
  201.         txouts = b.deserialize(txhex)['outs']
  202.         if len(txouts) >= 2 and txouts[0]['value'] >= minimum - 30000:
  203.             addr = b.script_to_address(txouts[0]['script'])
  204.             if addr == exodus:
  205.                 v = txouts[0]['value'] + 30000
  206.                 process_queue.append({
  207.                     "tx": h,
  208.                     "addr": b.b58check_to_hex(b.script_to_address(
  209.                                               txouts[1]['script'])),
  210.                     "value": v,
  211.                     "height": heights[h]
  212.                 })
  213.             else:
  214.                 sys.stderr.write("Non-purchase tx found (not to exodus): %s\n" % h)
  215.         elif len(txouts) == 1:
  216.             sys.stderr.write("Non-purchase tx found (single output): %s\n" % h)
  217.         else:
  218.             sys.stderr.write("Non-purchase tx found (insufficient value): %s\n" % h)
  219.     sys.stderr.write('Gathered outputs, collecting block timestamps\n')
  220.     # Determine the timestamp for every block height. We care about
  221.     # the timestamp of the previous confirmed block before a transaction.
  222.     # Save the results as a dictionary of transaction data
  223.     o = []
  224.     for i in range(0, len(process_queue), 20):
  225.         subpq = process_queue[i:i+20]
  226.         t = get_block_timestamp([x['height'] - 1 for x in subpq])
  227.         assert len(t) == len(subpq), [x['height'] - 1 for x in subpq]
  228.         o.extend([{
  229.             "tx": _a["tx"],
  230.             "addr": _a["addr"],
  231.             "value": _a["value"],
  232.             "time": _b
  233.         } for _a, _b in zip(subpq, t)])
  234.         sys.stderr.write('Collected timestamps: %d\n' % len(o))
  235.     return o
  236.  
  237.  
  238. # Compute ether value from BTC value, using as input objects containing
  239. # ether address, value and time and saving a map of ether address => balance
  240. def evaluate_purchases(purchases):
  241.     balances = {}
  242.     for p in purchases:
  243.         if p["time"] < start + initial_period:
  244.             rate = initial_rate
  245.         elif p["time"] < start + initial_period + rate_period * rate_periods:
  246.             pid = (p["time"] - (start + initial_period)) // rate_period + 1
  247.             rate = initial_rate - rate_decline * pid
  248.         elif p["time"] < start + initial_period + rate_period * \
  249.                 rate_periods + final_period:
  250.             rate = final_rate
  251.         else:
  252.             rate = post_rate
  253.         # Round to the nearest finney
  254.         balance_to_add = (p["value"] * rate // 10**5) * 10**15
  255.         balances[p["addr"]] = balances.get(p["addr"], 0) + balance_to_add
  256.     return {k: balances[k] for k in sorted(balances.keys())}
  257.  
  258.  
  259. # Compute a genesis block from purchase balances
  260. def mk_genesis_block(balances):
  261.     o = {k: {"balance": str(v)} for k, v in balances.items()}
  262.     total_purchased = sum(balances.values())
  263.     o[foundation_address] = {
  264.         "balance": str(total_purchased * 198 // 1000)
  265.     }
  266.     sys.stderr.write("Finished, total purchased: %d\n" % total_purchased)
  267.     sys.stderr.write("Foundation wallet creator address: %s\n" % foundation_address)
  268.     sys.stderr.write("Foundation balance: %s\n" % (total_purchased * 198 // 1000))
  269.     return {
  270.         "nonce": "0x0000000000000042",
  271.         "timestamp": "0x00",
  272.         "difficulty": "0x400000000",
  273.         "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  274.         "extraData": "0x"+EXTRADATA.encode('hex'),
  275.         "gasLimit": "0x1388",
  276.         "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  277.         "coinbase": "0x0000000000000000000000000000000000000000",
  278.         "alloc": o
  279.     }
  280.  
  281.  
  282. def evaluate():
  283.     outs = history(exodus)
  284.     sys.stderr.write('Gathered history: %d\n' % len(outs))
  285.     th = get_txs_and_heights(outs)
  286.     sys.stderr.write('Gathered txs and heights\n')
  287.     p = list_purchases(th)
  288.     sys.stderr.write('Listed purchases\n')
  289.     o = evaluate_purchases(p)
  290.     sys.stderr.write('Computed purchases\n')
  291.     g = mk_genesis_block(o)
  292.     return g
  293.  
  294. if __name__ == '__main__':
  295.     print (json.dumps(evaluate(), indent=4))
RAW Paste Data