1 from __future__ import division
6 chain_id_type = bitcoin_p2p.ComposedType([
7 ('last_p2pool_block_hash', bitcoin_p2p.HashType()),
8 ('bits', bitcoin_p2p.StructType('<I')),
11 coinbase_type = bitcoin_p2p.ComposedType([
12 ('identifier', bitcoin_p2p.StructType('<Q')),
13 ('last_p2pool_block_hash', bitcoin_p2p.HashType()),
14 ('previous_p2pool_share_hash', bitcoin_p2p.HashType()),
15 ('subsidy', bitcoin_p2p.StructType('<Q')),
16 ('last_share_index', bitcoin_p2p.StructType('<I')),
17 ('nonce', bitcoin_p2p.StructType('<Q')),
20 merkle_branch = bitcoin_p2p.ListType(bitcoin_p2p.ComposedType([
21 ('side', bitcoin_p2p.StructType('<B')),
22 ('hash', bitcoin_p2p.HashType()),
25 share1 = bitcoin_p2p.ComposedType([
26 ('header', bitcoin_p2p.block_header),
27 ('gentx', bitcoin_p2p.ComposedType([
28 ('tx', bitcoin_p2p.tx),
29 ('merkle_branch', merkle_branch),
33 def calculate_merkle_branch(txn_list, index):
34 hash_list = [(bitcoin_p2p.doublesha(bitcoin_p2p.tx.pack(data)), i == index, []) for i, data in enumerate(txn_list)]
36 while len(hash_list) > 1:
39 bitcoin_p2p.doublesha(bitcoin_p2p.merkle_record.pack(dict(left=left, right=right))),
41 (left_l if left_f else right_l) + [dict(side=1, hash=right) if left_f else dict(side=0, hash=left)],
43 for (left, left_f, left_l), (right, right_f, right_l) in
44 zip(hash_list[::2], hash_list[1::2] + [hash_list[::2][-1]])
47 assert check_merkle_branch(txn_list[index], hash_list[0][2]) == hash_list[0][0]
49 return hash_list[0][2]
51 def check_merkle_branch(txn, branch):
52 hash_ = bitcoin_p2p.doublesha(bitcoin_p2p.tx.pack(txn))
55 hash_ = bitcoin_p2p.doublesha(bitcoin_p2p.merkle_record.pack(dict(left=step['hash'], right=hash_)))
57 hash_ = bitcoin_p2p.doublesha(bitcoin_p2p.merkle_record.pack(dict(left=hash_, right=step['hash'])))
60 def txns_to_gentx(txns):
63 merkle_branch=calculate_merkle_branch(txns, 0),
67 def __init__(self, header, txns=None, gentx=None):
69 self.hash = bitcoin_p2p.block_hash(header)
75 if check_merkle_branch(gentx['tx'], gentx['merkle_branch']) != header['merkle_root']:
76 #print '%x' % check_merkle_branch(gentx['tx'], gentx['merkle_branch'])
77 #print '%x' % header['merkle_root']
78 raise ValueError("gentx doesn't match header")
80 raise ValueError('need either txns or gentx')
82 self.gentx = txns_to_gentx(txns)
84 if gentx != self.gentx:
85 raise ValueError('invalid gentx')
87 self.coinbase = coinbase_type.unpack(self.gentx['tx']['tx_ins'][0]['script'], ignore_extra=True)
88 self.previous_share_hash = self.coinbase['previous_p2pool_share_hash'] if self.coinbase['previous_p2pool_share_hash'] != 2**256 - 1 else None
89 self.chain_id_data = chain_id_type.pack(dict(last_p2pool_block_hash=self.coinbase['last_p2pool_block_hash'], bits=self.header['bits']))
93 raise ValueError('share does not contain all txns')
94 return dict(header=self.header, txns=self.txns)
97 return dict(header=self.header, gentx=self.gentx)
99 def check(self, chain, height, previous_share2, net):
100 if self.chain_id_data != chain.chain_id_data:
101 raise ValueError('wrong chain')
102 if self.hash > net.TARGET_MULTIPLIER*conv.bits_to_target(self.header['bits']):
103 raise ValueError('not enough work!')
106 t2, shares = generate_transaction(
107 last_p2pool_block_hash=chain.last_p2pool_block_hash,
108 previous_share2=previous_share2,
109 add_script=t['tx_outs'][self.coinbase['last_share_index']]['script'],
110 subsidy=self.coinbase['subsidy'],
111 nonce=self.coinbase['nonce'],
115 raise ValueError('invalid generate txn')
117 return Share2(self, shares, height)
119 class Share2(object):
120 '''Share with associated data'''
122 def __init__(self, share, shares, height):
124 self.shares = map(intern, shares)
128 def flag_shared(self):
131 def generate_transaction(last_p2pool_block_hash, previous_share2, add_script, subsidy, nonce, net):
132 shares = (previous_share2.shares if previous_share2 is not None else [net.SCRIPT]*net.SPREAD)[1:-1] + [add_script, add_script]
135 for script in shares:
136 dest_weights[script] = dest_weights.get(script, 0) + 1
137 total_weight = sum(dest_weights.itervalues())
139 amounts = dict((script, subsidy*weight*63//(64*total_weight)) for (script, weight) in dest_weights.iteritems())
140 amounts[net.SCRIPT] = amounts.get(net.SCRIPT, 0) + subsidy//64 # prevent fake previous p2pool blocks
141 amounts[net.SCRIPT] = amounts.get(net.SCRIPT, 0) + subsidy - sum(amounts.itervalues()) # collect any extra
143 dests = sorted(amounts.iterkeys())
148 previous_output=dict(index=4294967295, hash=0),
150 script=coinbase_type.pack(dict(
151 identifier=net.IDENTIFIER,
152 last_p2pool_block_hash=last_p2pool_block_hash,
153 previous_p2pool_share_hash=previous_share2.share.hash if previous_share2 is not None else 2**256 - 1,
155 last_share_index=dests.index(add_script),
159 tx_outs=[dict(value=amounts[script], script=script) for script in dests if amounts[script]],
164 # TARGET_MULTIPLIER needs to be less than the current difficulty to prevent miner clients from missing shares
166 class Testnet(object):
167 TARGET_MULTIPLIER = 30
169 ROOT_BLOCK = 0x3575d1e7b40fe37ad12d41169a1012d26df5f3c35486e2abfbe9d2c
170 SCRIPT = '410489175c7658845fd7c33d61029ebf4042e8386443ff6e6628fdb5ac938c31072dc61cee691ae1e8355c3a87cb4813cc9bf036fdb09078d35eacf9e9ab52374ebeac'.decode('hex')
171 IDENTIFIER = 0x808330dc87e313b7
174 TARGET_MULTIPLIER = SPREAD = 600
175 ROOT_BLOCK = 0xf78e83f63fd0f4e0f584d3bc2c7010f679834cd8886d61876d
176 SCRIPT = '410441ccbae5ca6ecfaa014028b0c49df2cd5588cb6058ac260d650bc13c9ec466f95c7a6d80a3ea7f7b8e2e87e49b96081e9b20415b06433d7a5b6a156b58690d96ac'.decode('hex')
177 IDENTIFIER = 0x49ddc0b4938708ad