Skip to content

Commit

Permalink
Merge branch 'bip9'
Browse files Browse the repository at this point in the history
  • Loading branch information
luke-jr committed Jul 30, 2016
2 parents f777cfa + e030e02 commit c96b0a9
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 36 deletions.
47 changes: 22 additions & 25 deletions eloipool.py
Expand Up @@ -28,10 +28,6 @@
__import__(configmod)
config = importlib.import_module(configmod)

if not hasattr(config, 'BlockVersion'):
config.BlockVersion = 4
config.BlockVersionBytes = struct.pack('<L', config.BlockVersion)

if not hasattr(config, 'ServerName'):
config.ServerName = 'Unnamed Eloipool'

Expand Down Expand Up @@ -317,7 +313,7 @@ def RegisterWork(username, wli, wld, RequestedTarget = None):
def getBlockHeader(username):
MRD = MM.getMRD()
merkleRoot = MRD[0]
hdr = MakeBlockHeader(MRD, config.BlockVersionBytes)
hdr = MakeBlockHeader(MRD)
workLog.setdefault(username, {})[merkleRoot] = (MRD, time())
target = RegisterWork(username, merkleRoot, MRD)
return (hdr, workLog[username][merkleRoot], target)
Expand Down Expand Up @@ -439,7 +435,7 @@ def blockSubmissionThread(payload, blkhash, share):
logShare(share)
blockSubmissionThread.logger = logging.getLogger('blockSubmission')

def checkData(share):
def checkData(share, wld):
data = share['data']
data = data[:80]
(prevBlock, height, bits) = MM.currentBlock
Expand All @@ -452,13 +448,14 @@ def checkData(share):
if data[72:76] != bits:
raise RejectedShare('bad-diffbits')

if data[0:4] != config.BlockVersionBytes:
MT = wld[1]
if data[0:4] != MT.MP['_BlockVersionBytes']:
raise RejectedShare('bad-version')

def buildStratumData(share, merkleroot):
def buildStratumData(share, merkleroot, versionbytes):
(prevBlock, height, bits) = MM.currentBlock

data = config.BlockVersionBytes
data = versionbytes
data += prevBlock
data += merkleroot
data += share['ntime'][::-1]
Expand All @@ -478,20 +475,23 @@ def IsJobValid(wli, wluser = None):
return False
return True

def LookupWork(username, wli):
if username not in workLog:
raise RejectedShare('unknown-user')
MWL = workLog[username]
if wli not in MWL:
raise RejectedShare('unknown-work')
return MWL[wli]

def checkShare(share):
shareTime = share['time'] = time()

username = share['username']
checkQuickDiffAdjustment = False
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']
Expand All @@ -509,22 +509,18 @@ def checkShare(share):
mode = 'MRD'
moden = 0
coinbase = None

(wld, issueT) = LookupWork(username, wli)
checkData(share, wld)
else:
# Stratum
checkQuickDiffAdjustment = config.DynamicTargetQuick
wli = share['jobid']
buildStratumData(share, b'\0' * 32)
(wld, issueT) = LookupWork(None, wli)
mode = 'MC'
moden = 1
othertxndata = b''
if None not in workLog:
# We haven't yet sent any stratum work for this block
raise RejectedShare('unknown-work')
MWL = workLog[None]

if wli not in MWL:
raise RejectedShare('unknown-work')
(wld, issueT) = MWL[wli]
share[mode] = wld

share['issuetime'] = issueT
Expand All @@ -536,7 +532,7 @@ def checkShare(share):
coinbase = workCoinbase + share['extranonce1'] + share['extranonce2']
cbtxn.setCoinbase(coinbase)
cbtxn.assemble()
data = buildStratumData(share, workMerkleTree.withFirst(cbtxn))
data = buildStratumData(share, workMerkleTree.withFirst(cbtxn), workMerkleTree.MP['_BlockVersionBytes'])
shareMerkleRoot = data[36:68]

if data in DupeShareHACK:
Expand Down Expand Up @@ -687,6 +683,9 @@ def receiveShare(share):
share['rejectReason'] = 'ERROR'
raise
finally:
if 'data' not in share:
# In case of rejection, data might not have been defined yet, but logging may need it
buildStratumData(share, b'\0' * 32, b'\xff\xff\xff\xff')
if not share.get('upstreamRejectReason', None) is PendingUpstream:
logShare(share)

Expand Down Expand Up @@ -940,7 +939,6 @@ def restoreState(SAVE_STATE_FILENAME):
server.getBlockTemplate = getBlockTemplate
server.receiveShare = receiveShare
server.RaiseRedFlags = RaiseRedFlags
server.BlockVersion = config.BlockVersion
server.ShareTarget = config.ShareTarget
server.checkAuthentication = checkAuthentication

Expand All @@ -954,7 +952,6 @@ def restoreState(SAVE_STATE_FILENAME):
stratumsrv.receiveShare = receiveShare
stratumsrv.RaiseRedFlags = RaiseRedFlags
stratumsrv.getTarget = getTarget
stratumsrv.BlockVersionHex = '%08x' % (config.BlockVersion,)
stratumsrv.defaultTarget = config.ShareTarget
stratumsrv.IsJobValid = IsJobValid
stratumsrv.checkAuthentication = checkAuthentication
Expand Down
7 changes: 6 additions & 1 deletion jsonrpc_getblocktemplate.py
Expand Up @@ -26,7 +26,6 @@ class _getblocktemplate:
def final_init(server):
ShareTargetHex = '%064x' % (server.ShareTarget,)
JSONRPCHandler.getblocktemplate_rv_template['target'] = ShareTargetHex
JSONRPCHandler.getblocktemplate_rv_template['version'] = server.BlockVersion

getblocktemplate_rv_template = {
'longpoll': '/LP',
Expand Down Expand Up @@ -87,6 +86,12 @@ def doJSON_getblocktemplate(self, params):
txno = {}
txno['data'] = b2a_hex(t.data).decode('ascii')
rv['coinbasetxn'] = txno
rv['version'] = merkleTree.MP['version']

rv['rules'] = merkleTree.MP['rules']
rv['vbavailable'] = merkleTree.MP['_filtered_vbavailable']
rv['vbrequired'] = rv['version'] & 0x1fffffff

return rv

def doJSON_submitblock(self, data, params = _NoParams):
Expand Down
76 changes: 67 additions & 9 deletions merklemaker.py
Expand Up @@ -27,6 +27,7 @@
from math import log
from merkletree import MerkleTree
import socket
import struct
from struct import pack
import threading
from time import sleep, time
Expand All @@ -35,8 +36,18 @@
_makeCoinbase = [0, 0]
_filecounter = 0

def MakeBlockHeader(MRD, BlockVersionBytes):
SupportedRules = ('csv',)

def SplitRuleFlag(ruleflag):
MandatoryRule = (ruleflag[0] == '!')
if MandatoryRule:
return (True, ruleflag[1:])
else:
return (False, ruleflag)

def MakeBlockHeader(MRD):
(merkleRoot, merkleTree, coinbase, prevBlock, bits) = MRD[:5]
BlockVersionBytes = merkleTree.MP['_BlockVersionBytes']
timestamp = pack('<L', int(time()))
hdr = BlockVersionBytes + prevBlock + merkleRoot + timestamp + bits + b'iolE'
return hdr
Expand All @@ -60,6 +71,7 @@ class merkleMaker(threading.Thread):
]
GBTReq = {
'capabilities': GBTCaps,
'rules': SupportedRules,
}
GMPReq = {
'capabilities': GBTCaps,
Expand Down Expand Up @@ -161,11 +173,20 @@ def URI2Access(uri):
self.lastMerkleUpdate = 0
self.nextMerkleUpdate = 0

def UpdateClearMerkleTree(self, MT, MP):
nMP = {}
for copy_mp in ('version', '_BlockVersionBytes', 'rules', '_filtered_vbavailable'):
nMP[copy_mp] = MP[copy_mp]
MT.MP = nMP

def createClearMerkleTree(self, height):
subsidy = self.SubsidyAlgo(height)
cbtxn = self.makeCoinbaseTxn(subsidy, False)
cbtxn.assemble()
return MerkleTree([cbtxn])
MT = MerkleTree([cbtxn])
if self.currentMerkleTree:
self.UpdateClearMerkleTree(MT, self.currentMerkleTree.MP)
return MT

def updateBlock(self, newBlock, height = None, bits = None, _HBH = None):
if newBlock == self.currentBlock[0]:
Expand Down Expand Up @@ -237,7 +258,9 @@ def updateBlock(self, newBlock, height = None, bits = None, _HBH = None):
self.readyCV.notify_all()

self.needMerkle = 2
self.onBlockChange()
# If we don't have MP yet, we need to wait until we do...
if hasattr(self.currentBlock, 'MP'):
self.onBlockChange()

def _trimBlock(self, MP, txnlist, txninfo, floodn, msgf):
fee = txninfo[-1].get('fee', None)
Expand Down Expand Up @@ -356,12 +379,35 @@ def _ProcessGBT(self, MP, TS = None):
oMP = MP
MP = deepcopy(MP)

if MP['version'] & 0xe0000000 != 0x20000000:
self.logger.error('Template from \'%s\' has non-BIP9 block version (%x)' % (TS['name'], MP['version']))
return None

ISupportAllRules = True
for ruleflag in MP['rules']:
(MandatoryRule, rule) = SplitRuleFlag(ruleflag)
if rule not in SupportedRules:
ISupportAllRules = False
if MandatoryRule:
self.logger.error('Template from \'%s\' strictly requires unsupported rule \'%s\'', TS['name'], rule)
return None
else:
self.logger.warning('Template from \'%s\' loosely requires unsupported rule \'%s\'', TS['name'], rule)

MP['_filtered_vbavailable'] = {}
for ruleflag in MP['vbavailable']:
rulebit = MP['vbavailable'][ruleflag]
rulemask = (1 << rulebit)
if MP['version'] & rulemask:
MP['_filtered_vbavailable'][ruleflag] = rulebit

prevBlock = bytes.fromhex(MP['previousblockhash'])[::-1]
if 'height' not in MP:
MP['height'] = TS['access'].getinfo()['blocks'] + 1
height = MP['height']
bits = bytes.fromhex(MP['bits'])[::-1]
(MP['_bits'], MP['_prevBlock']) = (bits, prevBlock)
MP['_BlockVersionBytes'] = struct.pack('<L', MP['version'])
if (prevBlock, height, bits) != self.currentBlock and (self.currentBlock[1] is None or height > self.currentBlock[1]):
self.updateBlock(prevBlock, height, bits, _HBH=(MP['previousblockhash'], MP['bits']))

Expand All @@ -379,6 +425,9 @@ def _ProcessGBT(self, MP, TS = None):
txnlist = [a for a in map(bytes.fromhex, txnlist)]

self._makeBlockSafe(MP, txnlist, txninfo)
if len(MP['transactions']) != len(txnlist) and not ISupportAllRules:
self.logger.error('Template from \'%s\' should be trimmed, but requires unsupported rule(s)', TS['name'])
return None

cbtxn = self.makeCoinbaseTxn(MP['coinbasevalue'], prevBlockHex = MP['previousblockhash'])
cbtxn.setCoinbase(b'\0\0')
Expand Down Expand Up @@ -423,7 +472,7 @@ def _CheckTemplate(self, newMerkleTree, TS):
cbtxn.assemble()
merkleRoot = newMerkleTree.merkleRoot()
MRD = (merkleRoot, newMerkleTree, coinbase, prevBlock, bits)
blkhdr = MakeBlockHeader(MRD, self.BlockVersionBytes)
blkhdr = MakeBlockHeader(MRD)
data = assembleBlock(blkhdr, txnlist)
ProposeReq = {
"mode": "proposal",
Expand Down Expand Up @@ -483,15 +532,13 @@ def _CheckTemplate(self, newMerkleTree, TS):
def _updateMerkleTree_fromTS(self, TS):
MP = self._CallGBT(TS)
newMerkleTree = self._ProcessGBT(MP, TS)
if newMerkleTree is None:
return None

# Some versions of bitcoinrpc ServiceProxy have problems copying/pickling, so just store name and URI for now
newMerkleTree.source = TS['name']
newMerkleTree.source_uri = TS['uri']

if MP['version'] < self.BlockVersion:
self.logger.error('Template from \'%s\' has too low block version (%u < %u)' % (TS['name'], MP['version'], self.BlockVersion))
return None

(AcceptedScore, TotalScore) = self._CheckTemplate(newMerkleTree, TS)
if TotalScore is None:
return (0, newMerkleTree)
Expand Down Expand Up @@ -551,6 +598,12 @@ def _updateMerkleTree_I(self):
if blkbasics != self.currentBlock:
self.updateBlock(*blkbasics, _HBH=(MP['previousblockhash'], MP['bits']))
self.currentMerkleTree = BestMT
FirstTemplate = not hasattr(self.curClearMerkleTree, 'MP')
self.UpdateClearMerkleTree(self.curClearMerkleTree, MP)
self.UpdateClearMerkleTree(self.nextMerkleTree, MP)
if FirstTemplate:
# This was skipped until we had MP info, so do it now
self.onBlockChange()

def _updateMerkleTree(self):
global now
Expand Down Expand Up @@ -749,6 +802,9 @@ class fakelogger:
def critical(self, *a):
if self.LO > 1: return
reallogger.critical(*a)
def error(self, *a):
if self.LO > 0.5: return
reallogger.error(*a)
def warning(self, *a):
if self.LO: return
reallogger.warning(*a)
Expand Down Expand Up @@ -816,7 +872,9 @@ def makeCoinbaseTxn(coinbaseValue, useCoinbaser = True, prevBlockHex = None):
'height': 219507,
'coinbasevalue': 3,
'previousblockhash': '000000000000012806bc100006dc83220bd9c2ac2709dc14a0d0fa1d6f9b733c',
'version': 1,
'version': 0x20000000,
'rules': (),
'vbavailable': {},
'bits': '1a05a6b1'
}
nMT = MM._ProcessGBT(gbt)
Expand Down
2 changes: 1 addition & 1 deletion stratumserver.py
Expand Up @@ -305,7 +305,7 @@ def updateJobOnly(self, wantClear = False, forceClean = False):
b2a_hex(txn.data[:pos - len(self.extranonce1null) - 4]).decode('ascii'),
b2a_hex(txn.data[pos:]).decode('ascii'),
steps,
self.BlockVersionHex,
'%08x' % (merkleTree.MP['version'],),
b2a_hex(bits[::-1]).decode('ascii'),
b2a_hex(struct.pack('>L', int(time()))).decode('ascii'),
forceClean or not self.IsJobValid(self.JobId)
Expand Down

0 comments on commit c96b0a9

Please sign in to comment.