def checkShare(share): shareTime = share['time'] = time() username = share['username'] if 'data' in share: # getwork/GBT checkData(share) data = share['data'] if username not in workLog: raise RejectedShare('unknown-user') MWL = workLog[username] shareMerkleRoot = data[36:68] if 'blkdata' in share: pl = share['blkdata'] (txncount, pl) = varlenDecode(pl) cbtxn = bitcoin.txn.Txn(pl) othertxndata = cbtxn.disassemble(retExtra=True) coinbase = cbtxn.getCoinbase() wliPos = coinbase[0] + 2 wliLen = coinbase[wliPos - 1] wli = coinbase[wliPos:wliPos+wliLen] mode = 'MC' moden = 1 else: wli = shareMerkleRoot mode = 'MRD' moden = 0 coinbase = None else: # Stratum MWL = workLog[None] wli = share['jobid'] buildStratumData(share, b'\0' * 32) mode = 'MC' moden = 1 othertxndata = b'' if wli not in MWL: raise RejectedShare('unknown-work') (wld, issueT) = MWL[wli] share[mode] = wld share['issuetime'] = issueT (workMerkleTree, workCoinbase) = wld[1:3] if 'jobid' in share: cbtxn = deepcopy(workMerkleTree.data[0]) coinbase = workCoinbase + share['extranonce1'] + share['extranonce2'] cbtxn.setCoinbase(coinbase) cbtxn.assemble() data = buildStratumData(share, workMerkleTree.withFirst(cbtxn)) shareMerkleRoot = data[36:68] if data in DupeShareHACK: raise RejectedShare('duplicate') DupeShareHACK[data] = None blkhash = PoWHash(data) blkhashn = LEhash2int(blkhash) global networkTarget logfunc = getattr(checkShare.logger, 'info' if blkhashn <= networkTarget else 'debug') logfunc('BLKHASH: %64x' % (blkhashn,)) logfunc(' TARGET: %64x' % (networkTarget,)) # NOTE: this isn't actually needed for MC mode, but we're abusing it for a trivial share check... txlist = workMerkleTree.data txlist = [deepcopy(txlist[0]),] + txlist[1:] cbtxn = txlist[0] cbtxn.setCoinbase(coinbase or workCoinbase) cbtxn.assemble() if blkhashn <= networkTarget: logfunc("Submitting upstream") RBDs.append( deepcopy( (data, txlist, share.get('blkdata', None), workMerkleTree, share, wld) ) ) if not moden: payload = assembleBlock(data, txlist) else: payload = share['data'] if len(othertxndata): payload += share['blkdata'] else: payload += assembleBlock(data, txlist)[80:] logfunc('Real block payload: %s' % (b2a_hex(payload).decode('utf8'),)) RBPs.append(payload) threading.Thread(target=blockSubmissionThread, args=(payload, blkhash, share)).start() bcnode.submitBlock(payload) if config.DelayLogForUpstream: share['upstreamRejectReason'] = PendingUpstream else: share['upstreamRejectReason'] = None share['upstreamResult'] = True MM.updateBlock(blkhash) # Gotwork hack... if gotwork and blkhashn <= config.GotWorkTarget: try: coinbaseMrkl = cbtxn.data coinbaseMrkl += blkhash steps = workMerkleTree._steps coinbaseMrkl += pack('B', len(steps)) for step in steps: coinbaseMrkl += step coinbaseMrkl += b"\0\0\0\0" info = {} info['hash'] = b2a_hex(blkhash).decode('ascii') info['header'] = b2a_hex(data).decode('ascii') info['coinbaseMrkl'] = b2a_hex(coinbaseMrkl).decode('ascii') thr = threading.Thread(target=submitGotwork, args=(info,)) thr.daemon = True thr.start() except: checkShare.logger.warning('Failed to build gotwork request') if 'target' in share: workTarget = share['target'] elif len(wld) > 6: workTarget = wld[6] else: workTarget = None if workTarget is None: workTarget = config.ShareTarget if blkhashn > workTarget: raise RejectedShare('high-hash') share['target'] = workTarget share['difficulty'] = config.ShareTarget / workTarget share['_targethex'] = '%064x' % (workTarget,) shareTimestamp = unpack('<L', data[68:72])[0] if shareTime < issueT - 120: raise RejectedShare('stale-work') if shareTimestamp < shareTime - 300: raise RejectedShare('time-too-old') if shareTimestamp > shareTime + 7200: raise RejectedShare('time-too-new') if config.DynamicTargetting and username in userStatus: # NOTE: userStatus[username] only doesn't exist across restarts status = userStatus[username] target = status[0] or config.ShareTarget if target == workTarget: userStatus[username][2] += 1 else: userStatus[username][2] += float(target) / workTarget if moden: cbpre = workCoinbase cbpreLen = len(cbpre) if coinbase[:cbpreLen] != cbpre: raise RejectedShare('bad-cb-prefix') # Filter out known "I support" flags, to prevent exploits for ff in (b'/P2SH/', b'NOP2SH', b'p2sh/CHV', b'p2sh/NOCHV'): if coinbase.find(ff) > max(-1, cbpreLen - len(ff)): raise RejectedShare('bad-cb-flag') if len(coinbase) > 100: raise RejectedShare('bad-cb-length') if shareMerkleRoot != workMerkleTree.withFirst(cbtxn): raise RejectedShare('bad-txnmrklroot') if len(othertxndata): allowed = assembleBlock(data, txlist)[80:] if allowed != share['blkdata']: raise RejectedShare('bad-txns')
def checkShare(share): global rskLastReceivedShareTime global rskSubmittedShares shareTime = share['time'] = time() username = share['username'] checkQuickDiffAdjustment = False if 'data' in share: # getwork/GBT data = share['data'] shareMerkleRoot = data[36:68] if 'blkdata' in share: pl = share['blkdata'] (txncount, pl) = varlenDecode(pl) cbtxn = bitcoin.txn.Txn(pl) othertxndata = cbtxn.disassemble(retExtra=True) coinbase = cbtxn.getCoinbase() wliPos = coinbase[0] + 2 wliLen = coinbase[wliPos - 1] wli = coinbase[wliPos:wliPos + wliLen] mode = 'MC' moden = 1 else: wli = shareMerkleRoot mode = 'MRD' moden = 0 coinbase = None (wld, issueT) = LookupWork(username, wli) checkData(share, wld) else: # Stratum checkQuickDiffAdjustment = config.DynamicTargetQuick wli = share['jobid'] (wld, issueT) = LookupWork(None, wli) mode = 'MC' moden = 1 othertxndata = b'' share[mode] = wld share['issuetime'] = issueT (workMerkleTree, workCoinbase) = wld[1:3] share['merkletree'] = workMerkleTree if 'jobid' in share: cbtxn = deepcopy(workMerkleTree.data[0]) coinbase = workCoinbase + share['extranonce1'] + share['extranonce2'] cbtxn.setCoinbase(coinbase) cbtxn.assemble() data = buildStratumData(share, workMerkleTree.withFirst(cbtxn), workMerkleTree.MP['_BlockVersionBytes']) shareMerkleRoot = data[36:68] if data in DupeShareHACK: raise RejectedShare('duplicate') DupeShareHACK[data] = None blkhash = dblsha(data) if not hasattr(config, 'DEV_MODE_ON') or not config.DEV_MODE_ON: if not _reachesLowestDifficulty(blkhash): if len(share[mode]) >= 4: # currentblock may be changed, so we retry looking up some previous block saved (in RKS could be usefull prev blocks). blockToUse = (share[mode][3], share[mode][0], share[mode][4]) retryData = buildStratumData( share, workMerkleTree.withFirst(cbtxn), workMerkleTree.MP['_BlockVersionBytes'], blockToUse) shareMerkleRoot = retryData[36:68] blkhash = dblsha(retryData) # retryData could be equal than data so we check again previous conditions if retryData == data or not _reachesLowestDifficulty(blkhash): raise RejectedShare('H-not-zero') data = retryData DupeShareHACK[data] = None else: raise RejectedShare('H-not-zero') blkhashn = LEhash2int(blkhash) global networkTarget logfunc = getattr(checkShare.logger, 'info' if blkhashn <= networkTarget else 'debug') logfunc('BLKHASH: %64x' % (blkhashn, )) logfunc(' TARGET: %64x' % (networkTarget, )) # NOTE: this isn't actually needed for MC mode, but we're abusing it for a trivial share check... txlist = workMerkleTree.data txlist = [ deepcopy(txlist[0]), ] + txlist[1:] cbtxn = txlist[0] cbtxn.setCoinbase(coinbase or workCoinbase) cbtxn.assemble() rootstockBlockHash = None rootstockTarget = None if hasattr(workMerkleTree, "rootstockBlockInfo" ) and workMerkleTree.rootstockBlockInfo is not None: rootstockBlockHash = workMerkleTree.rootstockBlockInfo[0] if hasattr(config, 'DEV_MODE_ON') and config.DEV_MODE_ON: rootstockTarget = bdiff2target(config.RSK_ELOIPOOL_DIFF) else: rootstockTarget = workMerkleTree.rootstockBlockInfo[1] submitRootstock = rootstockTarget is not None and blkhashn <= rootstockTarget if rskLastReceivedShareTime is None: rskLastReceivedShareTime = int(round(time() * 1000)) rskSubmittedShares = 0 lastReceivedShareTimeNow = int(round(time() * 1000)) if lastReceivedShareTimeNow - rskLastReceivedShareTime >= 1000: rskSubmittedShares = 0 rskLastReceivedShareTime = lastReceivedShareTimeNow checkShare.logger.info( "ROOTSTOCK_DEBUG: RSKValidShare: {0} {1} {2}".format( blkhashn, rootstockTarget, submitRootstock)) if lastReceivedShareTimeNow - rskLastReceivedShareTime < 1000 and rskSubmittedShares < 1 and submitRootstock: rskSubmittedShares += 1 else: submitRootstock = False submitBitcoin = blkhashn <= networkTarget #checkShare.logger.info("ROOTSTOCK_DEBUG: BTCValidShare: {0} {1} {2}".format(blkhashn, networkTarget, submitBitcoin)) if submitBitcoin or submitRootstock: if submitBitcoin: share['BTC_SOLUTION'] = True logfunc("Submitting upstream") RBDs.append( deepcopy((data, txlist, share.get('blkdata', None), workMerkleTree, share, wld))) if not moden: payload = assembleBlock(data, txlist) else: payload = share['data'] if len(othertxndata): payload += share['blkdata'] else: payload += assembleBlock(data, txlist)[80:] if submitRootstock: share['RSK_SOLUTION'] = True blockhashHexRskSubmit = b2a_hex(blkhash).decode('ascii') blockheaderHexRskSubmit = b2a_hex(share['data']).decode('ascii') coinbaseHexRskSubmit = b2a_hex(cbtxn.data).decode('ascii') coinbaseHashHexRskSubmit = b2a_hex(cbtxn.txid).decode('ascii') merkleHashesRskSubmit = [ b2a_hex(x).decode('ascii') for x in workMerkleTree._steps ] merkleHashesRskSubmit.insert(0, coinbaseHashHexRskSubmit) merkleHashesRskSubmit = ' '.join(merkleHashesRskSubmit) txnCountRskSubmit = hex(len(txlist))[2:] threading.Thread(target=rootstockSubmissionThread, args=(blockhashHexRskSubmit, blockheaderHexRskSubmit, coinbaseHexRskSubmit, merkleHashesRskSubmit, txnCountRskSubmit, share)).start() if not submitBitcoin: return logfunc('Real block payload: %s' % (b2a_hex(payload).decode('utf8'), )) RBPs.append(payload) threading.Thread(target=blockSubmissionThread, args=(payload, blkhash, share)).start() bcnode.submitBlock(payload) if config.DelayLogForUpstream: share['upstreamRejectReason'] = PendingUpstream else: share['upstreamRejectReason'] = None share['upstreamResult'] = True MM.updateBlock(blkhash) # Gotwork hack... if gotwork and blkhashn <= config.GotWorkTarget: try: coinbaseMrkl = cbtxn.data coinbaseMrkl += blkhash steps = workMerkleTree._steps coinbaseMrkl += pack('B', len(steps)) for step in steps: coinbaseMrkl += step coinbaseMrkl += b"\0\0\0\0" info = {} info['hash'] = b2a_hex(blkhash).decode('ascii') info['header'] = b2a_hex(data).decode('ascii') info['coinbaseMrkl'] = b2a_hex(coinbaseMrkl).decode('ascii') thr = threading.Thread(target=submitGotwork, args=(info, )) thr.daemon = True thr.start() except: checkShare.logger.warning('Failed to build gotwork request') if 'target' in share: workTarget = share['target'] elif len(wld) > 6: workTarget = wld[6] else: workTarget = None if workTarget is None: workTarget = config.ShareTarget if blkhashn > workTarget: raise RejectedShare('high-hash') share['target'] = workTarget share['_targethex'] = '%064x' % (workTarget, ) shareTimestamp = unpack('<L', data[68:72])[0] if shareTime < issueT - config.StaleWorkTimeout: raise RejectedShare('stale-work') if shareTimestamp < shareTime - 300: raise RejectedShare('time-too-old') if shareTimestamp > shareTime + 7200: raise RejectedShare('time-too-new') if moden: cbpre = workCoinbase cbpreLen = len(cbpre) if coinbase[:cbpreLen] != cbpre: raise RejectedShare('bad-cb-prefix') # Filter out known "I support" flags, to prevent exploits for ff in (b'/P2SH/', b'NOP2SH', b'p2sh/CHV', b'p2sh/NOCHV'): if coinbase.find(ff) > max(-1, cbpreLen - len(ff)): raise RejectedShare('bad-cb-flag') if len(coinbase) > 100: raise RejectedShare('bad-cb-length') if shareMerkleRoot != workMerkleTree.withFirst(cbtxn): raise RejectedShare('bad-txnmrklroot') if len(othertxndata): allowed = assembleBlock(data, txlist)[80:] if allowed != share['blkdata']: raise RejectedShare('bad-txns') if config.DynamicTargetting and username in userStatus: # NOTE: userStatus[username] only doesn't exist across restarts status = userStatus[username] target = status[0] or config.ShareTarget if target == workTarget: userStatus[username][2] += 1 else: userStatus[username][2] += float(target) / workTarget if checkQuickDiffAdjustment and userStatus[username][ 2] > config.DynamicTargetGoal * 2: stratumsrv.quickDifficultyUpdate(username)