1 from __future__ import division
3 from bitcoin import data as bitcoin_data
5 chain_id_type = bitcoin_data.ComposedType([
6 ('last_p2pool_block_hash', bitcoin_data.HashType()),
7 ('bits', bitcoin_data.StructType('<I')),
10 share_data_type = bitcoin_data.ComposedType([
11 ('last_p2pool_block_hash', bitcoin_data.HashType()),
12 ('previous_p2pool_share_hash', bitcoin_data.HashType()),
13 ('nonce', bitcoin_data.VarStrType()),
16 coinbase_type = bitcoin_data.ComposedType([
17 ('identifier', bitcoin_data.StructType('<Q')),
18 ('share_data', share_data_type),
21 merkle_branch_type = bitcoin_data.ListType(bitcoin_data.ComposedType([
22 ('side', bitcoin_data.StructType('<B')),
23 ('hash', bitcoin_data.HashType()),
26 gentx_info_type = bitcoin_data.ComposedType([
27 ('share_info', bitcoin_data.ComposedType([
28 ('share_data', share_data_type),
29 ('new_script', bitcoin_data.VarStrType()),
30 ('subsidy', bitcoin_data.StructType('<Q')),
32 ('merkle_branch', merkle_branch_type),
35 share1_type = bitcoin_data.ComposedType([
36 ('header', bitcoin_data.block_header_type),
37 ('gentx_info', gentx_info_type),
40 def calculate_merkle_branch(txs, index):
41 hash_list = [(bitcoin_data.tx_hash(tx), i == index, []) for i, tx in enumerate(txs)]
43 while len(hash_list) > 1:
46 bitcoin_data.doublesha(bitcoin_data.merkle_record_type.pack(dict(left=left, right=right))),
48 (left_l if left_f else right_l) + [dict(side=1, hash=right) if left_f else dict(side=0, hash=left)],
50 for (left, left_f, left_l), (right, right_f, right_l) in
51 zip(hash_list[::2], hash_list[1::2] + [hash_list[::2][-1]])
54 assert hash_list[0][1]
55 assert check_merkle_branch(txs[index], hash_list[0][2]) == hash_list[0][0]
57 return hash_list[0][2]
59 def check_merkle_branch(tx, branch):
60 hash_ = bitcoin_data.tx_hash(tx)
63 hash_ = bitcoin_data.doublesha(bitcoin_data.merkle_record_type.pack(dict(left=step['hash'], right=hash_)))
65 hash_ = bitcoin_data.doublesha(bitcoin_data.merkle_record_type.pack(dict(left=hash_, right=step['hash'])))
68 def txs_to_gentx_info(txs):
71 share_data=coinbase_type.unpack(txs[0]['tx_ins'][0]['script'])['share_data'],
72 subsidy=sum(tx_out['value'] for tx_out in txs[0]['tx_outs']),
73 new_script=txs[0]['tx_outs'][-1]['script'],
75 merkle_branch=calculate_merkle_branch(txs, 0),
78 def share_info_to_gentx_and_shares(share_info, chain, net):
79 return generate_transaction(
80 last_p2pool_block_hash=share_info['share_data']['last_p2pool_block_hash'],
81 previous_share2=chain.share2s[share_info['share_data']['previous_p2pool_share_hash']],
82 nonce=share_info['share_data']['nonce'],
83 new_script=share_info['new_script'],
84 subsidy=share_info['subsidy'],
88 def gentx_info_to_gentx_shares_and_merkle_root(gentx_info, chain, net):
89 gentx, shares = share_info_to_gentx_and_shares(gentx_info['share_info'], chain, net)
90 return gentx, shares, check_merkle_branch(gentx, gentx_info['merkle_branch'])
93 def __init__(self, header, txs=None, gentx_info=None):
95 if bitcoin_data.merkle_hash(txs) != header['merkle_root']:
96 raise ValueError("txs don't match header")
98 if gentx_info is None:
100 raise ValueError('need either txs or gentx_info')
102 gentx_info = txs_to_gentx_info(txs)
104 coinbase = gentx_info['share_info']['coinbase']
108 self.gentx_info = gentx_info
109 self.hash = bitcoin_data.block_hash(header)
110 self.previous_share_hash = coinbase['previous_p2pool_share_hash'] if coinbase['previous_p2pool_share_hash'] != 2**256 - 1 else None
111 self.chain_id_data = chain_id_type.pack(dict(last_p2pool_block_hash=coinbase['last_p2pool_block_hash'], bits=header['bits']))
115 raise ValueError('share does not contain all txs')
117 return dict(header=self.header, txs=self.txs)
120 return dict(header=self.header, gentx_info=self.gentx_info)
122 def check(self, chain, height, previous_share2, net):
123 if self.chain_id_data != chain.chain_id_data:
124 raise ValueError('wrong chain')
126 if self.hash > net.TARGET_MULTIPLIER*bitcoin_data.bits_to_target(self.header['bits']):
127 raise ValueError('not enough work!')
129 gentx, shares, merkle_root = gentx_info_to_gentx_shares_and_merkle_root(self.gentx_info, chain, net)
131 if merkle_root != self.header['merkle_root']:
132 raise ValueError("gentx doesn't match header")
134 return Share2(self, shares, height)
136 class Share2(object):
137 '''Share with associated data'''
139 def __init__(self, share, shares, height):
146 def flag_shared(self):
149 def generate_transaction(last_p2pool_block_hash, previous_share2, new_script, subsidy, nonce, net):
150 shares = (previous_share2.shares if previous_share2 is not None else [net.SCRIPT]*net.SPREAD)[1:-1] + [new_script, new_script]
153 for script in shares:
154 dest_weights[script] = dest_weights.get(script, 0) + 1
155 total_weight = sum(dest_weights.itervalues())
157 amounts = dict((script, subsidy*weight*63//(64*total_weight)) for (script, weight) in dest_weights.iteritems())
158 amounts[net.SCRIPT] = amounts.get(net.SCRIPT, 0) + subsidy//64 # prevent fake previous p2pool blocks
159 amounts[net.SCRIPT] = amounts.get(net.SCRIPT, 0) + subsidy - sum(amounts.itervalues()) # collect any extra
161 dests = sorted(amounts.iterkeys(), key=lambda script: (script == new_script, script))
162 assert dests[-1] == new_script
167 previous_output=dict(index=4294967295, hash=0),
169 script=coinbase_type.pack(dict(
170 identifier=net.IDENTIFIER,
172 last_p2pool_block_hash=last_p2pool_block_hash,
173 previous_p2pool_share_hash=previous_share2.share.hash if previous_share2 is not None else 2**256 - 1,
178 tx_outs=[dict(value=amounts[script], script=script) for script in dests if amounts[script]],
183 class Tracker(object):
185 self.shares = {} # hash -> share
186 self.reverse_shares = {} # previous_hash -> share_hash
187 self.heads = {} # hash -> (height, tail hash)
190 def add_share(self, share):
191 if share.hash in self.shares:
194 self.shares[share.hash] = share
195 self.reverse_shares.setdefault(share.previous_hash, set()).add(share.hash)
197 if self.reverse_shares.get(share.hash, set()):
200 self.heads.add(share.hash)
201 if share.previous_hash in self.heads:
202 self.heads.remove(share.previous_hash)
204 if __name__ == '__main__':
205 class FakeShare(object):
206 def __init__(self, hash, previous_hash):
208 self.previous_hash = previous_hash
212 t.add_share(FakeShare(1, 2))
214 t.add_share(FakeShare(4, 0))
216 t.add_share(FakeShare(3, 4))
219 # TARGET_MULTIPLIER needs to be less than the current difficulty to prevent miner clients from missing shares
221 class Testnet(object):
222 TARGET_MULTIPLIER = SPREAD = 30
223 ROOT_BLOCK = 0xd5070cd4f2987ad2191af71393731a2b143f094f7b84c9e6aa9a6a
224 SCRIPT = '410403ad3dee8ab3d8a9ce5dd2abfbe7364ccd9413df1d279bf1a207849310465b0956e5904b1155ecd17574778f9949589ebfd4fb33ce837c241474a225cf08d85dac'.decode('hex')
225 IDENTIFIER = 0x1ae3479e4eb6700a
226 PREFIX = 'd19778c812754854'.decode('hex')
227 ADDRS_TABLE = 'addrs_testnet'
230 TARGET_MULTIPLIER = SPREAD = 600
231 ROOT_BLOCK = 0x11a22c6e314b1a3f44cbbf50246187a37756ea8af4d41c43a8d6
232 SCRIPT = '4104ffd03de44a6e11b9917f3a29f9443283d9871c9d743ef30d5eddcd37094b64d1b3d8090496b53256786bf5c82932ec23c3b74d9f05a6f95a8b5529352656664bac'.decode('hex')
233 IDENTIFIER = 0x7452839666e1f8f8
234 PREFIX = '2d4224bf18c87b87'.decode('hex')
235 ADDRS_TABLE = 'addrs'