1 from binascii import a2b_hex, b2a_hex
2 from bitcoin.script import countSigOps
3 from bitcoin.txn import Txn
4 from collections import deque
5 from queue import Queue
8 from merkletree import MerkleTree
9 from struct import pack
11 from time import sleep, time
14 _makeCoinbase = [0, 0]
16 class merkleMaker(threading.Thread):
17 def __init__(self, *a, **k):
18 super().__init__(*a, **k)
20 self.logger = logging.getLogger('merkleMaker')
21 self.CoinbasePrefix = b''
25 self.access = jsonrpc.ServiceProxy(self.UpstreamURI)
27 self.currentBlock = (None, None)
28 self.currentMerkleTree = None
29 self.merkleRoots = deque(maxlen=self.WorkQueueSizeRegular[1])
30 self.clearMerkleTree = MerkleTree([self.clearCoinbaseTxn])
31 self.clearMerkleRoots = Queue(self.WorkQueueSizeLongpoll[1])
33 self.nextMerkleUpdate = 0
36 self.updateMerkleTree()
38 def updateBlock(self, newBlock, bits = None, _HBH = None):
39 if newBlock == self.currentBlock[0]:
43 bits = self.currentBlock[1]
45 _HBH = (b2a_hex(newBlock[::-1]).decode('utf8'), b2a_hex(bits[::-1]).decode('utf8'))
46 self.logger.debug('New block: %s (bits: %s)' % _HBH)
47 self.merkleRoots.clear()
48 self.currentMerkleTree = self.clearMerkleTree
49 self.lastBlock = self.currentBlock
50 self.currentBlock = (newBlock, bits)
54 def updateMerkleTree(self):
56 self.logger.debug('Polling bitcoind for memorypool')
57 self.nextMerkleUpdate = now + self.TxnUpdateRetryWait
58 MP = self.access.getmemorypool()
59 prevBlock = a2b_hex(MP['previousblockhash'])[::-1]
60 bits = a2b_hex(MP['bits'])[::-1]
61 if (prevBlock, bits) != self.currentBlock:
62 self.updateBlock(prevBlock, bits, _HBH=(MP['previousblockhash'], MP['bits']))
63 # TODO: cache Txn or at least txid from previous merkle roots?
64 txnlist = [a for a in map(a2b_hex, MP['transactions'])]
66 t = self.makeCoinbaseTxn(MP['coinbasevalue'])
67 t.setCoinbase(b'\0\0')
69 txnlist.insert(0, t.data)
71 txnlistsz = sum(map(len, txnlist))
72 while txnlistsz > 934464: # TODO: 1 "MB" limit - 64 KB breathing room
73 self.logger.debug('Trimming transaction for size limit')
74 txnlistsz -= len(txnlist.pop())
76 txnlistsz = sum(map(countSigOps, txnlist))
77 while txnlistsz > 19488: # TODO: 20k limit - 0x200 breathing room
78 self.logger.debug('Trimming transaction for SigOp limit')
79 txnlistsz -= countSigOps(txnlist.pop())
81 txnlist = [a for a in map(Txn, txnlist[1:])]
83 txnlist = list(txnlist)
84 newMerkleTree = MerkleTree(txnlist)
85 if newMerkleTree.merkleRoot() != self.currentMerkleTree.merkleRoot():
86 self.logger.debug('Updating merkle tree')
87 self.currentMerkleTree = newMerkleTree
88 self.nextMerkleUpdate = now + self.MinimumTxnUpdateWait
90 if self.needMerkle == 2:
93 def makeCoinbase(self):
95 if now > _makeCoinbase[0]:
96 _makeCoinbase[0] = now
100 rv = self.CoinbasePrefix
101 rv += pack('>L', now) + pack('>Q', _makeCoinbase[1]).lstrip(b'\0')
102 for v in self.CoinbaseAux.values():
106 def makeMerkleRoot(self, merkleTree):
107 t = merkleTree.data[0]
108 cb = self.makeCoinbase()
111 merkleRoot = merkleTree.merkleRoot()
112 return (merkleRoot, merkleTree, cb)
115 def _doing(self, what):
116 if self._doing_last == what:
121 self.logger.debug("Switching from (%4dx in %5.3f seconds) %s => %s" % (self._doing_i, now - self._doing_s, self._doing_last, what))
122 self._doing_last = what
126 def merkleMaker_I(self):
129 # First, update merkle tree if we haven't for a while and aren't crunched for time
131 if self.nextMerkleUpdate <= now and self.clearMerkleRoots.qsize() > self.WorkQueueSizeLongpoll[0] and len(self.merkleRoots) > self.WorkQueueSizeRegular[0]:
132 self.updateMerkleTree()
133 # Next, fill up the longpoll queue first, since it can be used as a failover for the main queue
134 elif not self.clearMerkleRoots.full():
135 self._doing('blank merkle roots')
136 self.clearMerkleRoots.put(self.makeMerkleRoot(self.clearMerkleTree))
137 # Next, fill up the main queue (until they're all current)
138 elif len(self.merkleRoots) < self.WorkQueueSizeRegular[1] or self.merkleRoots[0][1] != self.currentMerkleTree:
139 self._doing('regular merkle roots')
140 self.merkleRoots.append(self.makeMerkleRoot(self.currentMerkleTree))
142 if self.needMerkle == 1:
144 self.needMerkle = False
146 # TODO: rather than sleepspin, block until MinimumTxnUpdateWait expires or threading.Condition(?)
147 sleep(self.IdleSleepTime)
153 self._THISISUGLY._flushrecv()
155 self.logger.critical(traceback.format_exc())
157 def start(self, *a, **k):
159 super().start(*a, **k)
162 (prevBlock, bits) = self.currentBlock
164 MRD = self.merkleRoots.pop()
167 MRD = self.clearMerkleRoots.get()
169 (merkleRoot, merkleTree, cb) = MRD
170 return (merkleRoot, merkleTree, cb, prevBlock, bits, rollPrevBlk)