Skip to content

Commit

Permalink
Merge old branch 'segwit' (early part) into setwit
Browse files Browse the repository at this point in the history
  • Loading branch information
luke-jr committed Nov 21, 2016
2 parents bab9235 + 5120ab0 commit 647f53b
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 17 deletions.
7 changes: 7 additions & 0 deletions bitcoin/script.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ def toAddress(cls, addr):
elif ver == 5 or ver == 196:
return b'\xa9\x14' + pubkeyhash + b'\x87'
raise ValueError('invalid address version')

@classmethod
def commitment(cls, commitment):
clen = len(commitment)
if clen > 0x4b:
raise NotImplementedError
return b'\x6a' + bytes((clen,)) + commitment

def countSigOps(s):
# FIXME: don't count data as ops
Expand Down
27 changes: 25 additions & 2 deletions bitcoin/txn.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,16 @@
_nullprev = b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'

class Txn:
def __init__(self, data=None):
def __init__(self, data=None, txid=None):
if data:
self.data = data
self.idhash()
if txid:
self.txid = txid
else:
try:
self.idhash()
except NotImplementedError:
pass

@classmethod
def new(cls):
Expand All @@ -49,6 +55,9 @@ def addOutput(self, amount, pkScript):
self.outputs.append( (amount, pkScript) )

def disassemble(self, retExtra = False):
if self.data[4:6] == b'\0\1':
raise NotImplementedError

self.version = unpack('<L', self.data[:4])[0]
rc = [4]

Expand Down Expand Up @@ -115,7 +124,21 @@ def assemble(self):
self.idhash()

def idhash(self):
if self.data[4:6] == b'\0\1':
if hasattr(self, 'txid'):
del self.txid
raise NotImplementedError
self.txid = dblsha(self.data)
if hasattr(self, 'witness_hash'):
del self.witness_hash

def withash(self):
self.witness_hash = dblsha(self.data)

def get_witness_hash(self):
if not hasattr(self, 'witness_hash'):
self.withash()
return self.witness_hash

# Txn tests
def _test():
Expand Down
9 changes: 8 additions & 1 deletion eloipool.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,10 @@ def RaiseRedFlags(reason):
import subprocess
from time import time

def makeCoinbaseTxn(coinbaseValue, useCoinbaser = True, prevBlockHex = None):
def makeCoinbaseTxn(coinbaseValue, useCoinbaser = True, prevBlockHex = None, witness_commitment = NotImplemented):
if witness_commitment is NotImplemented:
raise NotImplementedError

txn = Txn.new()

if useCoinbaser and hasattr(config, 'CoinbaserCmd') and config.CoinbaserCmd:
Expand Down Expand Up @@ -131,6 +134,10 @@ def makeCoinbaseTxn(coinbaseValue, useCoinbaser = True, prevBlockHex = None):
pkScript = BitcoinScript.toAddress(config.TrackerAddr)
txn.addOutput(coinbaseValue, pkScript)

# SegWit commitment
if not witness_commitment is None:
txn.addOutput(0, BitcoinScript.commitment(WitnessMagic + witness_commitment))

# TODO
# TODO: red flag on dupe coinbase
return txn
Expand Down
52 changes: 38 additions & 14 deletions merklemaker.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@ def SplitRuleFlag(ruleflag):
else:
return (False, ruleflag)

def CalculateWitnessCommitment(txnobjs, nonce):
gentx_withash = b'\0' * 0x20
withashes = (gentx_withash,) + tuple(a.get_witness_hash() for a in txnobjs[1:])
txids = (gentx_withash,) + tuple(a.txid for a in txnobjs[1:])
if withashes == txids:
# Unnecessary
return None

wmr = MerkleTree(data=withashes).merkleRoot()
commitment = dblsha(wmr + nonce)
return commitment

def MakeBlockHeader(MRD):
(merkleRoot, merkleTree, coinbase, prevBlock, bits) = MRD[:5]
BlockVersionBytes = merkleTree.MP['_BlockVersionBytes']
Expand Down Expand Up @@ -94,6 +106,7 @@ def __init__(self, *a, **k):
self.currentBlock = (None, None, None)
self.lastBlock = (None, None, None)
self.SubsidyAlgo = lambda height: 5000000000 >> (height // 210000)
self.WitnessNonce = b'\0' * 0x20

def _prepare(self):
self.UseTemplateChecks = True
Expand Down Expand Up @@ -181,11 +194,13 @@ def UpdateClearMerkleTree(self, MT, MP):

def createClearMerkleTree(self, height):
subsidy = self.SubsidyAlgo(height)
cbtxn = self.makeCoinbaseTxn(subsidy, False)
cbtxn = self.makeCoinbaseTxn(subsidy, False, witness_commitment=None)
cbtxn.setCoinbase(b'\0\0') # necessary to avoid triggering segwit marker+flags
cbtxn.assemble()
MT = MerkleTree([cbtxn])
if self.currentMerkleTree:
self.UpdateClearMerkleTree(MT, self.currentMerkleTree.MP)
MT.witness_commitment = None
return MT

def updateBlock(self, newBlock, height = None, bits = None, _HBH = None):
Expand Down Expand Up @@ -295,15 +310,17 @@ def _APOT(self, txninfopot, MP, POTInfo):
return True

def _makeBlockSafe(self, MP, txnlist, txninfo):
sizelimit = MP.get('sizelimit', 1000000) - 0x10000 # 64 KB breathing room
blocksize = sum(map(len, txnlist)) + 80
while blocksize > 934464: # 1 "MB" limit - 64 KB breathing room
while blocksize > sizelimit:
txnsize = len(txnlist[-1])
self._trimBlock(MP, txnlist, txninfo, 'SizeLimit', lambda x: 'Making blocks over 1 MB size limit (%d bytes; %s)' % (blocksize, x))
blocksize -= txnsize

# NOTE: This check doesn't work at all without BIP22 transaction obj format
sigoplimit = MP.get('sigoplimit', 20000) - 0x200 # 512 sigop breathing room
blocksigops = sum(a.get('sigops', 0) for a in txninfo)
while blocksigops > 19488: # 20k limit - 0x200 breathing room
while blocksigops > sigoplimit:
txnsigops = txninfo[-1]['sigops']
self._trimBlock(MP, txnlist, txninfo, 'SigOpLimit', lambda x: 'Making blocks over 20k SigOp limit (%d; %s)' % (blocksigops, x))
blocksigops -= txnsigops
Expand Down Expand Up @@ -429,20 +446,27 @@ def _ProcessGBT(self, MP, TS = None):
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'])
txnobjs = [None]
for i in range(len(txnlist)):
iinfo = txninfo[i]
ka = {}
if 'txid' in iinfo:
ka['txid'] = iinfo['txid']
txnobjs.append(Txn(data=txnlist[i], **ka))

witness_commitment = CalculateWitnessCommitment(txnobjs, self.WitnessNonce)

cbtxn = self.makeCoinbaseTxn(MP['coinbasevalue'], prevBlockHex = MP['previousblockhash'], witness_commitment=witness_commitment)
cbtxn.setCoinbase(b'\0\0')
cbtxn.assemble()
txnlist.insert(0, cbtxn.data)
txninfo.insert(0, {
})

txnlist = [a for a in map(Txn, txnlist[1:])]
txnlist.insert(0, cbtxn)
txnlist = list(txnlist)
newMerkleTree = MerkleTree(txnlist)
txnobjs[0] = cbtxn

txnobjs = list(txnobjs)
newMerkleTree = MerkleTree(txnobjs)
newMerkleTree.POTInfo = MP.get('POTInfo')
newMerkleTree.MP = MP
newMerkleTree.oMP = oMP
newMerkleTree.witness_commitment = witness_commitment

return newMerkleTree

Expand Down Expand Up @@ -856,9 +880,9 @@ def MBS(LO = 0):
txninfo[2]['fee'] = 0
assert MBS(1) == (MP, txnlist, txninfo)
# _ProcessGBT tests
def makeCoinbaseTxn(coinbaseValue, useCoinbaser = True, prevBlockHex = None):
def makeCoinbaseTxn(coinbaseValue, useCoinbaser = True, prevBlockHex = None, witness_commitment=None):
txn = Txn.new()
txn.addOutput(coinbaseValue, b'')
txn.addOutput(coinbaseValue, BitcoinScript.commitment(witness_commitment) if witness_commitment else b'')
return txn
MM.makeCoinbaseTxn = makeCoinbaseTxn
MM.updateBlock = lambda *a, **ka: None
Expand Down

0 comments on commit 647f53b

Please sign in to comment.