def SegwitVersion1SignatureHash_legacy(script, txTo, inIdx, hashtype, amount): """ This method is identical to the regular `SegwitVersion1SignatureHash` method, but without support for SIGHASH_RANGEPROOF. So basically it's the old version of the method from before the new sighash flag was added. """ hashPrevouts = 0 hashSequence = 0 hashIssuance = 0 hashOutputs = 0 if not (hashtype & SIGHASH_ANYONECANPAY): serialize_prevouts = bytes() for i in txTo.vin: serialize_prevouts += i.prevout.serialize() hashPrevouts = uint256_from_str(hash256(serialize_prevouts)) if (not (hashtype & SIGHASH_ANYONECANPAY) and (hashtype & 0x1f) != SIGHASH_SINGLE and (hashtype & 0x1f) != SIGHASH_NONE): serialize_sequence = bytes() for i in txTo.vin: serialize_sequence += struct.pack("<I", i.nSequence) hashSequence = uint256_from_str(hash256(serialize_sequence)) if not (hashtype & SIGHASH_ANYONECANPAY): serialize_issuance = bytes() # TODO actually serialize issuances for _ in txTo.vin: serialize_issuance += b'\x00' hashIssuance = uint256_from_str(hash256(serialize_issuance)) if ((hashtype & 0x1f) != SIGHASH_SINGLE and (hashtype & 0x1f) != SIGHASH_NONE): serialize_outputs = bytes() for o in txTo.vout: serialize_outputs += o.serialize() hashOutputs = uint256_from_str(hash256(serialize_outputs)) elif ((hashtype & 0x1f) == SIGHASH_SINGLE and inIdx < len(txTo.vout)): serialize_outputs = txTo.vout[inIdx].serialize() hashOutputs = uint256_from_str(hash256(serialize_outputs)) ss = bytes() ss += struct.pack("<i", txTo.nVersion) ss += ser_uint256(hashPrevouts) ss += ser_uint256(hashSequence) ss += ser_uint256(hashIssuance) ss += txTo.vin[inIdx].prevout.serialize() ss += ser_string(script) ss += amount.serialize() ss += struct.pack("<I", txTo.vin[inIdx].nSequence) ss += ser_uint256(hashOutputs) ss += struct.pack("<i", txTo.nLockTime) ss += struct.pack("<I", hashtype) return hash256(ss)
def serializeblock_err(self, block): r = b"" r += struct.pack("<i", block.nVersion) r += ser_uint256(block.hashPrevBlock) r += ser_uint256(block.hashMerkleRoot) r += ser_uint256(block.hashImMerkleRoot) r += struct.pack("<I", block.nTime) r += struct.pack("B", block.xfieldType) r += block.xfield r += ser_string(block.proof) r += ser_vector(block.vtx) return r
def createblockproof(self, block, signblockprivkey): # create block proof with xField for all xfieldTypes r = b"" r += struct.pack("<i", block.nVersion) r += ser_uint256(block.hashPrevBlock) r += ser_uint256(block.hashMerkleRoot) r += ser_uint256(block.hashImMerkleRoot) r += struct.pack("<I", block.nTime) r += struct.pack("B", block.xfieldType) r += ser_string(block.xfield) sighash = hash256(r) block.proof = bytearray(self.signKey.sign(sighash)) block.rehash()
def serialize_addrman( *, format=1, lowest_compatible=4, net_magic="regtest", bucket_key=1, len_new=None, len_tried=None, mock_checksum=None, ): new = [] tried = [] INCOMPATIBILITY_BASE = 32 r = MAGIC_BYTES[net_magic] r += struct.pack("B", format) r += struct.pack("B", INCOMPATIBILITY_BASE + lowest_compatible) r += ser_uint256(bucket_key) r += struct.pack("i", len_new or len(new)) r += struct.pack("i", len_tried or len(tried)) ADDRMAN_NEW_BUCKET_COUNT = 1 << 10 r += struct.pack("i", ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30)) for _ in range(ADDRMAN_NEW_BUCKET_COUNT): r += struct.pack("i", 0) checksum = hash256(r) r += mock_checksum or checksum return r
def test_getmnlistdiff(self, baseBlockHash, blockHash, baseMNList, expectedDeleted, expectedUpdated): d = self.test_getmnlistdiff_base(baseBlockHash, blockHash) # Assert that the deletedMNs and mnList fields are what we expected assert_equal(set(d.deletedMNs), set([int(e, 16) for e in expectedDeleted])) assert_equal(set([e.proRegTxHash for e in d.mnList]), set(int(e, 16) for e in expectedUpdated)) # Build a new list based on the old list and the info from the diff newMNList = baseMNList.copy() for e in d.deletedMNs: newMNList.pop(format(e, '064x')) for e in d.mnList: newMNList[format(e.proRegTxHash, '064x')] = e # Verify that the merkle root matches what we locally calculate hashes = [] for mn in sorted(newMNList.values(), key=lambda mn: ser_uint256(mn.proRegTxHash)): hashes.append(hash256(mn.serialize())) merkleRoot = CBlock.get_merkle_root(hashes) assert_equal(merkleRoot, d.merkleRootMNList) return newMNList
def assert_tx_format_also_signed(self, utxo, segwit): raw = self.nodes[0].createrawtransaction([{ "txid": utxo["txid"], "vout": utxo["vout"] }], [{ self.unknown_addr: "49.9" }, { "fee": "0.1" }]) unsigned_decoded = self.nodes[0].decoderawtransaction(raw) assert_equal(len(unsigned_decoded["vin"]), 1) assert ('txinwitness' not in unsigned_decoded["vin"][0]) # Cross-check python serialization tx = CTransaction() tx.deserialize(BytesIO(hex_str_to_bytes(raw))) assert_equal(tx.vin[0].prevout.hash, int("0x" + utxo["txid"], 0)) assert_equal(len(tx.vin), len(unsigned_decoded["vin"])) assert_equal(len(tx.vout), len(unsigned_decoded["vout"])) # assert re-encoding serialized = bytes_to_hex_str(tx.serialize()) assert_equal(serialized, raw) # Now sign and repeat tests signed_raw = self.nodes[0].signrawtransactionwithwallet(raw)["hex"] signed_decoded = self.nodes[0].decoderawtransaction(signed_raw) assert_equal(len(signed_decoded["vin"]), 1) assert (("txinwitness" in signed_decoded["vin"][0]) == segwit) # Cross-check python serialization tx = CTransaction() tx.deserialize(BytesIO(hex_str_to_bytes(signed_raw))) assert_equal(tx.vin[0].prevout.hash, int("0x" + utxo["txid"], 0)) assert_equal(bytes_to_hex_str(tx.vin[0].scriptSig), signed_decoded["vin"][0]["scriptSig"]["hex"]) # test witness if segwit: wit_decoded = signed_decoded["vin"][0]["txinwitness"] for i in range(len(wit_decoded)): assert_equal( bytes_to_hex_str( tx.wit.vtxinwit[0].scriptWitness.stack[i]), wit_decoded[i]) # assert re-encoding serialized = bytes_to_hex_str(tx.serialize()) assert_equal(serialized, signed_raw) txid = self.nodes[0].sendrawtransaction(serialized) nodetx = self.nodes[0].getrawtransaction(txid, 1) assert_equal(nodetx["txid"], tx.rehash()) # cross-check wtxid report from node wtxid = bytes_to_hex_str(ser_uint256(tx.calc_sha256(True))[::-1]) assert_equal(nodetx["wtxid"], wtxid) assert_equal(nodetx["hash"], wtxid) # witness hash stuff assert_equal(nodetx["withash"], tx.calc_witness_hash()) return (txid, wtxid)
def to_jsonable(obj: Any) -> Any: if hasattr(obj, "__dict__"): return obj.__dict__ elif hasattr(obj, "__slots__"): ret = {} # type: Any for slot in obj.__slots__: val = getattr(obj, slot, None) if slot in HASH_INTS and isinstance(val, int): ret[slot] = ser_uint256(val).hex() elif slot in HASH_INT_VECTORS and isinstance(val[0], int): ret[slot] = [ser_uint256(a).hex() for a in val] else: ret[slot] = to_jsonable(val) return ret elif isinstance(obj, list): return [to_jsonable(a) for a in obj] elif isinstance(obj, bytes): return obj.hex() else: return obj
def test_compactblock_reconstruction_multiple_peers( self, stalling_peer, delivery_peer): node = self.nodes[0] assert len(self.utxos) def announce_cmpct_block(node, peer): utxo = self.utxos.pop(0) block = self.build_block_with_transactions(node, utxo, 5) cmpct_block = HeaderAndShortIDs() cmpct_block.initialize_from_block(block) msg = msg_cmpctblock(cmpct_block.to_p2p()) peer.send_and_ping(msg) with mininode_lock: assert "getblocktxn" in peer.last_message return block, cmpct_block block, cmpct_block = announce_cmpct_block(node, stalling_peer) for tx in block.vtx[1:]: delivery_peer.send_message(msg_tx(tx)) delivery_peer.sync_with_ping() mempool = node.getrawmempool() for tx in block.vtx[1:]: assert tx.hash in mempool delivery_peer.send_and_ping(msg_cmpctblock(cmpct_block.to_p2p())) assert_equal(int(node.getbestblockhash(), 16), block.sha256) self.utxos.append( [block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue]) # Now test that delivering an invalid compact block won't break relay block, cmpct_block = announce_cmpct_block(node, stalling_peer) for tx in block.vtx[1:]: delivery_peer.send_message(msg_tx(tx)) delivery_peer.sync_with_ping() cmpct_block.prefilled_txn[0].tx.wit.vtxinwit = [CTxInWitness()] cmpct_block.prefilled_txn[0].tx.wit.vtxinwit[0].scriptWitness.stack = [ ser_uint256(0) ] cmpct_block.use_witness = True delivery_peer.send_and_ping(msg_cmpctblock(cmpct_block.to_p2p())) assert int(node.getbestblockhash(), 16) != block.sha256 msg = msg_no_witness_blocktxn() msg.block_transactions.blockhash = block.sha256 msg.block_transactions.transactions = block.vtx[1:] stalling_peer.send_and_ping(msg) assert_equal(int(node.getbestblockhash(), 16), block.sha256)
def get_utxo(self, fund_tx, idx): spent = None # Coin selection for utxo in self.nodes[0].listunspent(): if utxo["txid"] == ser_uint256( fund_tx.vin[idx].prevout.hash)[::-1].hex( ) and utxo["vout"] == fund_tx.vin[idx].prevout.n: spent = utxo assert (spent is not None) assert (len(fund_tx.vin) == 2) return spent
def assert_tx_format_also_signed(self, utxo, segwit): raw = self.nodes[0].createrawtransaction( [{"txid": utxo["txid"], "vout": utxo["vout"]}], [{self.unknown_addr: "49.9"}, {"fee": "0.1"}] ) unsigned_decoded = self.nodes[0].decoderawtransaction(raw) assert_equal(len(unsigned_decoded["vin"]), 1) assert('txinwitness' not in unsigned_decoded["vin"][0]) # Cross-check python serialization tx = CTransaction() tx.deserialize(BytesIO(hex_str_to_bytes(raw))) assert_equal(tx.vin[0].prevout.hash, int("0x"+utxo["txid"], 0)) assert_equal(len(tx.vin), len(unsigned_decoded["vin"])) assert_equal(len(tx.vout), len(unsigned_decoded["vout"])) # assert re-encoding serialized = bytes_to_hex_str(tx.serialize()) assert_equal(serialized, raw) # Now sign and repeat tests signed_raw = self.nodes[0].signrawtransactionwithwallet(raw)["hex"] signed_decoded = self.nodes[0].decoderawtransaction(signed_raw) assert_equal(len(signed_decoded["vin"]), 1) assert(("txinwitness" in signed_decoded["vin"][0]) == segwit) # Cross-check python serialization tx = CTransaction() tx.deserialize(BytesIO(hex_str_to_bytes(signed_raw))) assert_equal(tx.vin[0].prevout.hash, int("0x"+utxo["txid"], 0)) assert_equal(bytes_to_hex_str(tx.vin[0].scriptSig), signed_decoded["vin"][0]["scriptSig"]["hex"]) # test witness if segwit: wit_decoded = signed_decoded["vin"][0]["txinwitness"] for i in range(len(wit_decoded)): assert_equal(bytes_to_hex_str(tx.wit.vtxinwit[0].scriptWitness.stack[i]), wit_decoded[i]) # assert re-encoding serialized = bytes_to_hex_str(tx.serialize()) assert_equal(serialized, signed_raw) txid = self.nodes[0].sendrawtransaction(serialized) nodetx = self.nodes[0].getrawtransaction(txid, 1) assert_equal(nodetx["txid"], tx.rehash()) # cross-check wtxid report from node wtxid = bytes_to_hex_str(ser_uint256(tx.calc_sha256(True))[::-1]) assert_equal(nodetx["wtxid"], wtxid) assert_equal(nodetx["hash"], wtxid) # witness hash stuff assert_equal(nodetx["withash"], tx.calc_witness_hash()) return (txid, wtxid)
def test_compactblock_reconstruction_multiple_peers(self, stalling_peer, delivery_peer): node = self.nodes[0] assert len(self.utxos) def announce_cmpct_block(node, peer): utxo = self.utxos.pop(0) block = self.build_block_with_transactions(node, utxo, 5) cmpct_block = HeaderAndShortIDs() cmpct_block.initialize_from_block(block) msg = msg_cmpctblock(cmpct_block.to_p2p()) peer.send_and_ping(msg) with mininode_lock: assert "getblocktxn" in peer.last_message return block, cmpct_block block, cmpct_block = announce_cmpct_block(node, stalling_peer) for tx in block.vtx[1:]: delivery_peer.send_message(msg_tx(tx)) delivery_peer.sync_with_ping() mempool = node.getrawmempool() for tx in block.vtx[1:]: assert tx.hash in mempool delivery_peer.send_and_ping(msg_cmpctblock(cmpct_block.to_p2p())) assert_equal(int(node.getbestblockhash(), 16), block.sha256) self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue]) # Now test that delivering an invalid compact block won't break relay block, cmpct_block = announce_cmpct_block(node, stalling_peer) for tx in block.vtx[1:]: delivery_peer.send_message(msg_tx(tx)) delivery_peer.sync_with_ping() cmpct_block.prefilled_txn[0].tx.wit.vtxinwit = [CTxInWitness()] cmpct_block.prefilled_txn[0].tx.wit.vtxinwit[0].scriptWitness.stack = [ser_uint256(0)] cmpct_block.use_witness = True delivery_peer.send_and_ping(msg_cmpctblock(cmpct_block.to_p2p())) assert int(node.getbestblockhash(), 16) != block.sha256 msg = msg_blocktxn() msg.block_transactions.blockhash = block.sha256 msg.block_transactions.transactions = block.vtx[1:] stalling_peer.send_and_ping(msg) assert_equal(int(node.getbestblockhash(), 16), block.sha256)
def serialize_addrman(*, format=1, lowest_compatible=3): new = [] tried = [] INCOMPATIBILITY_BASE = 32 r = MAGIC_BYTES["regtest"] r += struct.pack("B", format) r += struct.pack("B", INCOMPATIBILITY_BASE + lowest_compatible) r += ser_uint256(1) r += struct.pack("i", len(new)) r += struct.pack("i", len(tried)) ADDRMAN_NEW_BUCKET_COUNT = 1 << 10 r += struct.pack("i", ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30)) for _ in range(ADDRMAN_NEW_BUCKET_COUNT): r += struct.pack("i", 0) checksum = hash256(r) r += checksum return r
def _check_algorithm_sanity(self): ctx = ContextInfoContainer() ctx.height = 1337 ctx.keystone1 = "010203" ctx.keystone2 = "040506" assert_equal( ctx.getHash().hex(), "db35aad09a65b667a6c9e09cbd47b8d6b378b9ec705db604a4d5cd489afd2bc6") txroot = uint256_from_str( bytes.fromhex( "bf9fb4901a0d8fc9b0d3bf38546191f77a3f2ea5d543546aac0574290c0a9e83" )) poproot = EMPTY_POPDATA_ROOT_V1 tlmr = _calculateTopLevelMerkleRoot(txRoot=txroot, popDataRoot=poproot, ctx=ctx) tlmr_hex = ser_uint256(tlmr).hex() assert_equal( tlmr_hex, "700c1abb69dd1899796b4cafa81c0eefa7b7d0c5aaa4b2bcb67713b2918edb52")
def compute_last_header(prev_header, hashes): """Compute the last filter header from a starting header and a sequence of filter hashes.""" header = ser_uint256(prev_header) for filter_hash in hashes: header = hash256(ser_uint256(filter_hash) + header) return uint256_from_str(header)
def commits_test(self, node): def check_headers(number): wait_until(lambda: node.getblockchaininfo()['headers'] == number, timeout=5) def check_reject(err, block): wait_until(lambda: node.p2p.has_reject(err, block), timeout=5) def getbestblockhash(): return int(node.getbestblockhash(), 16) def make_block(prev=None, secret=None): if secret is None: secret = "default" coinbase_key = CECKey() coinbase_key.set_secretbytes(bytes(secret, "utf-8")) coinbase_pubkey = coinbase_key.get_pubkey() if prev is None: block_base_hash = getbestblockhash() block_time = int(time.time()) + 1 else: block_base_hash = prev.sha256 block_time = prev.nTime + 1 height = prev.height + 1 if prev else 1 snapshot_hash = 0 stake = self.nodes[0].listunspent()[0] coinbase = create_coinbase(height, stake, snapshot_hash, coinbase_pubkey) coinbase.rehash() b = create_block(block_base_hash, coinbase, block_time) b.solve() b.height = height return b def make_commits_msg(blocks): msg = msg_commits(0) for b in blocks: hc = HeaderAndCommits() hc.header = CBlockHeader(b) msg.data += [hc] return msg def send_commits(blocks): node.p2p.reset_messages() node.p2p.send_message(make_commits_msg(blocks)) chain = [] def generate(n): tip = chain[-1] if len(chain) > 0 else None for i in range(0, n): tip = make_block(tip) chain.append(tip) check_headers(0) # initial state of the node # generate 10 blocks and send commits generate(10) send_commits(chain) check_headers(10) # node accepted 10 headers # send same commits again send_commits(chain) check_headers(10) # send last 5 commits send_commits(chain[-5:]) check_headers(10) # generate next 10 blocks, try to send commits starting from 2nd block generate(10) send_commits(chain[11:]) check_reject(b'prev-blk-not-found', 0) # node rejected orphan headers check_headers(10) # must keep old amount of headers # send correct commits send_commits(chain[10:]) check_headers(20) # node accepted headers # generate next 10 blocks, send whole chain generate(10) send_commits(chain) check_headers(30) # node accepted headers # generate next 10 blocks, fool commit in one of them, send them generate(10) msg = make_commits_msg(chain[-10:]) malicious_block = copy.deepcopy(chain[-1]) msg.data[-1].commits = malicious_block.vtx # fool commits with coinbase tx tx = malicious_block.vtx[0] tx.calc_sha256() hashes = [ser_uint256(tx.sha256)] malicious_block.hash_finalizer_commits_merkle_root = CBlock.get_merkle_root(hashes) malicious_block.rehash() msg.data[-1].header.hash_finalizer_commits_merkle_root = malicious_block.hash_finalizer_commits_merkle_root node.p2p.send_message(msg) check_reject(b'bad-non-commit', malicious_block.sha256) # node rejected commits because of non-commit transaction check_headers(30) # must keep old amount of headers # send commits with bad merkle root msg = make_commits_msg(chain[-10:]) malicious_block = copy.deepcopy(chain[-2]) malicious_block.hash_finalizer_commits_merkle_root = 42 malicious_block.rehash() msg.data[-2].header.hash_finalizer_commits_merkle_root = malicious_block.hash_finalizer_commits_merkle_root node.p2p.send_message(msg) check_reject(b'bad-finalizer-commits-merkle-root', malicious_block.sha256) # node rejected commits because of bad commits merkle root check_headers(30) # must keep old amount of headers
def calcsnapshothash(inputs, outputs, *prev_hash): sm = bytes_to_hex_str(ser_uint256(0)) cw = bytes_to_hex_str(ser_uint256(2)) return self.nodes[0].calcsnapshothash(ser_utxos(inputs), ser_utxos(outputs), sm, cw, *prev_hash)
def test_witness_block_size(self): # TODO: Test that non-witness carrying blocks can't exceed 1MB # Skipping this test for now; this is covered in p2p-fullblocktest.py # Test that witness-bearing blocks are limited at ceil(base + wit/4) <= 1MB. block = self.build_next_block() assert len(self.utxo) > 0 # Create a P2WSH transaction. # The witness program will be a bunch of OP_2DROP's, followed by OP_TRUE. # This should give us plenty of room to tweak the spending tx's # virtual size. NUM_DROPS = 200 # 201 max ops per script! NUM_OUTPUTS = 50 witness_program = CScript([OP_2DROP] * NUM_DROPS + [OP_TRUE]) witness_hash = uint256_from_str(sha256(witness_program)) script_pubkey = CScript([OP_0, ser_uint256(witness_hash)]) prevout = COutPoint(self.utxo[0].sha256, self.utxo[0].n) value = self.utxo[0].nValue parent_tx = CTransaction() parent_tx.vin.append(CTxIn(prevout, b"")) child_value = int(value / NUM_OUTPUTS) for i in range(NUM_OUTPUTS): parent_tx.vout.append(CTxOut(child_value, script_pubkey)) parent_tx.vout[0].nValue -= 50000 assert parent_tx.vout[0].nValue > 0 parent_tx.rehash() filler_size = 3150 child_tx = CTransaction() for i in range(NUM_OUTPUTS): child_tx.vin.append(CTxIn(COutPoint(parent_tx.sha256, i), b"")) child_tx.vout = [CTxOut(value - 100000, CScript([OP_TRUE]))] for i in range(NUM_OUTPUTS): child_tx.wit.vtxinwit.append(CTxInWitness()) child_tx.wit.vtxinwit[-1].scriptWitness.stack = [ b'a' * filler_size ] * (2 * NUM_DROPS) + [witness_program] child_tx.rehash() self.update_witness_block_with_transactions(block, [parent_tx, child_tx]) vsize = get_virtual_size(block) assert_greater_than(MAX_BLOCK_BASE_SIZE, vsize) additional_bytes = (MAX_BLOCK_BASE_SIZE - vsize) * 4 i = 0 while additional_bytes > 0: # Add some more bytes to each input until we hit MAX_BLOCK_BASE_SIZE+1 extra_bytes = min(additional_bytes + 1, 55) block.vtx[-1].wit.vtxinwit[int( i / (2 * NUM_DROPS))].scriptWitness.stack[ i % (2 * NUM_DROPS)] = b'a' * (filler_size + extra_bytes) additional_bytes -= extra_bytes i += 1 block.vtx[0].vout.pop() # Remove old commitment add_witness_commitment(block) block.solve() vsize = get_virtual_size(block) assert_equal(vsize, MAX_BLOCK_BASE_SIZE + 1) # Make sure that our test case would exceed the old max-network-message # limit assert len(block.serialize()) > 2 * 1024 * 1024 test_witness_block(self.nodes[0], self.test_node, block, accepted=False) # Now resize the second transaction to make the block fit. cur_length = len(block.vtx[-1].wit.vtxinwit[0].scriptWitness.stack[0]) block.vtx[-1].wit.vtxinwit[0].scriptWitness.stack[0] = b'a' * ( cur_length - 1) block.vtx[0].vout.pop() add_witness_commitment(block) block.solve() assert get_virtual_size(block) == MAX_BLOCK_BASE_SIZE test_witness_block(self.nodes[0], self.test_node, block, accepted=True) # Update available utxo's self.utxo.pop(0) self.utxo.append( UTXO(block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue))
def run_test(self): node = self.nodes[0] self.wallet = MiniWallet(node) self.mine_chain() def assert_submitblock(block, result_str_1, result_str_2=None): block.solve() result_str_2 = result_str_2 or 'duplicate-invalid' assert_equal(result_str_1, node.submitblock(hexdata=block.serialize().hex())) assert_equal(result_str_2, node.submitblock(hexdata=block.serialize().hex())) self.log.info('getmininginfo') mining_info = node.getmininginfo() assert_equal(mining_info['blocks'], 200) assert_equal(mining_info['chain'], self.chain) assert 'currentblocktx' not in mining_info assert 'currentblockweight' not in mining_info assert 'currentblocksize' not in mining_info assert_equal(mining_info['difficulty'], Decimal('4.656542373906925E-10')) assert_equal(mining_info['networkhashps'], Decimal('0.003333333333333334')) assert_equal(mining_info['pooledtx'], 0) self.log.info("getblocktemplate: Test default witness commitment") txid = int(self.wallet.send_self_transfer(from_node=node)['wtxid'], 16) tmpl = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS) # Check that default_witness_commitment is present. assert 'default_witness_commitment' in tmpl witness_commitment = tmpl['default_witness_commitment'] # Check that default_witness_commitment is correct. witness_root = CBlock.get_merkle_root( [ser_uint256(0), ser_uint256(txid)]) script = get_witness_script(witness_root, 0) assert_equal(witness_commitment, script.hex()) # Mine a block to leave initial block download and clear the mempool self.generatetoaddress(node, 1, node.get_deterministic_priv_key().address) tmpl = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS) self.log.info("getblocktemplate: Test capability advertised") assert 'proposal' in tmpl['capabilities'] assert 'coinbasetxn' not in tmpl next_height = int(tmpl["height"]) coinbase_tx = create_coinbase(height=next_height) # sequence numbers must not be max for nLockTime to have effect coinbase_tx.vin[0].nSequence = 2**32 - 2 coinbase_tx.rehash() block = CBlock() block.nVersion = tmpl["version"] block.hashPrevBlock = int(tmpl["previousblockhash"], 16) block.nTime = tmpl["curtime"] block.nBits = int(tmpl["bits"], 16) block.nNonce = 0 block.vtx = [coinbase_tx] self.log.info("getblocktemplate: segwit rule must be set") assert_raises_rpc_error( -8, "getblocktemplate must be called with the segwit rule set", node.getblocktemplate) self.log.info("getblocktemplate: Test valid block") assert_template(node, block, None) self.log.info("submitblock: Test block decode failure") assert_raises_rpc_error(-22, "Block decode failed", node.submitblock, block.serialize()[:-15].hex()) self.log.info( "getblocktemplate: Test bad input hash for coinbase transaction") bad_block = copy.deepcopy(block) bad_block.vtx[0].vin[0].prevout.hash += 1 bad_block.vtx[0].rehash() assert_template(node, bad_block, 'bad-cb-missing') self.log.info("submitblock: Test invalid coinbase transaction") assert_raises_rpc_error(-22, "Block does not start with a coinbase", node.submitblock, bad_block.serialize().hex()) self.log.info("getblocktemplate: Test truncated final transaction") assert_raises_rpc_error( -22, "Block decode failed", node.getblocktemplate, { 'data': block.serialize()[:-1].hex(), 'mode': 'proposal', 'rules': ['segwit'], }) self.log.info("getblocktemplate: Test duplicate transaction") bad_block = copy.deepcopy(block) bad_block.vtx.append(bad_block.vtx[0]) assert_template(node, bad_block, 'bad-txns-duplicate') assert_submitblock(bad_block, 'bad-txns-duplicate', 'bad-txns-duplicate') self.log.info("getblocktemplate: Test invalid transaction") bad_block = copy.deepcopy(block) bad_tx = copy.deepcopy(bad_block.vtx[0]) bad_tx.vin[0].prevout.hash = 255 bad_tx.rehash() bad_block.vtx.append(bad_tx) assert_template(node, bad_block, 'bad-txns-inputs-missingorspent') assert_submitblock(bad_block, 'bad-txns-inputs-missingorspent') self.log.info("getblocktemplate: Test nonfinal transaction") bad_block = copy.deepcopy(block) bad_block.vtx[0].nLockTime = 2**32 - 1 bad_block.vtx[0].rehash() assert_template(node, bad_block, 'bad-txns-nonfinal') assert_submitblock(bad_block, 'bad-txns-nonfinal') self.log.info("getblocktemplate: Test bad tx count") # The tx count is immediately after the block header bad_block_sn = bytearray(block.serialize()) assert_equal(bad_block_sn[BLOCK_HEADER_SIZE], 1) bad_block_sn[BLOCK_HEADER_SIZE] += 1 assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, { 'data': bad_block_sn.hex(), 'mode': 'proposal', 'rules': ['segwit'], }) self.log.info("getblocktemplate: Test bad bits") bad_block = copy.deepcopy(block) bad_block.nBits = 469762303 # impossible in the real world assert_template(node, bad_block, 'bad-diffbits') self.log.info("getblocktemplate: Test bad merkle root") bad_block = copy.deepcopy(block) bad_block.hashMerkleRoot += 1 assert_template(node, bad_block, 'bad-txnmrklroot', False) assert_submitblock(bad_block, 'bad-txnmrklroot', 'bad-txnmrklroot') self.log.info("getblocktemplate: Test bad timestamps") bad_block = copy.deepcopy(block) bad_block.nTime = 2**31 - 1 assert_template(node, bad_block, 'time-too-new') assert_submitblock(bad_block, 'time-too-new', 'time-too-new') bad_block.nTime = 0 assert_template(node, bad_block, 'time-too-old') assert_submitblock(bad_block, 'time-too-old', 'time-too-old') self.log.info("getblocktemplate: Test not best block") bad_block = copy.deepcopy(block) bad_block.hashPrevBlock = 123 assert_template(node, bad_block, 'inconclusive-not-best-prevblk') assert_submitblock(bad_block, 'prev-blk-not-found', 'prev-blk-not-found') self.log.info('submitheader tests') assert_raises_rpc_error( -22, 'Block header decode failed', lambda: node.submitheader(hexdata='xx' * BLOCK_HEADER_SIZE)) assert_raises_rpc_error( -22, 'Block header decode failed', lambda: node.submitheader(hexdata='ff' * (BLOCK_HEADER_SIZE - 2))) assert_raises_rpc_error( -25, 'Must submit previous header', lambda: node.submitheader( hexdata=super(CBlock, bad_block).serialize().hex())) block.nTime += 1 block.solve() def chain_tip(b_hash, *, status='headers-only', branchlen=1): return { 'hash': b_hash, 'height': 202, 'branchlen': branchlen, 'status': status } assert chain_tip(block.hash) not in node.getchaintips() node.submitheader(hexdata=block.serialize().hex()) assert chain_tip(block.hash) in node.getchaintips() node.submitheader( hexdata=CBlockHeader(block).serialize().hex()) # Noop assert chain_tip(block.hash) in node.getchaintips() bad_block_root = copy.deepcopy(block) bad_block_root.hashMerkleRoot += 2 bad_block_root.solve() assert chain_tip(bad_block_root.hash) not in node.getchaintips() node.submitheader( hexdata=CBlockHeader(bad_block_root).serialize().hex()) assert chain_tip(bad_block_root.hash) in node.getchaintips() # Should still reject invalid blocks, even if we have the header: assert_equal( node.submitblock(hexdata=bad_block_root.serialize().hex()), 'bad-txnmrklroot') assert_equal( node.submitblock(hexdata=bad_block_root.serialize().hex()), 'bad-txnmrklroot') assert chain_tip(bad_block_root.hash) in node.getchaintips() # We know the header for this invalid block, so should just return early without error: node.submitheader( hexdata=CBlockHeader(bad_block_root).serialize().hex()) assert chain_tip(bad_block_root.hash) in node.getchaintips() bad_block_lock = copy.deepcopy(block) bad_block_lock.vtx[0].nLockTime = 2**32 - 1 bad_block_lock.vtx[0].rehash() bad_block_lock.hashMerkleRoot = bad_block_lock.calc_merkle_root() bad_block_lock.solve() assert_equal( node.submitblock(hexdata=bad_block_lock.serialize().hex()), 'bad-txns-nonfinal') assert_equal( node.submitblock(hexdata=bad_block_lock.serialize().hex()), 'duplicate-invalid') # Build a "good" block on top of the submitted bad block bad_block2 = copy.deepcopy(block) bad_block2.hashPrevBlock = bad_block_lock.sha256 bad_block2.solve() assert_raises_rpc_error( -25, 'bad-prevblk', lambda: node.submitheader(hexdata=CBlockHeader( bad_block2).serialize().hex())) # Should reject invalid header right away bad_block_time = copy.deepcopy(block) bad_block_time.nTime = 1 bad_block_time.solve() assert_raises_rpc_error( -25, 'time-too-old', lambda: node.submitheader( hexdata=CBlockHeader(bad_block_time).serialize().hex())) # Should ask for the block from a p2p node, if they announce the header as well: peer = node.add_p2p_connection(P2PDataStore()) peer.wait_for_getheaders(timeout=5) # Drop the first getheaders peer.send_blocks_and_test(blocks=[block], node=node) # Must be active now: assert chain_tip(block.hash, status='active', branchlen=0) in node.getchaintips() # Building a few blocks should give the same results self.generatetoaddress(node, 10, node.get_deterministic_priv_key().address) assert_raises_rpc_error( -25, 'time-too-old', lambda: node.submitheader( hexdata=CBlockHeader(bad_block_time).serialize().hex())) assert_raises_rpc_error( -25, 'bad-prevblk', lambda: node.submitheader(hexdata=CBlockHeader( bad_block2).serialize().hex())) node.submitheader(hexdata=CBlockHeader(block).serialize().hex()) node.submitheader( hexdata=CBlockHeader(bad_block_root).serialize().hex()) assert_equal(node.submitblock(hexdata=block.serialize().hex()), 'duplicate') # valid
def run_test(self): bitno = 1 activated_version = 0x20000000 | (1 << bitno) node = self.nodes[0] # convenience reference to the node self.bootstrap_p2p() # Add one p2p connection to the node assert_equal(self.get_bip9_status('finaltx')['status'], 'defined') assert_equal(self.get_bip9_status('finaltx')['since'], 0) self.log.info( "Generate some blocks to get the chain going and un-stick the mining RPCs" ) node.generate(2) assert_equal(node.getblockcount(), 2) self.height = 3 # height of the next block to build self.tip = int("0x" + node.getbestblockhash(), 0) self.nodeaddress = node.getnewaddress() self.last_block_time = int(time.time()) self.log.info("\'finaltx\' begins in DEFINED state") assert_equal(self.get_bip9_status('finaltx')['status'], 'defined') assert_equal(self.get_bip9_status('finaltx')['since'], 0) tmpl = node.getblocktemplate( {'rules': ['segwit', 'finaltx', 'auxpow']}) assert ('finaltx' not in tmpl['rules']) assert ('finaltx' not in tmpl['vbavailable']) assert ('finaltx' not in tmpl) assert_equal(tmpl['vbrequired'], 0) assert_equal(tmpl['version'] & activated_version, 0x20000000) self.log.info("Test 1: Advance from DEFINED to STARTED") test_blocks = self.generate_blocks(141, 4) # height = 143 assert_equal(self.get_bip9_status('finaltx')['status'], 'started') assert_equal(self.get_bip9_status('finaltx')['since'], 144) assert_equal( self.get_bip9_status('finaltx')['statistics']['elapsed'], 0) assert_equal(self.get_bip9_status('finaltx')['statistics']['count'], 0) tmpl = node.getblocktemplate( {'rules': ['segwit', 'finaltx', 'auxpow']}) assert ('finaltx' not in tmpl['rules']) assert_equal(tmpl['vbavailable']['finaltx'], bitno) assert_equal(tmpl['vbrequired'], 0) assert ('finaltx' not in tmpl) assert_equal(tmpl['version'] & activated_version, activated_version) self.log.info( "Save one of the anyone-can-spend coinbase outputs for later.") assert_equal(test_blocks[-1][0].vtx[0].vout[0].nValue, 5000000000) assert_equal(test_blocks[-1][0].vtx[0].vout[0].scriptPubKey, CScript([OP_TRUE])) early_coin = COutPoint(test_blocks[-1][0].vtx[0].sha256, 0) self.log.info( "Test 2: Check stats after max number of \"not signalling\" blocks such that LOCKED_IN still possible this period" ) self.generate_blocks(36, 4) # 0x00000004 (not signalling) self.generate_blocks(10, activated_version) # 0x20000001 (not signalling) assert_equal( self.get_bip9_status('finaltx')['statistics']['elapsed'], 46) assert_equal( self.get_bip9_status('finaltx')['statistics']['count'], 10) assert_equal( self.get_bip9_status('finaltx')['statistics']['possible'], True) self.log.info( "Test 3: Check stats after one additional \"not signalling\" block -- LOCKED_IN no longer possible this period" ) self.generate_blocks(1, 4) # 0x00000004 (not signalling) assert_equal( self.get_bip9_status('finaltx')['statistics']['elapsed'], 47) assert_equal( self.get_bip9_status('finaltx')['statistics']['count'], 10) assert_equal( self.get_bip9_status('finaltx')['statistics']['possible'], False) self.log.info( "Test 4: Finish period with \"ready\" blocks, but soft fork will still fail to advance to LOCKED_IN" ) self.generate_blocks( 97, activated_version) # 0x20000001 (signalling ready) assert_equal( self.get_bip9_status('finaltx')['statistics']['elapsed'], 0) assert_equal(self.get_bip9_status('finaltx')['statistics']['count'], 0) assert_equal( self.get_bip9_status('finaltx')['statistics']['possible'], True) assert_equal(self.get_bip9_status('finaltx')['status'], 'started') self.log.info( "Test 5: Fail to achieve LOCKED_IN 100 out of 144 signal bit 1 using a variety of bits to simulate multiple parallel softforks" ) self.generate_blocks( 50, activated_version) # 0x20000001 (signalling ready) self.generate_blocks(20, 4) # 0x00000004 (not signalling) self.generate_blocks( 50, activated_version) # 0x20000101 (signalling ready) self.generate_blocks(24, 4) # 0x20010000 (not signalling) assert_equal(self.get_bip9_status('finaltx')['status'], 'started') assert_equal(self.get_bip9_status('finaltx')['since'], 144) assert_equal( self.get_bip9_status('finaltx')['statistics']['elapsed'], 0) assert_equal(self.get_bip9_status('finaltx')['statistics']['count'], 0) tmpl = node.getblocktemplate( {'rules': ['segwit', 'finaltx', 'auxpow']}) assert ('finaltx' not in tmpl['rules']) assert_equal(tmpl['vbavailable']['finaltx'], bitno) assert_equal(tmpl['vbrequired'], 0) assert_equal(tmpl['version'] & activated_version, activated_version) self.log.info( "Test 6: 108 out of 144 signal bit 1 to achieve LOCKED_IN using a variety of bits to simulate multiple parallel softforks" ) self.generate_blocks( 57, activated_version) # 0x20000001 (signalling ready) self.generate_blocks(26, 4) # 0x00000004 (not signalling) self.generate_blocks( 50, activated_version) # 0x20000101 (signalling ready) self.generate_blocks(10, 4) # 0x20010000 (not signalling) self.log.info( "check counting stats and \"possible\" flag before last block of this period achieves LOCKED_IN..." ) assert_equal( self.get_bip9_status('finaltx')['statistics']['elapsed'], 143) assert_equal( self.get_bip9_status('finaltx')['statistics']['count'], 107) assert_equal( self.get_bip9_status('finaltx')['statistics']['possible'], True) assert_equal(self.get_bip9_status('finaltx')['status'], 'started') self.log.info("Test 7: ...continue with Test 6") self.generate_blocks( 1, activated_version) # 0x20000001 (signalling ready) assert_equal(self.get_bip9_status('finaltx')['status'], 'locked_in') assert_equal(self.get_bip9_status('finaltx')['since'], 576) tmpl = node.getblocktemplate( {'rules': ['segwit', 'finaltx', 'auxpow']}) assert ('finaltx' not in tmpl['rules']) self.log.info( "Test 8: 143 more version 536870913 blocks (waiting period-1)") self.generate_blocks(143, 4) assert_equal(self.get_bip9_status('finaltx')['status'], 'locked_in') assert_equal(self.get_bip9_status('finaltx')['since'], 576) tmpl = node.getblocktemplate( {'rules': ['segwit', 'finaltx', 'auxpow']}) assert ('finaltx' not in tmpl['rules']) assert ('finaltx' in tmpl['vbavailable']) assert_equal(tmpl['vbrequired'], 0) assert_equal(tmpl['version'] & activated_version, activated_version) self.log.info( "Test 9: Generate a block without any spendable outputs, which should be allowed under normal circumstances." ) test_blocks = self.generate_blocks(1, 4, sync=False) for txout in test_blocks[-1][0].vtx[0].vout: txout.scriptPubKey = CScript([OP_FALSE]) test_blocks[-1][0].vtx[0].rehash() test_blocks[-1][0].hashMerkleRoot = test_blocks[-1][ 0].calc_merkle_root() test_blocks[-1][0].rehash() test_blocks[-1][0].solve() node.submitblock(ToHex(test_blocks[-1][0])) assert_equal(node.getbestblockhash(), test_blocks[-1][0].hash) self.tip = test_blocks[-1][0].sha256 # Hash has changed assert_equal(self.get_bip9_status('finaltx')['status'], 'active') tmpl = node.getblocktemplate( {'rules': ['segwit', 'finaltx', 'auxpow']}) assert ('finaltx' in tmpl['rules']) assert ('finaltx' not in tmpl['vbavailable']) assert_equal(tmpl['vbrequired'], 0) assert (not (tmpl['version'] & (1 << bitno))) self.log.info( "Test 10: Attempt to do the same thing: generate a block with no spendable outputs in the coinbase. This fails because the next block needs at least one trivially spendable output to start the block-final transaction chain." ) block = create_block(self.tip, create_coinbase(self.height), self.last_block_time + 1) block.nVersion = 5 for txout in block.vtx[0].vout: txout.scriptPubKey = CScript([OP_FALSE]) block.vtx[0].rehash() block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() node.submitblock(ToHex(block)) assert_equal(node.getbestblockhash(), ser_uint256(self.tip)[::-1].hex()) self.log.info( "Test 11: Generate the first block with block-final-tx rules enforced, which reuires the coinbase to have a trivially-spendable output." ) self.generate_blocks(1, 4) assert (any(out.scriptPubKey == CScript([OP_TRUE]) for out in test_blocks[-1][0].vtx[0].vout)) for n, txout in enumerate(test_blocks[-1][0].vtx[0].vout): non_protected_output = COutPoint(test_blocks[-1][0].vtx[0].sha256, n) assert_equal(txout.nValue, 312500000) self.log.info("Test 12: Generate 98 blocks (maturity period - 2)") self.generate_blocks(98, 4) tmpl = node.getblocktemplate( {'rules': ['segwit', 'finaltx', 'auxpow']}) assert ('finaltx' not in tmpl) self.log.info( "Test 13: Generate one more block to allow non_protected_output to mature, which causes the block-final transaction to be required in the next block." ) self.generate_blocks(1, 4) tmpl = node.getblocktemplate( {'rules': ['segwit', 'finaltx', 'auxpow']}) assert ('finaltx' in tmpl) assert_equal(len(tmpl['finaltx']['prevout']), 1) assert_equal( tmpl['finaltx']['prevout'][0]['txid'], encode(ser_uint256(non_protected_output.hash)[::-1], 'hex_codec').decode('ascii')) assert_equal(tmpl['finaltx']['prevout'][0]['vout'], non_protected_output.n) assert_equal(tmpl['finaltx']['prevout'][0]['amount'], 312470199) self.log.info( "Extra pass-through value is not included in the coinbasevalue field." ) assert_equal(tmpl['coinbasevalue'], 5000000000 // 2**(self.height // 150)) self.log.info( "The transactions field does not contain the block-final transaction." ) assert_equal(len(tmpl['transactions']), 0) self.log.info( "Test 14: Attempt to create a block without the block-final transaction, which fails." ) block = create_block(self.tip, create_coinbase(self.height), self.last_block_time + 1) block.nVersion = 4 block.rehash() block.solve() node.submitblock(ToHex(block)) assert_equal(node.getbestblockhash(), ser_uint256(self.tip)[::-1].hex()) self.log.info( "Test 15: Add the block-final transaction, and it passes.") tx_final = CTransaction() tx_final.nVersion = 2 tx_final.vin.append( CTxIn(non_protected_output, CScript([]), 0xffffffff)) tx_final.vout.append(CTxOut(312470199, CScript([OP_TRUE]))) tx_final.nLockTime = block.vtx[0].nLockTime tx_final.lock_height = block.vtx[0].lock_height tx_final.rehash() block.vtx.append(tx_final) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() node.submitblock(ToHex(block)) assert_equal(node.getbestblockhash(), block.hash) prev_final_tx = block.vtx[-1] self.last_block_time += 1 self.tip = block.sha256 self.height += 1 tmpl = node.getblocktemplate( {'rules': ['segwit', 'finaltx', 'auxpow']}) assert ('finaltx' in tmpl) assert_equal(len(tmpl['finaltx']['prevout']), 1) assert_equal( tmpl['finaltx']['prevout'][0]['txid'], encode(ser_uint256(tx_final.sha256)[::-1], 'hex_codec').decode('ascii')) assert_equal(tmpl['finaltx']['prevout'][0]['vout'], 0) assert_equal(tmpl['finaltx']['prevout'][0]['amount'], 312469901) self.log.info( "Test 16: Create a block-final transaction with multiple outputs, which doesn't work because the number of outputs is restricted to be no greater than the number of inputs." ) block = create_block(self.tip, create_coinbase(self.height), self.last_block_time + 1) block.nVersion = 4 tx_final = CTransaction() tx_final.nVersion = 2 tx_final.vin.append( CTxIn(COutPoint(prev_final_tx.sha256, 0), CScript([]), 0xffffffff)) tx_final.vout.append(CTxOut(156234951, CScript([OP_TRUE]))) tx_final.vout.append(CTxOut(156234950, CScript([OP_TRUE]))) tx_final.nLockTime = block.vtx[0].nLockTime tx_final.lock_height = block.vtx[0].lock_height tx_final.rehash() block.vtx.append(tx_final) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() node.submitblock(ToHex(block)) assert_equal(node.getbestblockhash(), ser_uint256(self.tip)[::-1].hex()) self.log.info( "Test 17: Try increasing the number of inputs by using an old one doesn't work, because the block-final transaction must source its user inputs from the same block." ) utxo = node.gettxout( encode(ser_uint256(early_coin.hash)[::-1], 'hex_codec').decode('ascii'), early_coin.n) assert ('amount' in utxo) utxo_amount = int(100000000 * utxo['amount']) block.vtx[-1].vin.append(CTxIn(early_coin, CScript([]), 0xffffffff)) block.vtx[-1].rehash() block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() node.submitblock(ToHex(block)) assert_equal(node.getbestblockhash(), ser_uint256(self.tip)[::-1].hex()) self.log.info( "Test 18: But spend it via a user transaction instead, and it can be captured and sent to the coinbase as fee." ) # Insert spending transaction spend_tx = CTransaction() spend_tx.nVersion = 2 spend_tx.vin.append(CTxIn(early_coin, CScript([]), 0xffffffff)) spend_tx.vout.append(CTxOut(utxo_amount, CScript([OP_TRUE]))) spend_tx.nLockTime = 0 spend_tx.lock_height = block.vtx[0].lock_height spend_tx.rehash() block.vtx.insert(1, spend_tx) # Capture output of spend_tx in block-final tx (but don't update the # outputs--the value passes on to the coinbase as fee). block.vtx[-1].vin[-1].prevout = COutPoint(spend_tx.sha256, 0) block.vtx[-1].rehash() # Add the captured value to the block reward. block.vtx[0].vout[0].nValue += utxo_amount block.vtx[0].rehash() block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() node.submitblock(ToHex(block)) assert_equal(node.getbestblockhash(), block.hash) prev_final_tx = block.vtx[-1] self.last_block_time += 1 self.tip = block.sha256 self.height += 1 self.log.info( "Test 19: Spending only one of the prior outputs is insufficient. ALL prior block-final outputs must be spent." ) block = create_block(self.tip, create_coinbase(self.height), self.last_block_time + 1) block.nVersion = 4 tx_final = CTransaction() tx_final.nVersion = 2 tx_final.vin.append( CTxIn(COutPoint(prev_final_tx.sha256, 0), CScript([]), 0xffffffff)) tx_final.vout.append(CTxOut(156234801, CScript([OP_TRUE]))) tx_final.nLockTime = block.vtx[0].nLockTime tx_final.lock_height = block.vtx[0].lock_height tx_final.rehash() block.vtx.append(tx_final) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() node.submitblock(ToHex(block)) assert_equal(node.getbestblockhash(), ser_uint256(self.tip)[::-1].hex()) self.log.info( "Test 20: But spend all the prior outputs and it goes through.") block.vtx[-1].vin.append( CTxIn(COutPoint(prev_final_tx.sha256, 1), CScript([]), 0xffffffff)) block.vtx[-1].vout[0].nValue *= 2 block.vtx[-1].rehash() block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() node.submitblock(ToHex(block)) assert_equal(node.getbestblockhash(), block.hash) prev_final_tx = block.vtx[-1] self.last_block_time += 1 self.tip = block.sha256 self.height += 1 self.log.info( "Test 21: Now that the rules have activated, transactions spending the previous block-final transaction's outputs are rejected from the mempool." ) self.log.info( "First we do this with a non-block-final input to demonstrate the test would otherwise work." ) height = node.getblockcount() - 99 while True: blk = node.getblock(node.getblockhash(height)) txid = blk['tx'][0] utxo = node.gettxout(txid, 0) if utxo is not None and utxo['scriptPubKey']['hex'] == "51": break height -= 1 spend_tx = CTransaction() spend_tx.nVersion = 2 spend_tx.vin.append( CTxIn(COutPoint(uint256_from_str(unhexlify(txid)[::-1]), 0), CScript([]), 0xffffffff)) spend_tx.vout.append( CTxOut( int(utxo['amount'] * 100000000) - 10000, CScript([b'a' * 100])) ) # Make transaction large enough to avoid tx-size-small standardness check spend_tx.nLockTime = 0 spend_tx.lock_height = utxo['refheight'] spend_tx.rehash() node.sendrawtransaction(ToHex(spend_tx)) mempool = node.getrawmempool() assert (spend_tx.hash in mempool) self.log.info( "Now we do the same exact thing with the last block-final transaction's outputs. It should not enter the mempool." ) spend_tx = CTransaction() spend_tx.nVersion = 2 spend_tx.vin.append( CTxIn(COutPoint(prev_final_tx.sha256, 0), CScript([]), 0xffffffff)) spend_tx.vout.append( CTxOut(int(utxo['amount'] * 100000000), CScript([b'a' * 100])) ) # Make transaction large enough to avoid tx-size-small standardness check spend_tx.nLockTime = 0 spend_tx.lock_height = utxo['refheight'] spend_tx.rehash() try: node.sendrawtransaction(ToHex(spend_tx)) except JSONRPCException as e: assert ("spend-block-final-txn" in e.error['message']) else: assert (False) mempool = node.getrawmempool() assert (spend_tx.hash not in mempool) self.log.info( "Test 22: Invalidate the tip, then malleate and re-solve the same block. This is a fast way of testing test that the block-final txid is restored on a reorg." ) height = node.getblockcount() node.invalidateblock(block.hash) assert_equal(node.getblockcount(), height - 1) block.nVersion ^= 2 block.rehash() block.solve() node.submitblock(ToHex(block)) assert_equal(node.getblockcount(), height) assert_equal(node.getbestblockhash(), block.hash) self.tip = block.sha256 self.finaltx_vin = [ CTxIn(COutPoint(block.vtx[-1].sha256, 0), CScript([]), 0xffffffff) ] self.log.info( "Test 22-25: Mine two blocks with trivially-spendable coinbase outputs, then test that the one that is exactly 100 blocks old is allowed to be spent in a block-final transaction, but the older one cannot." ) self.generate_blocks(1, 4, finaltx=True) assert_equal(test_blocks[-1][0].vtx[0].vout[0].scriptPubKey, CScript([OP_TRUE])) txin1 = CTxIn(COutPoint(test_blocks[-1][0].vtx[0].sha256, 0), CScript([]), 0xffffffff) self.generate_blocks(1, 4, finaltx=True) assert_equal(test_blocks[-1][0].vtx[0].vout[0].scriptPubKey, CScript([OP_TRUE])) txin2 = CTxIn(COutPoint(test_blocks[-1][0].vtx[0].sha256, 0), CScript([]), 0xffffffff) self.generate_blocks(1, 4, finaltx=True) assert_equal(test_blocks[-1][0].vtx[0].vout[0].scriptPubKey, CScript([OP_TRUE])) txin3 = CTxIn(COutPoint(test_blocks[-1][0].vtx[0].sha256, 0), CScript([]), 0xffffffff) self.generate_blocks(98, 4, finaltx=True) # txin1 is too old -- it should have been collected on the last block block = create_block(self.tip, create_coinbase(self.height), self.last_block_time + 1) block.nVersion = 4 tx_final = CTransaction() tx_final.nVersion = 2 tx_final.vin.extend(self.finaltx_vin) tx_final.vin.append(txin1) tx_final.vout.append(CTxOut(0, CScript([OP_TRUE]))) tx_final.nLockTime = block.vtx[0].nLockTime tx_final.lock_height = block.vtx[0].lock_height tx_final.rehash() block.vtx.append(tx_final) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() node.submitblock(ToHex(block)) assert_equal(node.getbestblockhash(), ser_uint256(self.tip)[::-1].hex()) # txin3 is too young -- it hasn't matured block.vtx[-1].vin.pop() block.vtx[-1].vin.append(txin3) block.vtx[-1].rehash() block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() node.submitblock(ToHex(block)) assert_equal(node.getbestblockhash(), ser_uint256(self.tip)[::-1].hex()) # txin2 is just right block.vtx[-1].vin.pop() block.vtx[-1].vin.append(txin2) block.vtx[-1].rehash() block.hashMerkleRoot = block.calc_merkle_root() block.rehash() block.solve() node.submitblock(ToHex(block)) assert_equal(node.getbestblockhash(), block.hash) self.last_block_time += 1 self.tip = block.sha256 self.height += 1 self.finaltx_vin = [ CTxIn(COutPoint(block.vtx[-1].sha256, 0), CScript([]), 0xffffffff) ]
def on_inv(self, message): for inv in message.inv: hashhex = ser_uint256(inv.hash)[::-1].hex() self.seen_invs[hashhex] = time.time() super().on_inv(message)
def tapscript_satisfy_test(self, script, inputs=[], add_issuance=False, add_pegin=False, fail=None, add_prevout=False, add_asset=False, add_value=False, add_spk=False, seq=0, add_out_spk=None, add_out_asset=None, add_out_value=None, add_out_nonce=None, ver=2, locktime=0, add_num_outputs=False, add_weight=False, blind=False): # Create a taproot utxo scripts = [("s0", script)] prev_tx, prev_vout, spk, sec, pub, tap = self.create_taproot_utxo( scripts) if add_pegin: fund_info = self.nodes[0].getpeginaddress() peg_id = self.nodes[0].sendtoaddress( fund_info["mainchain_address"], 1) raw_peg_tx = self.nodes[0].gettransaction(peg_id)["hex"] peg_txid = self.nodes[0].sendrawtransaction(raw_peg_tx) self.nodes[0].generate(101) peg_prf = self.nodes[0].gettxoutproof([peg_txid]) claim_script = fund_info["claim_script"] raw_claim = self.nodes[0].createrawpegin(raw_peg_tx, peg_prf, claim_script) tx = FromHex(CTransaction(), raw_claim['hex']) else: tx = CTransaction() tx.nVersion = ver tx.nLockTime = locktime # Spend the pegin and taproot tx together in_total = prev_tx.vout[prev_vout].nValue.getAmount() fees = 1000 tap_in_pos = 0 if blind: # Add an unrelated output key = ECKey() key.generate() tx.vout.append( CTxOut(nValue=CTxOutValue(10000), scriptPubKey=spk, nNonce=CTxOutNonce(key.get_pubkey().get_bytes()))) tx_hex = self.nodes[0].fundrawtransaction(tx.serialize().hex()) tx = FromHex(CTransaction(), tx_hex['hex']) tx.vin.append( CTxIn(COutPoint(prev_tx.sha256, prev_vout), nSequence=seq)) tx.vout.append( CTxOut(nValue=CTxOutValue(in_total - fees), scriptPubKey=spk)) # send back to self tx.vout.append(CTxOut(CTxOutValue(fees))) if add_issuance: blind_addr = self.nodes[0].getnewaddress() issue_addr = self.nodes[0].validateaddress( blind_addr)['unconfidential'] # Issuances only require one fee output and that output must the last # one. However the way, the current code is structured, it is not possible # to this in a super clean without special casing. if add_pegin: tx.vout.pop() tx.vout.pop() tx.vout.insert(0, CTxOut(nValue=CTxOutValue(in_total), scriptPubKey=spk)) # send back to self) issued_tx = self.nodes[0].rawissueasset( tx.serialize().hex(), [{ "asset_amount": 2, "asset_address": issue_addr, "blind": False }])[0]["hex"] tx = FromHex(CTransaction(), issued_tx) # Sign inputs if add_pegin: signed = self.nodes[0].signrawtransactionwithwallet( tx.serialize().hex()) tx = FromHex(CTransaction(), signed['hex']) tap_in_pos += 1 else: # Need to create empty witness when not deserializing from rpc tx.wit.vtxinwit.append(CTxInWitness()) if blind: tx.vin[0], tx.vin[1] = tx.vin[1], tx.vin[0] utxo = self.get_utxo(tx, 1) zero_str = "0" * 64 blinded_raw = self.nodes[0].rawblindrawtransaction( tx.serialize().hex(), [zero_str, utxo["amountblinder"]], [1.2, utxo['amount']], [utxo['asset'], utxo['asset']], [zero_str, utxo['assetblinder']]) tx = FromHex(CTransaction(), blinded_raw) signed_raw_tx = self.nodes[0].signrawtransactionwithwallet( tx.serialize().hex()) tx = FromHex(CTransaction(), signed_raw_tx['hex']) suffix_annex = [] control_block = bytes([ tap.leaves["s0"].version + tap.negflag ]) + tap.inner_pubkey + tap.leaves["s0"].merklebranch # Add the prevout to the top of inputs. The witness script will check for equality. if add_prevout: inputs = [ prev_vout.to_bytes(4, 'little'), ser_uint256(prev_tx.sha256) ] if add_asset: assert blind # only used with blinding in testing utxo = self.nodes[0].gettxout( ser_uint256(tx.vin[1].prevout.hash)[::-1].hex(), tx.vin[1].prevout.n) if "assetcommitment" in utxo: asset = bytes.fromhex(utxo["assetcommitment"]) else: asset = b"\x01" + bytes.fromhex(utxo["asset"])[::-1] inputs = [asset[0:1], asset[1:33]] if add_value: utxo = self.nodes[0].gettxout( ser_uint256(tx.vin[1].prevout.hash)[::-1].hex(), tx.vin[1].prevout.n) if "valuecommitment" in utxo: value = bytes.fromhex(utxo["valuecommitment"]) inputs = [value[0:1], value[1:33]] else: value = b"\x01" + int( satoshi_round(utxo["value"]) * COIN).to_bytes(8, 'little') inputs = [value[0:1], value[1:9]] if add_spk: ver = CScriptOp.decode_op_n(int.from_bytes(spk[0:1], 'little')) inputs = [CScriptNum.encode(CScriptNum(ver))[1:], spk[2:len(spk)]] # always segwit # Add witness for outputs if add_out_asset is not None: asset = tx.vout[add_out_asset].nAsset.vchCommitment inputs = [asset[0:1], asset[1:33]] if add_out_value is not None: value = tx.vout[add_out_value].nValue.vchCommitment if len(value) == 9: inputs = [value[0:1], value[1:9][::-1]] else: inputs = [value[0:1], value[1:33]] if add_out_nonce is not None: nonce = tx.vout[add_out_nonce].nNonce.vchCommitment if len(nonce) == 1: inputs = [b''] else: inputs = [nonce] if add_out_spk is not None: out_spk = tx.vout[add_out_spk].scriptPubKey if len(out_spk) == 0: # Python upstream encoding CScriptNum interesting behaviour where it also encodes the length # This assumes the implicit wallet behaviour of using segwit outputs. # This is useful while sending scripts, but not while using CScriptNums in constructing scripts inputs = [ CScriptNum.encode(CScriptNum(-1))[1:], sha256(out_spk) ] else: ver = CScriptOp.decode_op_n( int.from_bytes(out_spk[0:1], 'little')) inputs = [ CScriptNum.encode(CScriptNum(ver))[1:], out_spk[2:len(out_spk)] ] # always segwit if add_num_outputs: num_outs = len(tx.vout) inputs = [CScriptNum.encode(CScriptNum(num_outs))[1:]] if add_weight: # Add a dummy input and check the overall weight inputs = [int(5).to_bytes(8, 'little')] wit = inputs + [bytes(tap.leaves["s0"].script), control_block ] + suffix_annex tx.wit.vtxinwit[tap_in_pos].scriptWitness.stack = wit exp_weight = self.nodes[0].decoderawtransaction( tx.serialize().hex())["weight"] inputs = [exp_weight.to_bytes(8, 'little')] wit = inputs + [bytes(tap.leaves["s0"].script), control_block ] + suffix_annex tx.wit.vtxinwit[tap_in_pos].scriptWitness.stack = wit if fail: assert_raises_rpc_error(-26, fail, self.nodes[0].sendrawtransaction, tx.serialize().hex()) return self.nodes[0].sendrawtransaction(hexstring=tx.serialize().hex()) self.nodes[0].generate(1) last_blk = self.nodes[0].getblock(self.nodes[0].getbestblockhash()) tx.rehash() assert (tx.hash in last_blk['tx'])