Accept ntime+1000 (necessary for using proxy & bfl rig)
[stratum-mining.git] / lib / block_template.py
1 import StringIO
2 import binascii
3 import struct
4
5 import util
6 import merkletree
7 import halfnode
8 from coinbasetx import CoinbaseTransaction
9
10 # Remove dependency to settings, coinbase extras should be
11 # provided from coinbaser
12 from stratum import settings
13
14 class BlockTemplate(halfnode.CBlock):
15     '''Template is used for generating new jobs for clients.
16     Let's iterate extranonce1, extranonce2, ntime and nonce
17     to find out valid bitcoin block!'''
18     
19     coinbase_transaction_class = CoinbaseTransaction
20     
21     def __init__(self, timestamper, coinbaser, job_id):
22         super(BlockTemplate, self).__init__()
23         
24         self.job_id = job_id 
25         self.timestamper = timestamper
26         self.coinbaser = coinbaser
27         
28         self.prevhash_bin = '' # reversed binary form of prevhash
29         self.prevhash_hex = ''
30         self.timedelta = 0
31         self.curtime = 0
32         self.target = 0
33         #self.coinbase_hex = None 
34         self.merkletree = None
35                 
36         self.broadcast_args = []
37         
38         # List of 4-tuples (extranonce1, extranonce2, ntime, nonce)
39         # registers already submitted and checked shares
40         # There may be registered also invalid shares inside!
41         self.submits = [] 
42                 
43     def fill_from_rpc(self, data):
44         '''Convert getblocktemplate result into BlockTemplate instance'''
45         
46         #txhashes = [None] + [ binascii.unhexlify(t['hash']) for t in data['transactions'] ]
47         txhashes = [None] + [ util.ser_uint256(int(t['hash'], 16)) for t in data['transactions'] ]
48         mt = merkletree.MerkleTree(txhashes)
49
50         coinbase = self.coinbase_transaction_class(self.timestamper, self.coinbaser, data['coinbasevalue'],
51                         data['coinbaseaux']['flags'], data['height'], settings.COINBASE_EXTRAS)
52         
53         self.height = data['height']
54         self.nVersion = data['version']
55         self.hashPrevBlock = int(data['previousblockhash'], 16)
56         self.nBits = int(data['bits'], 16)
57         self.hashMerkleRoot = 0
58         self.nTime = 0
59         self.nNonce = 0
60         self.vtx = [ coinbase, ]
61         
62         for tx in data['transactions']:
63             t = halfnode.CTransaction()
64             t.deserialize(StringIO.StringIO(binascii.unhexlify(tx['data'])))
65             self.vtx.append(t)
66             
67         self.curtime = data['curtime']
68         self.timedelta = self.curtime - int(self.timestamper.time()) 
69         self.merkletree = mt
70         self.target = util.uint256_from_compact(self.nBits)
71         
72         # Reversed prevhash
73         self.prevhash_bin = binascii.unhexlify(util.reverse_hash(data['previousblockhash']))
74         self.prevhash_hex = "%064x" % self.hashPrevBlock
75         
76         self.broadcast_args = self.build_broadcast_args()
77                 
78     def register_submit(self, extranonce1, extranonce2, ntime, nonce):
79         '''Client submitted some solution. Let's register it to
80         prevent double submissions.'''
81         
82         t = (extranonce1, extranonce2, ntime, nonce)
83         if t not in self.submits:
84             self.submits.append(t)
85             return True
86         return False
87             
88     def build_broadcast_args(self):
89         '''Build parameters of mining.notify call. All clients
90         may receive the same params, because they include
91         their unique extranonce1 into the coinbase, so every
92         coinbase_hash (and then merkle_root) will be unique as well.'''
93         job_id = self.job_id
94         prevhash = binascii.hexlify(self.prevhash_bin)
95         (coinb1, coinb2) = [ binascii.hexlify(x) for x in self.vtx[0]._serialized ]
96         merkle_branch = [ binascii.hexlify(x) for x in self.merkletree._steps ]
97         version = binascii.hexlify(struct.pack(">i", self.nVersion))
98         nbits = binascii.hexlify(struct.pack(">I", self.nBits))
99         ntime = binascii.hexlify(struct.pack(">I", self.curtime))
100         clean_jobs = True
101         
102         return (job_id, prevhash, coinb1, coinb2, merkle_branch, version, nbits, ntime, clean_jobs)
103
104     def serialize_coinbase(self, extranonce1, extranonce2):
105         '''Serialize coinbase with given extranonce1 and extranonce2
106         in binary form'''
107         (part1, part2) = self.vtx[0]._serialized
108         return part1 + extranonce1 + extranonce2 + part2
109     
110     def check_ntime(self, ntime):
111         '''Check for ntime restrictions.'''
112         if ntime < self.curtime:
113             return False
114         
115         if ntime > (self.timestamper.time() + 1000):
116             # Be strict on ntime into the near future
117             # may be unnecessary
118             return False
119         
120         return True
121
122     def serialize_header(self, merkle_root_int, ntime_bin, nonce_bin):
123         '''Serialize header for calculating block hash'''
124         r  = struct.pack(">i", self.nVersion)
125         r += self.prevhash_bin
126         r += util.ser_uint256_be(merkle_root_int)
127         r += ntime_bin
128         r += struct.pack(">I", self.nBits)
129         r += nonce_bin    
130         return r       
131
132     def finalize(self, merkle_root_int, extranonce1_bin, extranonce2_bin, ntime, nonce):
133         '''Take all parameters required to compile block candidate.
134         self.is_valid() should return True then...'''
135         
136         self.hashMerkleRoot = merkle_root_int
137         self.nTime = ntime
138         self.nNonce = nonce
139         self.vtx[0].set_extranonce(extranonce1_bin + extranonce2_bin)        
140         self.sha256 = None # We changed block parameters, let's reset sha256 cache