Merge commit 'b44865e'
[bitcoin:eloipool.git] / merklemaker.py
1 # Eloipool - Python Bitcoin pool server
2 # Copyright (C) 2011-2012  Luke Dashjr <luke-jr+eloipool@utopios.org>
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as
6 # published by the Free Software Foundation, either version 3 of the
7 # License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU Affero General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 from binascii import b2a_hex
18 import bitcoin.script
19 from bitcoin.script import countSigOps
20 from bitcoin.txn import Txn
21 from bitcoin.varlen import varlenEncode, varlenDecode
22 from collections import deque
23 from copy import deepcopy
24 from queue import Queue
25 import jsonrpc
26 import logging
27 from math import log
28 from merkletree import MerkleTree
29 import socket
30 from struct import pack
31 import threading
32 from time import sleep, time
33 import traceback
34
35 _makeCoinbase = [0, 0]
36 _filecounter = 0
37
38 def MakeBlockHeader(MRD):
39         (merkleRoot, merkleTree, coinbase, prevBlock, bits) = MRD[:5]
40         timestamp = pack('<L', int(time()))
41         hdr = b'\2\0\0\0' + prevBlock + merkleRoot + timestamp + bits + b'iolE'
42         return hdr
43
44 def assembleBlock(blkhdr, txlist):
45         payload = blkhdr
46         payload += varlenEncode(len(txlist))
47         for tx in txlist:
48                 payload += tx.data
49         return payload
50
51 class merkleMaker(threading.Thread):
52         GBTCaps = [
53                 'coinbasevalue',
54                 'coinbase/append',
55                 'coinbase',
56                 'generation',
57                 'time',
58                 'transactions/remove',
59                 'prevblock',
60         ]
61         GBTReq = {
62                 'capabilities': GBTCaps,
63         }
64         GMPReq = {
65                 'capabilities': GBTCaps,
66                 'tx': 'obj',
67         }
68         
69         def __init__(self, *a, **k):
70                 super().__init__(*a, **k)
71                 self.daemon = True
72                 self.logger = logging.getLogger('merkleMaker')
73                 self.CoinbasePrefix = b''
74                 self.CoinbaseAux = {}
75                 self.isOverflowed = False
76                 self.lastWarning = {}
77                 self.MinimumTxnUpdateWait = 5
78                 self.overflowed = 0
79                 self.DifficultyChangeMod = 2016
80                 self.MinimumTemplateAcceptanceRatio = 0
81                 self.MinimumTemplateScore = 1
82                 self.currentBlock = (None, None, None)
83                 self.lastBlock = (None, None, None)
84         
85         def _prepare(self):
86                 self.TemplateSources = list(getattr(self, 'TemplateSources', ()))
87                 self.TemplateChecks = list(getattr(self, 'TemplateChecks', ()))
88                 if getattr(self, 'BlockSubmissions', None) is None:
89                         self.BlockSubmissions = ()
90                 if hasattr(self, 'UpstreamURI'):
91                         self.TemplateSources.append({
92                                 'name': 'UpstreamURI',
93                                 'uri': self.UpstreamURI,
94                         })
95                 URI2Name = {}
96                 Name2URI = {}
97                 for a in (self.TemplateSources + self.TemplateChecks + list(self.BlockSubmissions)):
98                         if not ('name' in a and 'uri' in a):
99                                 continue
100                         URI2Name.setdefault(a['uri'], a['name'])
101                         Name2URI.setdefault(a['name'], a['uri'])
102                 def URINamePair(a, defname):
103                         if 'name' not in a:
104                                 a['name'] = URI2Name.get(a['uri'], defname)
105                         elif 'uri' not in a:
106                                 a['uri'] = Name2URI[a['name']]
107                 _URI2Access = {}
108                 def URI2Access(uri):
109                         if uri not in _URI2Access:
110                                 access = jsonrpc.ServiceProxy(uri)
111                                 access.OldGMP = False
112                                 _URI2Access[uri] = access
113                         return _URI2Access[uri]
114                 LeveledTS = {}
115                 for i in range(len(self.TemplateSources)):
116                         TS = self.TemplateSources[i]
117                         URINamePair(TS, 'TemplateSources[%u]' % (i,))
118                         TS.setdefault('priority', 0)
119                         TS.setdefault('weight', 1)
120                         TS['access'] = URI2Access(TS['uri'])
121                         LeveledTS.setdefault(TS['priority'], []).append(TS)
122                 LeveledTS = tuple(x[1] for x in sorted(LeveledTS.items()))
123                 self.TemplateSources = LeveledTS
124                 for i in range(len(self.TemplateChecks)):
125                         TC = self.TemplateChecks[i]
126                         URINamePair(TC, 'TemplateChecks[%u]' % (i,))
127                         TC.setdefault('unanimous', False)
128                         TC.setdefault('weight', 1)
129                         TC['access'] = URI2Access(TC['uri'])
130                 for i in range(len(getattr(self, 'BlockSubmissions', ()))):
131                         BS = self.BlockSubmissions[i]
132                         URINamePair(BS, 'BlockSubmissions[%u]' % (i,))
133                         BS['access'] = URI2Access(BS['uri'])
134                 
135                 self.ready = False
136                 self.readyCV = threading.Condition()
137                 
138                 self.currentMerkleTree = None
139                 self.merkleRoots = deque(maxlen=self.WorkQueueSizeRegular[1])
140                 self.LowestMerkleRoots = self.WorkQueueSizeRegular[1]
141                 
142                 if not hasattr(self, 'WorkQueueSizeClear'):
143                         self.WorkQueueSizeClear = self.WorkQueueSizeLongpoll
144                 self._MaxClearSize = max(self.WorkQueueSizeClear[1], self.WorkQueueSizeLongpoll[1])
145                 self.clearMerkleRoots = Queue(self._MaxClearSize)
146                 self.LowestClearMerkleRoots = self.WorkQueueSizeClear[1]
147                 self.nextMerkleRoots = Queue(self._MaxClearSize)
148                 
149                 if not hasattr(self, 'WarningDelay'):
150                         self.WarningDelay = max(15, self.MinimumTxnUpdateWait * 2)
151                 if not hasattr(self, 'WarningDelayTxnLongpoll'):
152                         self.WarningDelayTxnLongpoll = self.WarningDelay
153                 if not hasattr(self, 'WarningDelayMerkleUpdate'):
154                         self.WarningDelayMerkleUpdate = self.WarningDelay
155                 
156                 self.lastMerkleUpdate = 0
157                 self.nextMerkleUpdate = 0
158         
159         def createClearMerkleTree(self, height):
160                 subsidy = 5000000000 >> (height // 210000)
161                 cbtxn = self.makeCoinbaseTxn(subsidy, False)
162                 cbtxn.assemble()
163                 return MerkleTree([cbtxn])
164         
165         def updateBlock(self, newBlock, height = None, bits = None, _HBH = None):
166                 if newBlock == self.currentBlock[0]:
167                         if height in (None, self.currentBlock[1]) and bits in (None, self.currentBlock[2]):
168                                 return
169                         if not self.currentBlock[2] is None:
170                                 self.logger.error('Was working on block with wrong specs: %s (height: %d->%d; bits: %s->%s' % (
171                                         b2a_hex(newBlock[::-1]).decode('utf8'),
172                                         self.currentBlock[1],
173                                         height,
174                                         b2a_hex(self.currentBlock[2][::-1]).decode('utf8'),
175                                         b2a_hex(bits[::-1]).decode('utf8'),
176                                 ))
177                                 if self.needMerkle == 1:
178                                         self.needMerkle = False
179                                 self.onBlockUpdate()
180                 
181                 # Old block is invalid
182                 if self.currentBlock[0] != newBlock:
183                         self.lastBlock = self.currentBlock
184                 
185                 lastHeight = self.currentBlock[1]
186                 if height is None:
187                         height = self.currentBlock[1] + 1
188                 if bits is None:
189                         if height % self.DifficultyChangeMod == 1 or self.currentBlock[2] is None:
190                                 self.logger.warning('New block: %s (height %d; bits: UNKNOWN)' % (b2a_hex(newBlock[::-1]).decode('utf8'), height))
191                                 # Pretend to be 1 lower height, so we possibly retain nextMerkleRoots
192                                 self.currentBlock = (None, height - 1, None)
193                                 OCMR = self.clearMerkleRoots
194                                 self.clearMerkleRoots = Queue(self.WorkQueueSizeClear[1])
195                                 if OCMR.empty():
196                                         OCMR.put(None)
197                                 self.merkleRoots.clear()
198                                 self.ready = False
199                                 return
200                         else:
201                                 bits = self.currentBlock[2]
202                 
203                 if _HBH is None:
204                         _HBH = (b2a_hex(newBlock[::-1]).decode('utf8'), b2a_hex(bits[::-1]).decode('utf8'))
205                 self.logger.info('New block: %s (height: %d; bits: %s)' % (_HBH[0], height, _HBH[1]))
206                 self.currentBlock = (newBlock, height, bits)
207                 
208                 if lastHeight != height:
209                         # TODO: Perhaps reuse clear merkle trees more intelligently
210                         OCMR = self.clearMerkleRoots
211                         if lastHeight == height - 1:
212                                 self.curClearMerkleTree = self.nextMerkleTree
213                                 self.clearMerkleRoots = self.nextMerkleRoots
214                                 self.logger.debug('Adopting next-height clear merkleroots :)')
215                         else:
216                                 if lastHeight:
217                                         self.logger.warning('Change from height %d->%d; no longpoll merkleroots available!' % (lastHeight, height))
218                                 self.curClearMerkleTree = self.createClearMerkleTree(height)
219                                 self.clearMerkleRoots = Queue(self.WorkQueueSizeClear[1])
220                         if OCMR.empty():
221                                 OCMR.put(None)
222                         self.nextMerkleTree = self.createClearMerkleTree(height + 1)
223                         self.nextMerkleRoots = Queue(self._MaxClearSize)
224                 else:
225                         self.logger.debug('Already using clear merkleroots for this height')
226                 self.currentMerkleTree = self.curClearMerkleTree
227                 self.merkleRoots.clear()
228                 
229                 if not self.ready:
230                         self.ready = True
231                         with self.readyCV:
232                                 self.readyCV.notify_all()
233                 
234                 self.needMerkle = 2
235                 self.onBlockChange()
236         
237         def _trimBlock(self, MP, txnlist, txninfo, floodn, msgf):
238                 fee = txninfo[-1].get('fee', None)
239                 if fee is None:
240                         raise self._floodCritical(now, floodn, doin=msgf('fees unknown'))
241                 if fee:
242                         # FIXME: coinbasevalue is *not* guaranteed to exist here
243                         MP['coinbasevalue'] -= fee
244                 
245                 txnlist[-1:] = ()
246                 txninfo[-1:] = ()
247                 
248                 return True
249         
250         # Aggressive "Power Of Two": Remove transactions even with fees to reach our goal
251         def _APOT(self, txninfopot, MP, POTInfo):
252                 feeTxnsTrimmed = 0
253                 feesTrimmed = 0
254                 for txn in txninfopot:
255                         if txn.get('fee') is None:
256                                 self._floodWarning(now, 'APOT-No-Fees', doin='Upstream didn\'t provide fee information required for aggressive POT', logf=self.logger.info)
257                                 return
258                         if not txn['fee']:
259                                 continue
260                         feesTrimmed += txn['fee']
261                         feeTxnsTrimmed += 1
262                 MP['coinbasevalue'] -= feesTrimmed
263                 
264                 POTInfo[2] = [feeTxnsTrimmed, feesTrimmed]
265                 self._floodWarning(now, 'POT-Trimming-Fees', doin='Aggressive POT trimming %d transactions with %d.%08d BTC total fees' % (feeTxnsTrimmed, feesTrimmed//100000000, feesTrimmed % 100000000), logf=self.logger.debug)
266                 
267                 return True
268         
269         def _makeBlockSafe(self, MP, txnlist, txninfo):
270                 blocksize = sum(map(len, txnlist)) + 80
271                 while blocksize > 934464:  # 1 "MB" limit - 64 KB breathing room
272                         txnsize = len(txnlist[-1])
273                         self._trimBlock(MP, txnlist, txninfo, 'SizeLimit', lambda x: 'Making blocks over 1 MB size limit (%d bytes; %s)' % (blocksize, x))
274                         blocksize -= txnsize
275                 
276                 # NOTE: This check doesn't work at all without BIP22 transaction obj format
277                 blocksigops = sum(a.get('sigops', 0) for a in txninfo)
278                 while blocksigops > 19488:  # 20k limit - 0x200 breathing room
279                         txnsigops = txninfo[-1]['sigops']
280                         self._trimBlock(MP, txnlist, txninfo, 'SigOpLimit', lambda x: 'Making blocks over 20k SigOp limit (%d; %s)' % (blocksigops, x))
281                         blocksigops -= txnsigops
282                 
283                 # Aim to produce blocks with "Power Of Two" transaction counts
284                 # This helps avoid any chance of someone abusing CVE-2012-2459 with them
285                 POTMode = getattr(self, 'POT', 1)
286                 txncount = len(txnlist) + 1
287                 if POTMode:
288                         feetxncount = txncount
289                         for i in range(txncount - 2, -1, -1):
290                                 if 'fee' not in txninfo[i] or txninfo[i]['fee']:
291                                         break
292                                 feetxncount -= 1
293                         
294                         if getattr(self, 'Greedy', None):
295                                 # Aim to cut off extra zero-fee transactions on the end
296                                 # NOTE: not cutting out ones intermixed, in case of dependencies
297                                 idealtxncount = feetxncount
298                         else:
299                                 idealtxncount = txncount
300                         
301                         pot = 2**int(log(idealtxncount, 2))
302                         POTInfo = MP['POTInfo'] = [[idealtxncount, feetxncount, txncount], [pot, None], None]
303                         if pot < idealtxncount:
304                                 if pot * 2 <= txncount:
305                                         pot *= 2
306                                 elif pot >= feetxncount:
307                                         pass
308                                 elif POTMode > 1 and self._APOT(txninfo[pot-1:], MP, POTInfo):
309                                         # Trimmed even transactions with fees
310                                         pass
311                                 else:
312                                         pot = idealtxncount
313                                         self._floodWarning(now, 'Non-POT', doin='Making merkle tree with %d transactions (ideal: %d; max: %d)' % (pot, idealtxncount, txncount))
314                         POTInfo[1][1] = pot
315                         pot -= 1
316                         txnlist[pot:] = ()
317                         txninfo[pot:] = ()
318         
319         def _CallGBT(self, TS):
320                 access = TS['access']
321                 self.logger.debug('Requesting new template from \'%s\'' % (TS['name'],))
322                 try:
323                         # First, try BIP 22 standard getblocktemplate :)
324                         MP = access.getblocktemplate(self.GBTReq)
325                         access.OldGMP = False
326                 except:
327                         try:
328                                 # Failing that, give BIP 22 draft (2012-02 through 2012-07) getmemorypool a chance
329                                 MP = access.getmemorypool(self.GMPReq)
330                         except:
331                                 try:
332                                         # Finally, fall back to bitcoind 0.5/0.6 getmemorypool
333                                         MP = access.getmemorypool()
334                                 except:
335                                         MP = False
336                         if MP is False:
337                                 # This way, we get the error from the BIP22 call if the old one fails too
338                                 raise
339                         
340                         # Pre-BIP22 server (bitcoind <0.7 or Eloipool <20120513)
341                         if not access.OldGMP:
342                                 access.OldGMP = True
343                                 self.logger.warning('Upstream \'%s\' is not BIP 22 compatible' % (TS['name'],))
344                 
345                 return MP
346         
347         def _ProcessGBT(self, MP):
348                 oMP = MP
349                 MP = deepcopy(MP)
350                 
351                 prevBlock = bytes.fromhex(MP['previousblockhash'])[::-1]
352                 if 'height' not in MP:
353                         MP['height'] = self.access.getinfo()['blocks'] + 1
354                 height = MP['height']
355                 bits = bytes.fromhex(MP['bits'])[::-1]
356                 (MP['_bits'], MP['_prevBlock']) = (bits, prevBlock)
357                 if (prevBlock, height, bits) != self.currentBlock and (self.currentBlock[1] is None or height > self.currentBlock[1]):
358                         self.updateBlock(prevBlock, height, bits, _HBH=(MP['previousblockhash'], MP['bits']))
359                 
360                 txnlist = MP['transactions']
361                 if len(txnlist) and isinstance(txnlist[0], dict):
362                         txninfo = txnlist
363                         txnlist = tuple(a['data'] for a in txnlist)
364                 elif 'transactionfees' in MP:
365                         # Backward compatibility with pre-BIP22 gmp_fees branch
366                         txninfo = [{'fee':a} for a in MP['transactionfees']]
367                 else:
368                         # Backward compatibility with pre-BIP22 hex-only (bitcoind <0.7, Eloipool <future)
369                         txninfo = [{}] * len(txnlist)
370                 # TODO: cache Txn or at least txid from previous merkle roots?
371                 txnlist = [a for a in map(bytes.fromhex, txnlist)]
372                 
373                 self._makeBlockSafe(MP, txnlist, txninfo)
374                 
375                 cbtxn = self.makeCoinbaseTxn(MP['coinbasevalue'])
376                 cbtxn.setCoinbase(b'\0\0')
377                 cbtxn.assemble()
378                 txnlist.insert(0, cbtxn.data)
379                 txninfo.insert(0, {
380                 })
381                 
382                 txnlist = [a for a in map(Txn, txnlist[1:])]
383                 txnlist.insert(0, cbtxn)
384                 txnlist = list(txnlist)
385                 newMerkleTree = MerkleTree(txnlist)
386                 newMerkleTree.POTInfo = MP.get('POTInfo')
387                 newMerkleTree.MP = MP
388                 newMerkleTree.oMP = oMP
389                 
390                 return newMerkleTree
391         
392         def _CheckTemplate(self, newMerkleTree, TS):
393                 TCList = self.TemplateChecks
394                 if not TCList:
395                         if 'proposal' not in oMP.get('capabilities', ()):
396                                 return (0, newMerkleTree)
397                         TCList = (
398                                 {
399                                         'name': TS['name'],
400                                         'access': TS['access'],
401                                         'unanimous': True,
402                                         'weight': 1,
403                                 },
404                         )
405                 
406                 MP = newMerkleTree.MP
407                 (prevBlock, height, bits) = (MP['_prevBlock'], MP['height'], MP['_bits'])
408                 txnlist = newMerkleTree.data
409                 cbtxn = txnlist[0]
410                 
411                 coinbase = self.makeCoinbase(height=height)
412                 cbtxn.setCoinbase(coinbase)
413                 cbtxn.assemble()
414                 merkleRoot = newMerkleTree.merkleRoot()
415                 MRD = (merkleRoot, newMerkleTree, coinbase, prevBlock, bits)
416                 blkhdr = MakeBlockHeader(MRD)
417                 data = assembleBlock(blkhdr, txnlist)
418                 ProposeReq = {
419                         "mode": "proposal",
420                         "data": b2a_hex(data).decode('utf8'),
421                 }
422                 
423                 AcceptedScore = 0
424                 RejectedScore = 0
425                 Rejections = {}
426                 ProposalErrors = {}
427                 for TC in TCList:
428                         caccess = TC['access']
429                         try:
430                                 propose = caccess.getblocktemplate(ProposeReq)
431                         except (socket.error, ValueError) as e:
432                                 self.logger.error('Upstream \'%s\' errored on proposal from \'%s\': %s' % (TC['name'], TS['name'], e))
433                                 ProposalErrors[TC['name']] = e
434                                 continue
435                         if propose is None:
436                                 AcceptedScore += TC['weight']
437                                 self.logger.debug('Upstream \'%s\' accepted proposal' % (TC['name'],))
438                         elif propose == 'orphan':
439                                 self.logger.debug('Upstream \'%s\' considered proposal an orphan' % (TC['name'],))
440                                 ProposalErrors[TC['name']] = propose
441                         else:
442                                 RejectedScore += TC['weight']
443                                 Rejections[TC['name']] = propose
444                                 try:
445                                         propose = propose['reject-reason']
446                                 except:
447                                         pass
448                                 self.logger.error('Upstream \'%s\' rejected proposed block from \'%s\': %s' % (TC['name'], TS['name'], propose))
449                 
450                 if Rejections:
451                         RPInfo = {
452                                 'merkleTree': newMerkleTree,
453                                 'AcceptedScore': AcceptedScore,
454                                 'RejectedScore': RejectedScore,
455                                 'Rejections': Rejections,
456                                 'ProposalErrors': ProposalErrors,
457                         }
458                         self.RejectedProposal = RPInfo
459                         
460                         try:
461                                 global _filecounter
462                                 _filecounter += 1
463                                 import pickle
464                                 with open('RejectedProposals/%d_%d' % (int(time()), _filecounter), 'wb') as f:
465                                         pickle.dump(RPInfo, f)
466                         except IOError:
467                                 pass
468                 
469                 TotalScore = AcceptedScore + RejectedScore
470                 
471                 return (AcceptedScore, TotalScore)
472         
473         def _updateMerkleTree_fromTS(self, TS):
474                 MP = self._CallGBT(TS)
475                 newMerkleTree = self._ProcessGBT(MP)
476                 
477                 # Some versions of bitcoinrpc ServiceProxy have problems copying/pickling, so just store name and URI for now
478                 newMerkleTree.source = TS['name']
479                 newMerkleTree.source_uri = TS['uri']
480                 
481                 (AcceptedScore, TotalScore) = self._CheckTemplate(newMerkleTree, TS)
482                 
483                 AcceptRatio = AcceptedScore / TotalScore
484                 
485                 self.logger.debug('Template from \'%s\' has %s acceptance ratio and score of %s' % (TS['name'], AcceptRatio, AcceptedScore))
486                 
487                 if AcceptRatio <= self.MinimumTemplateAcceptanceRatio:
488                         return None
489                 
490                 if TotalScore < self.MinimumTemplateScore:
491                         return None
492                 
493                 return (AcceptRatio, newMerkleTree)
494         
495         def _updateMerkleTree_I(self):
496                 Best = (-1, None)
497                 for TSPriList in self.TemplateSources:
498                         # FIXME: Implement weighting
499                         for i in range(len(TSPriList)):
500                                 TS = TSPriList.pop(0)
501                                 TSPriList.append(TS)
502                                 
503                                 try:
504                                         r = self._updateMerkleTree_fromTS(TS)
505                                         if r is None:
506                                                 # Failed completely
507                                                 continue
508                                         if Best[0] < r[0]:
509                                                 Best = r
510                                                 if r[0] == 1:
511                                                         break
512                                 except:
513                                         if TSPriList == self.TemplateSources[-1] and i == len(TSPriList) - 1 and Best[1] is None:
514                                                 raise
515                                         else:
516                                                 self.logger.error(traceback.format_exc())
517                 
518                 BestMT = Best[1]
519                 if BestMT is None:
520                         raise RuntimeError('Failed to create usable template')
521                 
522                 self.logger.debug('Updating merkle tree with template from \'%s\'' % (BestMT.source,))
523                 MP = BestMT.MP
524                 blkbasics = (MP['_prevBlock'], MP['height'], MP['_bits'])
525                 if blkbasics != self.currentBlock:
526                         self.updateBlock(*blkbasics, _HBH=(MP['previousblockhash'], MP['bits']))
527                 self.currentMerkleTree = BestMT
528         
529         def updateMerkleTree(self):
530                 global now
531                 self.logger.debug('Polling for new block template')
532                 self.nextMerkleUpdate = now + self.TxnUpdateRetryWait
533                 
534                 self._updateMerkleTree_I()
535                 
536                 self.lastMerkleUpdate = now
537                 self.nextMerkleUpdate = now + self.MinimumTxnUpdateWait
538                 
539                 if self.needMerkle == 2:
540                         self.needMerkle = 1
541                         self.needMerkleSince = now
542         
543         def makeCoinbase(self, height):
544                 now = int(time())
545                 if now > _makeCoinbase[0]:
546                         _makeCoinbase[0] = now
547                         _makeCoinbase[1] = 0
548                 else:
549                         _makeCoinbase[1] += 1
550                 rv = self.CoinbasePrefix
551                 rv += pack('>L', now) + pack('>Q', _makeCoinbase[1]).lstrip(b'\0')
552                 # NOTE: Not using varlenEncode, since this is always guaranteed to be < 100
553                 rv = bytes( (len(rv),) ) + rv
554                 for v in self.CoinbaseAux.values():
555                         rv += v
556                 if len(rv) > 95:
557                         t = time()
558                         if self.overflowed < t - 300:
559                                 self.logger.warning('Overflowing coinbase data! %d bytes long' % (len(rv),))
560                                 self.overflowed = t
561                                 self.isOverflowed = True
562                         rv = rv[:95]
563                 else:
564                         self.isOverflowed = False
565                 rv = bitcoin.script.encodeUNum(height) + rv
566                 return rv
567         
568         def makeMerkleRoot(self, merkleTree, height):
569                 cbtxn = merkleTree.data[0]
570                 cb = self.makeCoinbase(height=height)
571                 cbtxn.setCoinbase(cb)
572                 cbtxn.assemble()
573                 merkleRoot = merkleTree.merkleRoot()
574                 return (merkleRoot, merkleTree, cb)
575         
576         _doing_last = None
577         def _doing(self, what):
578                 if self._doing_last == what:
579                         self._doing_i += 1
580                         return
581                 global now
582                 if self._doing_last:
583                         self.logger.debug("Switching from (%4dx in %5.3f seconds) %s => %s" % (self._doing_i, now - self._doing_s, self._doing_last, what))
584                 self._doing_last = what
585                 self._doing_i = 1
586                 self._doing_s = now
587         
588         def _floodWarning(self, now, wid, wmsgf = None, doin = True, logf = None):
589                 if doin is True:
590                         doin = self._doing_last
591                         def a(f = wmsgf):
592                                 return lambda: "%s (doing %s)" % (f(), doin)
593                         wmsgf = a()
594                 winfo = self.lastWarning.setdefault(wid, [0, None])
595                 (lastTime, lastDoing) = winfo
596                 if now <= lastTime + max(5, self.MinimumTxnUpdateWait):
597                         return
598                 winfo[0] = now
599                 nowDoing = doin
600                 winfo[1] = nowDoing
601                 if logf is None:
602                         logf = self.logger.warning
603                 logf(wmsgf() if wmsgf else doin)
604         
605         def _makeOne(self, putf, merkleTree, height):
606                 MakingAtThisHeight = self.currentBlock[1]
607                 MR = self.makeMerkleRoot(merkleTree, height=height)
608                 # Only add it if the height hasn't changed in the meantime, to avoid a race
609                 if self.currentBlock[1] == MakingAtThisHeight:
610                         putf(MR)
611         
612         def makeClear(self):
613                 self._doing('clear merkle roots')
614                 self._makeOne(self.clearMerkleRoots.put, self.curClearMerkleTree, height=self.currentBlock[1])
615         
616         def makeNext(self):
617                 self._doing('longpoll merkle roots')
618                 self._makeOne(self.nextMerkleRoots.put, self.nextMerkleTree, height=self.currentBlock[1] + 1)
619         
620         def makeRegular(self):
621                 self._doing('regular merkle roots')
622                 self._makeOne(self.merkleRoots.append, self.currentMerkleTree, height=self.currentBlock[1])
623         
624         def merkleMaker_II(self):
625                 global now
626                 
627                 # No bits = no mining :(
628                 if not self.ready:
629                         return self.updateMerkleTree()
630                 
631                 # First, ensure we have the minimum clear, next, and regular (in that order)
632                 if self.clearMerkleRoots.qsize() < self.WorkQueueSizeClear[0]:
633                         return self.makeClear()
634                 if self.nextMerkleRoots.qsize() < self.WorkQueueSizeLongpoll[0]:
635                         return self.makeNext()
636                 if len(self.merkleRoots) < self.WorkQueueSizeRegular[0]:
637                         return self.makeRegular()
638                 
639                 # If we've met the minimum requirements, consider updating the merkle tree
640                 if self.nextMerkleUpdate <= now:
641                         return self.updateMerkleTree()
642                 
643                 # Finally, fill up clear, next, and regular until we've met the maximums
644                 if self.clearMerkleRoots.qsize() < self.WorkQueueSizeClear[1]:
645                         return self.makeClear()
646                 if self.nextMerkleRoots.qsize() < self.WorkQueueSizeLongpoll[1]:
647                         return self.makeNext()
648                 if len(self.merkleRoots) < self.WorkQueueSizeRegular[1] or self.merkleRoots[0][1] != self.currentMerkleTree:
649                         return self.makeRegular()
650                 
651                 # Nothing left to do, fire onBlockUpdate event (if appropriate) and sleep
652                 if self.needMerkle == 1:
653                         self.onBlockUpdate()
654                         self.needMerkle = False
655                 self._doing('idle')
656                 # TODO: rather than sleepspin, block until MinimumTxnUpdateWait expires or threading.Condition(?)
657                 sleep(self.IdleSleepTime)
658         
659         def merkleMaker_I(self):
660                 global now
661                 now = time()
662                 
663                 self.merkleMaker_II()
664                 
665                 if self.needMerkle == 1 and now > self.needMerkleSince + self.WarningDelayTxnLongpoll:
666                         self._floodWarning(now, 'NeedMerkle', lambda: 'Transaction-longpoll requested %d seconds ago, and still not ready. Is your server fast enough to keep up with your configured WorkQueueSizeRegular maximum?' % (now - self.needMerkleSince,))
667                 if now > self.nextMerkleUpdate + self.WarningDelayMerkleUpdate:
668                         self._floodWarning(now, 'MerkleUpdate', lambda: "Haven't updated the merkle tree in at least %d seconds! Is your server fast enough to keep up with your configured work queue minimums?" % (now - self.lastMerkleUpdate,))
669         
670         def run(self):
671                 while True:
672                         try:
673                                 self.merkleMaker_I()
674                         except:
675                                 self.logger.critical(traceback.format_exc())
676         
677         def start(self, *a, **k):
678                 self._prepare()
679                 super().start(*a, **k)
680         
681         def getMRD(self):
682                 try:
683                         MRD = self.merkleRoots.pop()
684                         self.LowestMerkleRoots = min(len(self.merkleRoots), self.LowestMerkleRoots)
685                         rollPrevBlk = False
686                 except IndexError:
687                         qsz = self.clearMerkleRoots.qsize()
688                         if qsz < 0x10:
689                                 self.logger.warning('clearMerkleRoots running out! only %d left' % (qsz,))
690                         MRD = None
691                         while MRD is None:
692                                 MRD = self.clearMerkleRoots.get()
693                         self.LowestClearMerkleRoots = min(self.clearMerkleRoots.qsize(), self.LowestClearMerkleRoots)
694                         rollPrevBlk = True
695                 (merkleRoot, merkleTree, cb) = MRD
696                 (prevBlock, height, bits) = self.currentBlock
697                 return (merkleRoot, merkleTree, cb, prevBlock, bits, rollPrevBlk)
698         
699         def getMC(self, wantClear = False):
700                 if not self.ready:
701                         with self.readyCV:
702                                 while not self.ready:
703                                         self.readyCV.wait()
704                 (prevBlock, height, bits) = self.currentBlock
705                 mt = self.curClearMerkleTree if wantClear else self.currentMerkleTree
706                 cb = self.makeCoinbase(height=height)
707                 rollPrevBlk = (mt == self.curClearMerkleTree)
708                 return (height, mt, cb, prevBlock, bits, rollPrevBlk)
709
710 # merkleMaker tests
711 def _test():
712         global now
713         now = 1337039788
714         MM = merkleMaker()
715         reallogger = MM.logger
716         class fakelogger:
717                 LO = False
718                 def critical(self, *a):
719                         if self.LO > 1: return
720                         reallogger.critical(*a)
721                 def warning(self, *a):
722                         if self.LO: return
723                         reallogger.warning(*a)
724                 def debug(self, *a):
725                         pass
726         MM.logger = fakelogger()
727         class NMTClass:
728                 pass
729         
730         # _makeBlockSafe tests
731         from copy import deepcopy
732         MP = {
733                 'coinbasevalue':50,
734         }
735         txnlist = [b'\0', b'\x01', b'\x02']
736         txninfo = [{'fee':0, 'sigops':1}, {'fee':5, 'sigops':10000}, {'fee':0, 'sigops':10001}]
737         def MBS(LO = 0):
738                 m = deepcopy( (MP, txnlist, txninfo) )
739                 MM.logger.LO = LO
740                 try:
741                         MM._makeBlockSafe(*m)
742                 except:
743                         if LO < 2:
744                                 raise
745                 else:
746                         assert LO < 2  # An expected error wasn't thrown
747                 if 'POTInfo' in m[0]:
748                         del m[0]['POTInfo']
749                 return m
750         MM.POT = 0
751         assert MBS() == (MP, txnlist[:2], txninfo[:2])
752         txninfo[2]['fee'] = 1
753         MPx = deepcopy(MP)
754         MPx['coinbasevalue'] -= 1
755         assert MBS() == (MPx, txnlist[:2], txninfo[:2])
756         txninfo[2]['sigops'] = 1
757         assert MBS(1) == (MP, txnlist, txninfo)
758         # APOT tests
759         MM.POT = 2
760         txnlist.append(b'\x03')
761         txninfo.append({'fee':1, 'sigops':0})
762         MPx = deepcopy(MP)
763         MPx['coinbasevalue'] -= 1
764         assert MBS() == (MPx, txnlist[:3], txninfo[:3])
765         # POT tests
766         MM.POT = 1
767         MM.Greedy = True
768         txninfo[1]['fee'] = 0
769         txninfo[2]['fee'] = 0
770         assert MBS(1) == (MP, txnlist, txninfo)
771         # _ProcessGBT tests
772         def makeCoinbaseTxn(coinbaseValue, useCoinbaser = True):
773                 txn = Txn.new()
774                 txn.addOutput(coinbaseValue, b'')
775                 return txn
776         MM.makeCoinbaseTxn = makeCoinbaseTxn
777         MM.updateBlock = lambda *a, **ka: None
778         gbt = {
779                 'transactions': [
780                         {'data': '11', 'depends': [], 'fee': 1, 'sigops': 1},
781                         {'data': '11', 'depends': [], 'fee': 0, 'sigops': 1},
782                         {'data': '11', 'depends': [], 'fee': 0, 'sigops': 1},
783                         {'data': '11', 'depends': [], 'fee': 1, 'sigops': 2}
784                 ],
785                 'height': 219507,
786                 'coinbasevalue': 3,
787                 'previousblockhash': '000000000000012806bc100006dc83220bd9c2ac2709dc14a0d0fa1d6f9b733c',
788                 'version': 1,
789                 'bits': '1a05a6b1'
790         }
791         nMT = MM._ProcessGBT(gbt)
792         assert len(nMT.data) == 5
793         nMT.data[0].disassemble()
794         assert sum(outp[0] for outp in nMT.data[0].outputs) == 3
795         MM.POT = 2
796         nMT = MM._ProcessGBT(gbt)
797         assert len(nMT.data) in (2, 4)
798         nMT.data[0].disassemble()
799         assert sum(outp[0] for outp in nMT.data[0].outputs) == 2
800
801 _test()