From 3c21411c67a2c23932ed8d8f12c25db0c621f2e7 Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Mon, 11 Sep 2017 07:51:58 +0200 Subject: [PATCH] work towards bringing mininode.py up to date with the main node and towards re-enabling rpc tests --- qa/rpc-tests/listtransactions.py | 2 +- qa/rpc-tests/p2p-compactblocks.py | 10 +- qa/rpc-tests/test_framework/blocktools.py | 45 ++-- qa/rpc-tests/test_framework/mininode.py | 275 ++++++++++++++-------- src/primitives/transaction.h | 2 +- 5 files changed, 205 insertions(+), 129 deletions(-) diff --git a/qa/rpc-tests/listtransactions.py b/qa/rpc-tests/listtransactions.py index 0b34c0c48d..c12eed4119 100755 --- a/qa/rpc-tests/listtransactions.py +++ b/qa/rpc-tests/listtransactions.py @@ -7,7 +7,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * -from test_framework.mininode import CTransaction, setCTxOutValue, CTxOutValue, COIN +from test_framework.mininode import CTransaction, COIN from io import BytesIO import binascii diff --git a/qa/rpc-tests/p2p-compactblocks.py b/qa/rpc-tests/p2p-compactblocks.py index 9151ecf5de..21ba595763 100755 --- a/qa/rpc-tests/p2p-compactblocks.py +++ b/qa/rpc-tests/p2p-compactblocks.py @@ -131,7 +131,7 @@ class CompactBlocksTest(BitcoinTestFramework): height = node.getblockcount() tip = node.getbestblockhash() mtp = node.getblockheader(tip)['mediantime'] - block = create_block(int(tip, 16), create_coinbase(height + 1), mtp + 1) + block = create_block(int(tip, 16), create_coinbase(height + 1), mtp + 1, height + 1) block.nVersion = 4 if segwit: add_witness_commitment(block) @@ -146,12 +146,12 @@ class CompactBlocksTest(BitcoinTestFramework): assert(int(self.nodes[0].getbestblockhash(), 16) == block.sha256) self.nodes[0].generate(100) - total_value = block.vtx[0].vout[0].nValue - out_value = total_value // 10 + total_value = block.vtx[0].vout[0].nValue.getAmount() + out_value = total_value tx = CTransaction() tx.vin.append(CTxIn(COutPoint(block.vtx[0].sha256, 0), b'')) for i in range(10): - tx.vout.append(CTxOut(out_value, CScript([OP_TRUE]))) + tx.vout.append(CTxOut(CTxOutValue(out_value), CScript([OP_TRUE]))) tx.rehash() block2 = self.build_block_on_tip(self.nodes[0]) @@ -458,7 +458,7 @@ class CompactBlocksTest(BitcoinTestFramework): for i in range(num_transactions): tx = CTransaction() tx.vin.append(CTxIn(COutPoint(utxo[0], utxo[1]), b'')) - tx.vout.append(CTxOut(utxo[2] - 1000, CScript([OP_TRUE]))) + tx.vout.append(CTxOut(CTxOutValue(utxo[2].getAmount() - 1000), CScript([OP_TRUE]))) tx.rehash() utxo = [tx.sha256, 0, tx.vout[0].nValue] block.vtx.append(tx) diff --git a/qa/rpc-tests/test_framework/blocktools.py b/qa/rpc-tests/test_framework/blocktools.py index f69958823c..2214022fb7 100644 --- a/qa/rpc-tests/test_framework/blocktools.py +++ b/qa/rpc-tests/test_framework/blocktools.py @@ -8,7 +8,7 @@ from .mininode import * from .script import CScript, OP_TRUE, OP_CHECKSIG, OP_RETURN # Create a block (with regtest difficulty) -def create_block(hashprev, coinbase, nTime=None): +def create_block(hashprev, coinbase, nTime=None, height=0): block = CBlock() if nTime is None: import time @@ -16,9 +16,11 @@ def create_block(hashprev, coinbase, nTime=None): else: block.nTime = nTime block.hashPrevBlock = hashprev - block.nBits = 0x207fffff # Will break after a difficulty adjustment... block.vtx.append(coinbase) block.hashMerkleRoot = block.calc_merkle_root() + block.nHeight = height + block.proof.challenge = CScript([OP_TRUE]) + block.proof.solution = b'' block.calc_sha256() return block @@ -39,39 +41,28 @@ def add_witness_commitment(block, nonce=0): # witness commitment is the last OP_RETURN output in coinbase output_data = WITNESS_COMMITMENT_HEADER + ser_uint256(witness_commitment) - block.vtx[0].vout.append(CTxOut(0, CScript([OP_RETURN, output_data]))) + block.vtx[0].vout.append(CTxOut(CTxOutValue(0), CScript([OP_RETURN, output_data]))) block.vtx[0].rehash() block.hashMerkleRoot = block.calc_merkle_root() block.rehash() - -def serialize_script_num(value): - r = bytearray(0) - if value == 0: - return r - neg = value < 0 - absvalue = -value if neg else value - while (absvalue): - r.append(int(absvalue & 0xff)) - absvalue >>= 8 - if r[-1] & 0x80: - r.append(0x80 if neg else 0) - elif neg: - r[-1] |= 0x80 - return r - # Create a coinbase transaction, assuming no miner fees. # If pubkey is passed in, the coinbase output will be a P2PK output; # otherwise an anyone-can-spend output. -def create_coinbase(height, pubkey = None): +def create_coinbase(height, pubkey = None, amount = 0): coinbase = CTransaction() - coinbase.vin.append(CTxIn(COutPoint(0, 0xffffffff), - ser_string(serialize_script_num(height)), 0xffffffff)) + # coinbase transaction scriptsigs must be at least 2 bytes + coinbase.vin.append(CTxIn(COutPoint(0, 0xffffffff), + CScript([height, OP_TRUE]), 0xffffffff)) coinbaseoutput = CTxOut() - coinbaseoutput.nValue = 50 * COIN - halvings = int(height/150) # regtest - coinbaseoutput.nValue >>= halvings - if (pubkey != None): + + coinbaseoutput.nValue.setToAmount(amount) + coinbaseoutput.nAsset.setToAsset(b'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa') + + if amount == 0: + # zero utxo's must be made unspendable + coinbaseoutput.scriptPubKey = CScript([OP_RETURN]) + elif pubkey != None: coinbaseoutput.scriptPubKey = CScript([pubkey, OP_CHECKSIG]) else: coinbaseoutput.scriptPubKey = CScript([OP_TRUE]) @@ -85,7 +76,7 @@ def create_transaction(prevtx, n, sig, value, scriptPubKey=CScript()): tx = CTransaction() assert(n < len(prevtx.vout)) tx.vin.append(CTxIn(COutPoint(prevtx.sha256, n), sig, 0xffffffff)) - tx.vout.append(CTxOut(value, scriptPubKey)) + tx.vout.append(CTxOut(CTxOutValue(value), scriptPubKey)) tx.calc_sha256() return tx diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index e3207f1862..280d18f596 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -359,57 +359,119 @@ class CTxIn(object): % (repr(self.prevout), bytes_to_hex_str(self.scriptSig), self.nSequence) -def setCTxOutValue(amount): - vchCommitment = [0]*33 - for i in range(8): #8 bytes - vchCommitment[33-1-i] = ((amount >> (i*8)) & 0xff) - return ''.join(map(chr, vchCommitment)) - -#Serialization only does value commitment -class CTxOutValue(object): - def __init__(self, vchCommitment=[], vchRangeProof="", vchNonceCommitment=""): +class CTxOutAsset(object): + def __init__(self, vchCommitment=b"\x00"): self.vchCommitment = vchCommitment - self.vchRangeProof = vchRangeProof - self.vchNonceCommitment = vchNonceCommitment def deserialize(self, f): - self.vchCommitment = f.read(33) - #self.vchRangeProof = deser_string(f) - #self.vchNonceCommitment = deser_string(f) + version = ord(f.read(1)) + if version == 0: self.vchCommitment = b'\x00' + elif version == 1: self.vchCommitment = b'\x01' + f.read(32) + elif version == 0xff: self.vchCommitment = b'\xff' + f.read(32) + elif version == 10 or version == 11: self.vchCommitment = bytes([version]) + f.read(32) + else: raise 'invalid CTxOutAsset in deserialize' + + def serialize(self): + r = b"" + r += self.vchCommitment + return r + + def setToAsset(self, val): + if len(val) != 32: + raise 'invalid asset hash (expected 32 bytes got %d)' % len(val) + self.vchCommitment = b'\x01' + val + + def __repr__(self): + return "CTxOutAsset(vchCommitment=%s)" % self.vchCommitment + +class CTxOutValue(object): + + def __init__(self, value=None): + self.setNull() + if value != None: self.setToAmount(value) + + def setNull(self): + self.vchCommitment = b'\x00' + + def deserialize(self, f): + version = ord(f.read(1)) + if version == 0: self.vchCommitment = b'\x00' + elif version == 1: self.vchCommitment = b'\x01' + f.read(8) + elif version == 0xff: self.vchCommitment = b'\xff' + f.read(8) + elif version == 8 or version == 9: self.vchCommitment = bytes([version]) + f.read(32) + else: raise Exception('invalid CTxOutValue in deserialize. version %d' % version) + + def serialize(self): + r = b"" + if len(self.vchCommitment) < 1: raise ValueError('invalid commitment') + r += self.vchCommitment + return r + + def setToAmount(self, amount): + commit = [1]*9 + for i in range(8): #8 bytes + commit[8-i] = ((amount >> (i*8)) & 0xff) + self.vchCommitment = bytes(commit) + + def getAmount(self): + if self.vchCommitment[0] != 1: + raise ValueError('getAmount() called on non-explicit CTxOutValue') + ret = 0 + for i in range(8): #8 bytes + ret <<= 8 + ret |= self.vchCommitment[i+1] + return ret + + def __repr__(self): + return "CTxOutValue(vchCommitment=%s)" % self.vchCommitment + +class CTxOutNonce(object): + def __init__(self, vchCommitment=b"\x00"): + self.vchCommitment = vchCommitment + + def deserialize(self, f): + version = ord(f.read(1)) + if version == 0: self.vchCommitment = b'\x00' + elif version == 1: self.vchCommitment = b'\x01' + f.read(32) + elif version == 0xff: self.vchCommitment = b'\xff' + f.read(32) + elif version == 2 or version == 3: self.vchCommitment = bytes([version]) + f.read(32) + else: raise ValueError('invalid CTxOutNonce in deserialize') def serialize(self): r = b"" r += self.vchCommitment - #r += ''.join(map(chr, self.vchCommitment)) - #r += ser_string(self.vchRangeProof) - #r += ser_string(self.vchNonceCommitment) return r def __repr__(self): - return "CTxOutValue(vchCommitment=%s vchRangeProof=%s, vchnonceCommitment=%s)" \ - % ("COMMITMENT", "RANGE", "NONCE")#(self.vchCommitment, self.vchRangeProof, - #self.vchNonceCommitment) + return "CTxOutNonce(vchCommitment=%s)" % self.vchCommitment class CTxOut(object): - def __init__(self, nValue=CTxOutValue(), scriptPubKey=""): + def __init__(self, nValue=CTxOutValue(), scriptPubKey=b'', nAsset=CTxOutAsset(), nNonce=CTxOutNonce()): + self.nAsset = nAsset self.nValue = nValue + self.nNonce = nNonce self.scriptPubKey = scriptPubKey def deserialize(self, f): + self.nAsset = CTxOutAsset() + self.nAsset.deserialize(f) self.nValue = CTxOutValue() self.nValue.deserialize(f) + self.nNonce = CTxOutNonce() + self.nNonce.deserialize(f) self.scriptPubKey = deser_string(f) def serialize(self): r = b"" + r += self.nAsset.serialize() r += self.nValue.serialize() + r += self.nNonce.serialize() r += ser_string(self.scriptPubKey) return r def __repr__(self): - return "CTxOut(nValue=%s scriptPubKey=%s)" \ - % (self.nValue // COIN, self.nValue % COIN, - bytes_to_hex_str(self.scriptPubKey)) + return "CTxOut(nAsset=%s nValue=%s nNonce=%s scriptPubKey=%s)" \ + % (self.nAsset, self.nValue, self.nNonce, bytes_to_hex_str(self.scriptPubKey)) class CScriptWitness(object): @@ -429,28 +491,63 @@ class CScriptWitness(object): class CTxInWitness(object): def __init__(self): + self.vchIssuanceAmountRangeproof = b''; + self.vchInflationKeysRangeproof = b''; self.scriptWitness = CScriptWitness() def deserialize(self, f): + self.vchIssuanceAmountRangeproof = deser_string(f) + self.vchInflationKeysRangeproof = deser_string(f) self.scriptWitness.stack = deser_string_vector(f) def serialize(self): - return ser_string_vector(self.scriptWitness.stack) + r = b'' + r += ser_string(self.vchIssuanceAmountRangeproof) + r += ser_string(self.vchInflationKeysRangeproof) + r += ser_string_vector(self.scriptWitness.stack) + return r def __repr__(self): - return repr(self.scriptWitness) + return "CTxInWitness (%s, %s, %s)" % (self.vchIssuanceAmountRangeproof, + self.vchInflationKeysRangeproof, self.scriptWitness) def is_null(self): - return self.scriptWitness.is_null() + return len(self.vchIssuanceAmountRangeproof) == 0 \ + and len(self.vchInflationKeysRangeproof) == 0 \ + and self.scriptWitness.is_null() +class CTxOutWitness(object): + def __init__(self): + self.vchSurjectionproof = b''; + self.vchRangeproof = b''; + + def deserialize(self, f): + self.vchSurjectionproof = deser_string(f) + self.vchRangeproof = deser_string(f) + + def serialize(self): + r = b'' + r += ser_string(self.vchSurjectionproof) + r += ser_string(self.vchRangeproof) + return r + + def __repr__(self): + return "CTxOutWitness (%s, %s)" % (self.vchSurjectionproof, self.vchRangeproof) + + def is_null(self): + return len(self.vchSurjectionproof) == 0 \ + and len(self.vchRangeproof) == 0 class CTxWitness(object): def __init__(self): self.vtxinwit = [] + self.vtxoutwit = [] def deserialize(self, f): for i in range(len(self.vtxinwit)): self.vtxinwit[i].deserialize(f) + for i in range(len(self.vtxoutwit)): + self.vtxoutwit[i].deserialize(f) def serialize(self): r = b"" @@ -462,13 +559,17 @@ class CTxWitness(object): return r def __repr__(self): - return "CTxWitness(%s)" % \ - (';'.join([repr(x) for x in self.vtxinwit])) + return "CTxWitness([%s], [%s])" % \ + (';'.join([repr(x) for x in self.vtxinwit]), + ';'.join([repr(x) for x in self.vtxoutwit])) def is_null(self): for x in self.vtxinwit: if not x.is_null(): return False + for x in self.vtxoutwit: + if not x.is_null(): + return False return True @@ -492,14 +593,6 @@ class CTransaction(object): self.wit = copy.deepcopy(tx.wit) def deserialize(self, f): - self.nVersion = struct.unpack(" 0: self.wit.vtxinwit = [CTxInWitness() for i in range(len(self.vin))] + self.wit.vtxoutwit = [CTxOutWitness() for i in range(len(self.vout))] self.wit.deserialize(f) - if flags & 2 > 0: - for i in range(len(self.vout)): - self.vout[i].nValue.vchRangeProof = deser_string(f) - self.vout[i].nValue.vchNonceCommitment = deser_string(f) - if flags > 3: + if flags > 1: raise TypeError('Extra witness flags:' + str(flags)) self.nLockTime = struct.unpack(" 0: - for i in range(len(self.vout)): - r += ser_string(self.vout[i].nValue.vchRangeProof) - r += ser_string(self.vout[i].nValue.vchNonceCommitment) - r += struct.pack(" target: - return False +# TODO: check signatures +# target = uint256_from_compact(self.nBits) +# if self.sha256 > target: +# return False for tx in self.vtx: if not tx.is_valid(): return False @@ -753,15 +838,15 @@ class CBlock(CBlockHeader): def solve(self): self.rehash() - target = uint256_from_compact(self.nBits) - while self.sha256 > target: - self.nNonce += 1 - self.rehash() +# target = uint256_from_compact(self.nBits) +# while self.sha256 > target: +# self.nNonce += 1 +# self.rehash() def __repr__(self): - return "CBlock(nVersion=%i hashPrevBlock=%064x hashMerkleRoot=%064x nTime=%s nBits=%08x nNonce=%08x vtx=%s)" \ + return "CBlock(nVersion=%i hashPrevBlock=%064x hashMerkleRoot=%064x nTime=%s vtx=%s)" \ % (self.nVersion, self.hashPrevBlock, self.hashMerkleRoot, - time.ctime(self.nTime), self.nBits, self.nNonce, repr(self.vtx)) + time.ctime(self.nTime), repr(self.vtx)) class CUnsignedAlert(object): diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 89f30dcef3..bf0b4f8f8a 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -134,7 +134,7 @@ public: }; /** - * An 33-byte data field that typically is used to convey to the + * A 33-byte data field that typically is used to convey to the * recipient the ECDH ephemeral key (an EC point) for deriving the * transaction output blinding factor. */ class CConfidentialNonce : public CConfidentialCommitment<33, 2, 3>