1 from __future__ import division
3 from bitcoin import data as bitcoin_data
5 share_data_type = bitcoin_data.ComposedType([
6 ('previous_p2pool_share_hash', bitcoin_data.HashType()),
7 ('bits2', bitcoin_data.FixedStrType(4)),
8 ('nonce', bitcoin_data.VarStrType()),
11 coinbase_type = bitcoin_data.ComposedType([
12 ('identifier', bitcoin_data.StructType('<Q')),
13 ('share_data', share_data_type),
16 merkle_branch_type = bitcoin_data.ListType(bitcoin_data.ComposedType([
17 ('side', bitcoin_data.StructType('<B')),
18 ('hash', bitcoin_data.HashType()),
21 gentx_info_type = bitcoin_data.ComposedType([
22 ('share_info', bitcoin_data.ComposedType([
23 ('share_data', share_data_type),
24 ('new_script', bitcoin_data.VarStrType()),
25 ('subsidy', bitcoin_data.StructType('<Q')),
27 ('merkle_branch', merkle_branch_type),
30 share1_type = bitcoin_data.ComposedType([
31 ('header', bitcoin_data.block_header_type),
32 ('gentx_info', gentx_info_type),
35 def calculate_merkle_branch(txs, index):
36 hash_list = [(bitcoin_data.tx_hash(tx), i == index, []) for i, tx in enumerate(txs)]
38 while len(hash_list) > 1:
41 bitcoin_data.doublesha(bitcoin_data.merkle_record_type.pack(dict(left=left, right=right))),
43 (left_l if left_f else right_l) + [dict(side=1, hash=right) if left_f else dict(side=0, hash=left)],
45 for (left, left_f, left_l), (right, right_f, right_l) in
46 zip(hash_list[::2], hash_list[1::2] + [hash_list[::2][-1]])
49 assert hash_list[0][1]
50 assert check_merkle_branch(txs[index], hash_list[0][2]) == hash_list[0][0]
52 return hash_list[0][2]
54 def check_merkle_branch(tx, branch):
55 hash_ = bitcoin_data.tx_hash(tx)
58 hash_ = bitcoin_data.doublesha(bitcoin_data.merkle_record_type.pack(dict(left=step['hash'], right=hash_)))
60 hash_ = bitcoin_data.doublesha(bitcoin_data.merkle_record_type.pack(dict(left=hash_, right=step['hash'])))
63 def txs_to_gentx_info(txs):
66 share_data=coinbase_type.unpack(txs[0]['tx_ins'][0]['script'])['share_data'],
67 subsidy=sum(tx_out['value'] for tx_out in txs[0]['tx_outs']),
68 new_script=txs[0]['tx_outs'][-1]['script'],
70 merkle_branch=calculate_merkle_branch(txs, 0),
73 def share_info_to_gentx_and_shares(share_info, chain, net):
74 return generate_transaction(
75 previous_share2=chain.share2s[share_info['share_data']['previous_p2pool_share_hash']],
76 nonce=share_info['share_data']['nonce'],
77 new_script=share_info['new_script'],
78 subsidy=share_info['subsidy'],
82 def gentx_info_to_gentx_shares_and_merkle_root(gentx_info, chain, net):
83 gentx, shares = share_info_to_gentx_and_shares(gentx_info['share_info'], chain, net)
84 return gentx, shares, check_merkle_branch(gentx, gentx_info['merkle_branch'])
87 def __init__(self, header, txs=None, gentx_info=None):
89 if bitcoin_data.merkle_hash(txs) != header['merkle_root']:
90 raise ValueError("txs don't match header")
92 if gentx_info is None:
94 raise ValueError('need either txs or gentx_info')
96 gentx_info = txs_to_gentx_info(txs)
98 coinbase = gentx_info['share_info']['coinbase']
102 self.gentx_info = gentx_info
103 self.hash = bitcoin_data.block_hash(header)
104 self.previous_share_hash = coinbase['previous_p2pool_share_hash'] if coinbase['previous_p2pool_share_hash'] != 2**256 - 1 else None
105 self.chain_id_data = chain_id_type.pack(dict(last_p2pool_block_hash=coinbase['last_p2pool_block_hash'], bits=header['bits']))
109 raise ValueError('share does not contain all txs')
111 return dict(header=self.header, txs=self.txs)
114 return dict(header=self.header, gentx_info=self.gentx_info)
116 def check(self, chain, height, previous_share2, net):
117 if self.chain_id_data != chain.chain_id_data:
118 raise ValueError('wrong chain')
120 if self.hash > net.TARGET_MULTIPLIER*bitcoin_data.bits_to_target(self.header['bits']):
121 raise ValueError('not enough work!')
123 gentx, shares, merkle_root = gentx_info_to_gentx_shares_and_merkle_root(self.gentx_info, chain, net)
125 if merkle_root != self.header['merkle_root']:
126 raise ValueError("gentx doesn't match header")
128 return Share2(self, shares, height)
130 class Share2(object):
131 '''Share with associated data'''
133 def __init__(self, share, shares, height):
140 def flag_shared(self):
143 def generate_transaction(last_p2pool_block_hash, previous_share2, new_script, subsidy, nonce, net):
144 shares = (previous_share2.shares if previous_share2 is not None else [net.SCRIPT]*net.SPREAD)[1:-1] + [new_script, new_script]
147 for script in shares:
148 dest_weights[script] = dest_weights.get(script, 0) + 1
149 total_weight = sum(dest_weights.itervalues())
151 amounts = dict((script, subsidy*weight*63//(64*total_weight)) for (script, weight) in dest_weights.iteritems())
152 amounts[net.SCRIPT] = amounts.get(net.SCRIPT, 0) + subsidy//64 # prevent fake previous p2pool blocks
153 amounts[net.SCRIPT] = amounts.get(net.SCRIPT, 0) + subsidy - sum(amounts.itervalues()) # collect any extra
155 dests = sorted(amounts.iterkeys(), key=lambda script: (script == new_script, script))
156 assert dests[-1] == new_script
158 pre_target = sum(bitcoin_data.target_to_average_attempts(share(x ago).target) for x in xrange(1000))/(share(1000 ago).timestamp - share(1 ago).timestamp)
159 bits2 = bitcoin_data.compress_target_to_bits(pre_target)
164 previous_output=dict(index=4294967295, hash=0),
166 script=coinbase_type.pack(dict(
167 identifier=net.IDENTIFIER,
169 last_p2pool_block_hash=last_p2pool_block_hash,
170 previous_p2pool_share_hash=previous_share2.share.hash if previous_share2 is not None else 2**256 - 1,
176 tx_outs=[dict(value=amounts[script], script=script) for script in dests if amounts[script]],
181 class Tracker(object):
183 self.shares = {} # hash -> share
184 self.reverse_shares = {} # previous_hash -> share_hash
185 self.heads = {} # hash -> (height, tail hash)
188 def add_share(self, share):
189 if share.hash in self.shares:
192 self.shares[share.hash] = share
193 self.reverse_shares.setdefault(share.previous_hash, set()).add(share.hash)
195 if self.reverse_shares.get(share.hash, set()):
198 self.heads.add(share.hash)
199 if share.previous_hash in self.heads:
200 self.heads.remove(share.previous_hash)
202 def get_chain(self, start):
203 share_hash_to_get = start
204 while share_hash_to_get in self.shares:
205 share = self.shares[share_hash_to_get]
207 share_hash_to_get = share.previous_hash
210 return max(self.heads, key=self.score_chain)
212 def score_chain(self, start):
213 length = len(self.get_chain(start))
216 for share in itertools.islice(self.get_chain(start), 1000):
219 return (min(length, 1000), score)
221 if __name__ == '__main__':
222 class FakeShare(object):
223 def __init__(self, hash, previous_hash):
225 self.previous_hash = previous_hash
229 t.add_share(FakeShare(1, 2))
231 t.add_share(FakeShare(4, 0))
233 t.add_share(FakeShare(3, 4))
236 # TARGET_MULTIPLIER needs to be less than the current difficulty to prevent miner clients from missing shares
238 class Mainnet(bitcoin_data.Mainnet):
239 TARGET_MULTIPLIER = SPREAD = 600
240 ROOT_BLOCK = 0x6c9cb0589a44808d9a9361266a4ffb9fea2e2cf4d70bb2118b5
241 SCRIPT = '4104ffd03de44a6e11b9917f3a29f9443283d9871c9d743ef30d5eddcd37094b64d1b3d8090496b53256786bf5c82932ec23c3b74d9f05a6f95a8b5529352656664bac'.decode('hex')
242 IDENTIFIER = 0x7452839666e1f8f8
243 PREFIX = '2d4224bf18c87b87'.decode('hex')
244 ADDRS_TABLE = 'addrs'
247 class Testnet(bitcoin_data.Testnet):
248 TARGET_MULTIPLIER = SPREAD = 30
249 ROOT_BLOCK = 0xd5070cd4f2987ad2191af71393731a2b143f094f7b84c9e6aa9a6a
250 SCRIPT = '410403ad3dee8ab3d8a9ce5dd2abfbe7364ccd9413df1d279bf1a207849310465b0956e5904b1155ecd17574778f9949589ebfd4fb33ce837c241474a225cf08d85dac'.decode('hex')
251 IDENTIFIER = 0x1ae3479e4eb6700a
252 PREFIX = 'd19778c812754854'.decode('hex')
253 ADDRS_TABLE = 'addrs_testnet'