diff --git a/test/functional/mempool_expiry.py b/test/functional/mempool_expiry.py index 2b7530322..ba0dd60ea 100644 --- a/test/functional/mempool_expiry.py +++ b/test/functional/mempool_expiry.py @@ -1,126 +1,121 @@ # Copyright (c) 2020 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Tests that a mempool transaction expires after a given timeout and that its children are removed as well. Both the default expiry timeout defined by DEFAULT_MEMPOOL_EXPIRY_HOURS and a user definable expiry timeout via the '-mempoolexpiry=' command line argument ( is the timeout in hours) are tested. """ from datetime import timedelta from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error from test_framework.wallet import MiniWallet DEFAULT_MEMPOOL_EXPIRY_HOURS = 336 # hours CUSTOM_MEMPOOL_EXPIRY = 10 # hours class MempoolExpiryTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 - self.setup_clean_chain = True def test_transaction_expiry(self, timeout): """Tests that a transaction expires after the expiry timeout and its children are removed as well.""" node = self.nodes[0] - self.wallet = MiniWallet(node) - - # Add enough mature utxos to the wallet so that all txs spend confirmed - # coins. - self.generate(self.wallet, 4) - self.generate(node, 100) # Send a parent transaction that will expire. parent_txid = self.wallet.send_self_transfer(from_node=node)["txid"] parent_utxo = self.wallet.get_utxo(txid=parent_txid) independent_utxo = self.wallet.get_utxo() # Ensure the transactions we send to trigger the mempool check spend # utxos that are independent of the transactions being tested for # expiration. trigger_utxo1 = self.wallet.get_utxo() trigger_utxo2 = self.wallet.get_utxo() # Set the mocktime to the arrival time of the parent transaction. entry_time = node.getmempoolentry(parent_txid)["time"] node.setmocktime(entry_time) # Let half of the timeout elapse and broadcast the child transaction # spending the parent transaction. half_expiry_time = entry_time + int(60 * 60 * timeout / 2) node.setmocktime(half_expiry_time) child_txid = self.wallet.send_self_transfer( from_node=node, utxo_to_spend=parent_utxo )["txid"] assert_equal(parent_txid, node.getmempoolentry(child_txid)["depends"][0]) self.log.info( "Broadcast child transaction after " f"{timedelta(seconds=half_expiry_time - entry_time)} hours." ) # Broadcast another (independent) transaction. independent_txid = self.wallet.send_self_transfer( from_node=node, utxo_to_spend=independent_utxo )["txid"] # Let most of the timeout elapse and check that the parent tx is still # in the mempool. nearly_expiry_time = entry_time + 60 * 60 * timeout - 5 node.setmocktime(nearly_expiry_time) # Broadcast a transaction as the expiry of transactions in the mempool # is only checked when a new transaction is added to the mempool. self.wallet.send_self_transfer(from_node=node, utxo_to_spend=trigger_utxo1) self.log.info( "Test parent tx not expired after " f"{timedelta(seconds=nearly_expiry_time - entry_time)} hours." ) assert_equal(entry_time, node.getmempoolentry(parent_txid)["time"]) # Transaction should be evicted from the mempool after the expiry time # has passed. expiry_time = entry_time + 60 * 60 * timeout + 5 node.setmocktime(expiry_time) # Again, broadcast a transaction so the expiry of transactions in the # mempool is checked. self.wallet.send_self_transfer(from_node=node, utxo_to_spend=trigger_utxo2) self.log.info( "Test parent tx expiry after " f"{timedelta(seconds=expiry_time - entry_time)} hours." ) assert_raises_rpc_error( -5, "Transaction not in mempool", node.getmempoolentry, parent_txid ) # The child transaction should be removed from the mempool as well. self.log.info("Test child tx is evicted as well.") assert_raises_rpc_error( -5, "Transaction not in mempool", node.getmempoolentry, child_txid ) # Check that the independent tx is still in the mempool. self.log.info( "Test the independent tx not expired after " f"{timedelta(seconds=expiry_time - half_expiry_time)} hours." ) assert_equal(half_expiry_time, node.getmempoolentry(independent_txid)["time"]) def run_test(self): + self.wallet = MiniWallet(self.nodes[0]) + self.log.info( f"Test default mempool expiry timeout of {DEFAULT_MEMPOOL_EXPIRY_HOURS} hours." ) self.test_transaction_expiry(DEFAULT_MEMPOOL_EXPIRY_HOURS) self.log.info( f"Test custom mempool expiry timeout of {CUSTOM_MEMPOOL_EXPIRY} hours." ) self.restart_node(0, [f"-mempoolexpiry={CUSTOM_MEMPOOL_EXPIRY}"]) self.test_transaction_expiry(CUSTOM_MEMPOOL_EXPIRY) if __name__ == "__main__": MempoolExpiryTest().main() diff --git a/test/functional/mempool_resurrect.py b/test/functional/mempool_resurrect.py index a0cf320bc..89cc02c02 100644 --- a/test/functional/mempool_resurrect.py +++ b/test/functional/mempool_resurrect.py @@ -1,69 +1,63 @@ # Copyright (c) 2014-2019 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test resurrection of mined transactions when the blockchain is re-organized.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal from test_framework.wallet import MiniWallet class MempoolCoinbaseTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 - self.setup_clean_chain = True def run_test(self): node = self.nodes[0] wallet = MiniWallet(node) - # Add enough mature utxos to the wallet so that all txs spend confirmed - # coins - self.generate(wallet, 3) - self.generate(node, 100) - # Spend block 1/2/3's coinbase transactions # Mine a block # Create three more transactions, spending the spends # Mine another block # ... make sure all the transactions are confirmed # Invalidate both blocks # ... make sure all the transactions are put back in the mempool # Mine a new block # ... make sure all the transactions are confirmed again blocks = [] spends1_ids = [ wallet.send_self_transfer(from_node=node)["txid"] for _ in range(3) ] blocks.extend(self.generate(node, 1)) spends2_ids = [ wallet.send_self_transfer(from_node=node)["txid"] for _ in range(3) ] blocks.extend(self.generate(node, 1)) spends_ids = set(spends1_ids + spends2_ids) # mempool should be empty, all txns confirmed assert_equal(set(node.getrawmempool()), set()) confirmed_txns = set( node.getblock(blocks[0])["tx"] + node.getblock(blocks[1])["tx"] ) # Checks that all spend txns are contained in the mined blocks assert spends_ids < confirmed_txns # Use invalidateblock to re-org back node.invalidateblock(blocks[0]) # All txns should be back in mempool with 0 confirmations assert_equal(set(node.getrawmempool()), spends_ids) # Generate another block, they should all get mined blocks = self.generate(node, 1) # mempool should be empty, all txns confirmed assert_equal(set(node.getrawmempool()), set()) confirmed_txns = set(node.getblock(blocks[0])["tx"]) assert spends_ids < confirmed_txns if __name__ == "__main__": MempoolCoinbaseTest().main() diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py index 833ca4dc5..7d13fe9a9 100644 --- a/test/functional/rpc_net.py +++ b/test/functional/rpc_net.py @@ -1,390 +1,387 @@ # Copyright (c) 2017 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test RPC calls related to net. Tests correspond to code in rpc/net.cpp. """ import time from decimal import Decimal from itertools import product import test_framework.messages from test_framework.avatools import create_coinbase_stakes from test_framework.key import ECKey from test_framework.messages import NODE_NETWORK from test_framework.p2p import P2PInterface from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_approx, assert_equal, assert_greater_than, assert_raises_rpc_error, p2p_port, ) from test_framework.wallet_util import bytes_to_wif def assert_net_servicesnames(servicesflag, servicenames): """Utility that checks if all flags are correctly decoded in `getpeerinfo` and `getnetworkinfo`. :param servicesflag: The services as an integer. :param servicenames: The list of decoded services names, as strings. """ servicesflag_generated = 0 for servicename in servicenames: servicesflag_generated |= getattr( test_framework.messages, f"NODE_{servicename}" ) assert servicesflag_generated == servicesflag class NetTest(BitcoinTestFramework): def set_test_params(self): - self.setup_clean_chain = True self.num_nodes = 2 self.extra_args = [ [ "-avaproofstakeutxodustthreshold=1000000", "-avaproofstakeutxoconfirmations=1", "-minrelaytxfee=10", ], [ "-avaproofstakeutxodustthreshold=1000000", "-avaproofstakeutxoconfirmations=1", "-minrelaytxfee=5", ], ] self.supports_cli = False def run_test(self): - # Get out of IBD for the minfeefilter and getpeerinfo tests. - self.generate(self.nodes[0], 101) # Connect nodes both ways. self.connect_nodes(0, 1) self.connect_nodes(1, 0) self.sync_all() self.test_connection_count() self.test_getpeerinfo() self.test_getnettotals() self.test_getnetworkinfo() self.test_getaddednodeinfo() self.test_service_flags() self.test_getnodeaddresses() self.test_addpeeraddress() def test_connection_count(self): self.log.info("Test getconnectioncount") # After using `connect_nodes` to connect nodes 0 and 1 to each other. assert_equal(self.nodes[0].getconnectioncount(), 2) def test_getnettotals(self): self.log.info("Test getnettotals") # Test getnettotals and getpeerinfo by doing a ping. The bytes # sent/received should increase by at least the size of one ping (32 # bytes) and one pong (32 bytes). net_totals_before = self.nodes[0].getnettotals() peer_info_before = self.nodes[0].getpeerinfo() self.nodes[0].ping() self.wait_until( lambda: self.nodes[0].getnettotals()["totalbytessent"] >= net_totals_before["totalbytessent"] + 32 * 2, timeout=10, ) self.wait_until( lambda: self.nodes[0].getnettotals()["totalbytesrecv"] >= net_totals_before["totalbytesrecv"] + 32 * 2, timeout=10, ) for peer_before in peer_info_before: def peer_after(): return next( p for p in self.nodes[0].getpeerinfo() if p["id"] == peer_before["id"] ) self.wait_until( lambda: peer_after()["bytesrecv_per_msg"].get("pong", 0) >= peer_before["bytesrecv_per_msg"].get("pong", 0) + 32, timeout=10, ) self.wait_until( lambda: peer_after()["bytessent_per_msg"].get("ping", 0) >= peer_before["bytessent_per_msg"].get("ping", 0) + 32, timeout=10, ) def test_getnetworkinfo(self): self.log.info("Test getnetworkinfo") info = self.nodes[0].getnetworkinfo() assert_equal(info["networkactive"], True) assert_equal(info["connections"], 2) assert_equal(info["connections_in"], 1) assert_equal(info["connections_out"], 1) with self.nodes[0].assert_debug_log( expected_msgs=["SetNetworkActive: false\n"] ): self.nodes[0].setnetworkactive(state=False) assert_equal(self.nodes[0].getnetworkinfo()["networkactive"], False) # Wait a bit for all sockets to close self.wait_until( lambda: self.nodes[0].getnetworkinfo()["connections"] == 0, timeout=3 ) with self.nodes[0].assert_debug_log(expected_msgs=["SetNetworkActive: true\n"]): self.nodes[0].setnetworkactive(state=True) # Connect nodes both ways. self.connect_nodes(0, 1) self.connect_nodes(1, 0) info = self.nodes[0].getnetworkinfo() assert_equal(info["networkactive"], True) assert_equal(info["connections"], 2) assert_equal(info["connections_in"], 1) assert_equal(info["connections_out"], 1) # check the `servicesnames` field network_info = [node.getnetworkinfo() for node in self.nodes] for info in network_info: assert_net_servicesnames( int(info["localservices"], 0x10), info["localservicesnames"] ) # Check dynamically generated networks list in getnetworkinfo help # output. assert "(ipv4, ipv6, onion, i2p)" in self.nodes[0].help("getnetworkinfo") def test_getaddednodeinfo(self): self.log.info("Test getaddednodeinfo") assert_equal(self.nodes[0].getaddednodeinfo(), []) # add a node (node2) to node0 ip_port = f"127.0.0.1:{p2p_port(2)}" self.nodes[0].addnode(node=ip_port, command="add") # check that the node has indeed been added added_nodes = self.nodes[0].getaddednodeinfo(ip_port) assert_equal(len(added_nodes), 1) assert_equal(added_nodes[0]["addednode"], ip_port) # check that node cannot be added again assert_raises_rpc_error( -23, "Node already added", self.nodes[0].addnode, node=ip_port, command="add", ) # check that node can be removed self.nodes[0].addnode(node=ip_port, command="remove") assert_equal(self.nodes[0].getaddednodeinfo(), []) # check that trying to remove the node again returns an error assert_raises_rpc_error( -24, "Node could not be removed", self.nodes[0].addnode, node=ip_port, command="remove", ) # check that a non-existent node returns an error assert_raises_rpc_error( -24, "Node has not been added", self.nodes[0].getaddednodeinfo, "1.1.1.1" ) def test_getpeerinfo(self): self.log.info("Test getpeerinfo") # Create a few getpeerinfo last_block/last_transaction/last_proof # values. if self.is_wallet_compiled(): self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1000000) tip = self.generate(self.nodes[1], 1)[0] self.sync_all() stake = create_coinbase_stakes( self.nodes[1], [tip], self.nodes[1].get_deterministic_priv_key().key ) privkey = ECKey() privkey.generate() proof = self.nodes[1].buildavalancheproof( 42, 2000000000, bytes_to_wif(privkey.get_bytes()), stake ) self.nodes[1].sendavalancheproof(proof) self.sync_proofs() time_now = int(time.time()) peer_info = [x.getpeerinfo() for x in self.nodes] # Verify last_block, last_transaction and last_proof keys/values. for node, peer, field in product( range(self.num_nodes), range(2), ["last_block", "last_transaction", "last_proof"], ): assert field in peer_info[node][peer].keys() if peer_info[node][peer][field] != 0: assert_approx(peer_info[node][peer][field], time_now, vspan=60) # check both sides of bidirectional connection between nodes # the address bound to on one side will be the source address for the # other node assert_equal(peer_info[0][0]["addrbind"], peer_info[1][0]["addr"]) assert_equal(peer_info[1][0]["addrbind"], peer_info[0][0]["addr"]) assert_equal(peer_info[0][0]["minfeefilter"], Decimal("5.00")) assert_equal(peer_info[1][0]["minfeefilter"], Decimal("10.00")) # check the `servicesnames` field for info in peer_info: assert_net_servicesnames( int(info[0]["services"], 0x10), info[0]["servicesnames"] ) assert_equal(peer_info[0][0]["connection_type"], "inbound") assert_equal(peer_info[0][1]["connection_type"], "manual") assert_equal(peer_info[1][0]["connection_type"], "manual") assert_equal(peer_info[1][1]["connection_type"], "inbound") # Check dynamically generated networks list in getpeerinfo help output. assert "(ipv4, ipv6, onion, i2p, not_publicly_routable)" in self.nodes[0].help( "getpeerinfo" ) # Node state fields for node, peer, field in product( range(self.num_nodes), range(2), ["startingheight", "synced_headers", "synced_blocks", "inflight"], ): assert field in peer_info[node][peer].keys() def test_service_flags(self): self.log.info("Test service flags") self.nodes[0].add_p2p_connection(P2PInterface(), services=(1 << 5) | (1 << 63)) assert_equal( ["UNKNOWN[2^5]", "UNKNOWN[2^63]"], self.nodes[0].getpeerinfo()[-1]["servicesnames"], ) self.nodes[0].disconnect_p2ps() def test_getnodeaddresses(self): self.log.info("Test getnodeaddresses") self.nodes[0].add_p2p_connection(P2PInterface()) # Add an IPv6 address to the address manager. ipv6_addr = "1233:3432:2434:2343:3234:2345:6546:4534" self.nodes[0].addpeeraddress(address=ipv6_addr, port=8333) # Add 10,000 IPv4 addresses to the address manager. Due to the way bucket # and bucket positions are calculated, some of these addresses will # collide. imported_addrs = [] for i in range(10000): first_octet = i >> 8 second_octet = i % 256 a = f"{first_octet}.{second_octet}.1.1" imported_addrs.append(a) self.nodes[0].addpeeraddress(a, 8333) # Fetch the addresses via the RPC and test the results. # default count is 1 assert_equal(len(self.nodes[0].getnodeaddresses()), 1) assert_equal(len(self.nodes[0].getnodeaddresses(count=2)), 2) assert_equal(len(self.nodes[0].getnodeaddresses(network="ipv4", count=8)), 8) # Maximum possible addresses in AddrMan is 10000. The actual number will # usually be less due to bucket and bucket position collisions. node_addresses = self.nodes[0].getnodeaddresses(0, "ipv4") assert_greater_than(len(node_addresses), 5000) assert_greater_than(10000, len(node_addresses)) for a in node_addresses: assert_greater_than(a["time"], 1527811200) # 1st June 2018 assert_equal(a["services"], NODE_NETWORK) assert a["address"] in imported_addrs assert_equal(a["port"], 8333) assert_equal(a["network"], "ipv4") # Test the IPv6 address. res = self.nodes[0].getnodeaddresses(0, "ipv6") assert_equal(len(res), 1) assert_equal(res[0]["address"], ipv6_addr) assert_equal(res[0]["network"], "ipv6") assert_equal(res[0]["port"], 8333) assert_equal(res[0]["services"], NODE_NETWORK) # Test for the absence of onion and I2P addresses. for network in ["onion", "i2p"]: assert_equal(self.nodes[0].getnodeaddresses(0, network), []) # Test invalid arguments. assert_raises_rpc_error( -8, "Address count out of range", self.nodes[0].getnodeaddresses, -1 ) assert_raises_rpc_error( -8, "Network not recognized: Foo", self.nodes[0].getnodeaddresses, 1, "Foo" ) def test_addpeeraddress(self): """RPC addpeeraddress sets the source address equal to the destination address. If an address with the same /16 as an existing new entry is passed, it will be placed in the same new bucket and have a 1/64 chance of the bucket positions colliding (depending on the value of nKey in the addrman), in which case the new address won't be added. The probability of collision can be reduced to 1/2^16 = 1/65536 by using an address from a different /16. We avoid this here by first testing adding a tried table entry before testing adding a new table one. """ self.log.info("Test addpeeraddress") self.restart_node(1, ["-checkaddrman=1"]) node = self.nodes[1] self.log.debug("Test that addpeerinfo is a hidden RPC") # It is hidden from general help, but its detailed help may be called # directly. assert "addpeerinfo" not in node.help() assert "addpeerinfo" in node.help("addpeerinfo") self.log.debug("Test that adding an empty address fails") assert_equal(node.addpeeraddress(address="", port=8333), {"success": False}) assert_equal(node.getnodeaddresses(count=0), []) self.log.debug("Test that adding a valid address to the tried table succeeds") assert_equal( node.addpeeraddress(address="1.2.3.4", tried=True, port=8333), {"success": True}, ) with node.assert_debug_log( expected_msgs=["CheckAddrman: new 0, tried 1, total 1 started"] ): # getnodeaddresses re-runs the addrman checks addrs = node.getnodeaddresses(count=0) assert_equal(len(addrs), 1) assert_equal(addrs[0]["address"], "1.2.3.4") assert_equal(addrs[0]["port"], 8333) self.log.debug( "Test that adding an already-present tried address to the new and tried" " tables fails" ) for value in [True, False]: assert_equal( node.addpeeraddress(address="1.2.3.4", tried=value, port=8333), {"success": False}, ) assert_equal(len(node.getnodeaddresses(count=0)), 1) self.log.debug( "Test that adding a second address, this time to the new table, succeeds" ) assert_equal( node.addpeeraddress(address="2.0.0.0", port=8333), {"success": True} ) with node.assert_debug_log( expected_msgs=["CheckAddrman: new 1, tried 1, total 2 started"] ): # getnodeaddresses re-runs the addrman checks addrs = node.getnodeaddresses(count=0) assert_equal(len(addrs), 2) if __name__ == "__main__": NetTest().main() diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py index 0f4cc62e4..c6b512bf8 100644 --- a/test/functional/rpc_rawtransaction.py +++ b/test/functional/rpc_rawtransaction.py @@ -1,962 +1,952 @@ # Copyright (c) 2014-2019 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test the rawtranscation RPCs. Test the following RPCs: - createrawtransaction - signrawtransactionwithwallet - sendrawtransaction - decoderawtransaction - getrawtransaction """ from collections import OrderedDict from decimal import Decimal from io import BytesIO from test_framework.messages import COutPoint, CTransaction, CTxIn, CTxOut, ToHex from test_framework.script import CScript from test_framework.test_framework import BitcoinTestFramework from test_framework.txtools import pad_raw_tx from test_framework.util import ( assert_equal, assert_greater_than, assert_raises_rpc_error, find_vout_for_address, ) class multidict(dict): """Dictionary that allows duplicate keys. Constructed with a list of (key, value) tuples. When dumped by the json module, will output invalid json with repeated keys, eg: >>> json.dumps(multidict([(1,2),(1,2)]) '{"1": 2, "1": 2}' Used to test calls to rpc methods with repeated keys in the json object.""" def __init__(self, x): dict.__init__(self, x) self.x = x def items(self): return self.x # Create one-input, one-output, no-fee transaction: class RawTransactionsTest(BitcoinTestFramework): def set_test_params(self): - self.setup_clean_chain = True self.num_nodes = 3 self.extra_args = [["-txindex"], ["-txindex"], ["-txindex"]] # whitelist all peers to speed up tx relay / mempool sync for args in self.extra_args: args.append("-whitelist=noban@127.0.0.1") self.supports_cli = False def skip_test_if_missing_module(self): self.skip_if_no_wallet() def setup_network(self): super().setup_network() self.connect_nodes(0, 2) def run_test(self): - self.log.info("prepare some coins for multiple *rawtransaction commands") - self.generate(self.nodes[2], 1) - self.generate(self.nodes[0], 101) - self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1500000) - self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1000000) - self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 5000000) - self.sync_all() - self.generate(self.nodes[0], 5) - self.log.info( "Test getrawtransaction on genesis block coinbase returns an error" ) block = self.nodes[0].getblock(self.nodes[0].getblockhash(0)) assert_raises_rpc_error( -5, "The genesis block coinbase is not considered an ordinary transaction", self.nodes[0].getrawtransaction, block["merkleroot"], ) self.log.info( "Check parameter types and required parameters of createrawtransaction" ) # Test `createrawtransaction` required parameters assert_raises_rpc_error( -1, "createrawtransaction", self.nodes[0].createrawtransaction ) assert_raises_rpc_error( -1, "createrawtransaction", self.nodes[0].createrawtransaction, [] ) # Test `createrawtransaction` invalid extra parameters assert_raises_rpc_error( -1, "createrawtransaction", self.nodes[0].createrawtransaction, [], {}, 0, "foo", ) # Test `createrawtransaction` invalid `inputs` txid = "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000" assert_raises_rpc_error( -3, "Expected type array", self.nodes[0].createrawtransaction, "foo", {} ) assert_raises_rpc_error( -1, "JSON value is not an object as expected", self.nodes[0].createrawtransaction, ["foo"], {}, ) assert_raises_rpc_error( -1, "JSON value is not a string as expected", self.nodes[0].createrawtransaction, [{}], {}, ) assert_raises_rpc_error( -8, "txid must be of length 64 (not 3, for 'foo')", self.nodes[0].createrawtransaction, [{"txid": "foo"}], {}, ) assert_raises_rpc_error( -8, "txid must be hexadecimal string (not" " 'ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844')", self.nodes[0].createrawtransaction, [ { "txid": "ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844" } ], {}, ) assert_raises_rpc_error( -8, "Invalid parameter, missing vout key", self.nodes[0].createrawtransaction, [{"txid": txid}], {}, ) assert_raises_rpc_error( -8, "Invalid parameter, vout must be a number", self.nodes[0].createrawtransaction, [{"txid": txid, "vout": "foo"}], {}, ) assert_raises_rpc_error( -8, "Invalid parameter, vout cannot be negative", self.nodes[0].createrawtransaction, [{"txid": txid, "vout": -1}], {}, ) assert_raises_rpc_error( -8, "Invalid parameter, sequence number is out of range", self.nodes[0].createrawtransaction, [{"txid": txid, "vout": 0, "sequence": -1}], {}, ) # Test `createrawtransaction` invalid `outputs` address = self.nodes[0].getnewaddress() address2 = self.nodes[0].getnewaddress() assert_raises_rpc_error( -1, "JSON value is not an array as expected", self.nodes[0].createrawtransaction, [], "foo", ) # Should not throw for backwards compatibility self.nodes[0].createrawtransaction(inputs=[], outputs={}) self.nodes[0].createrawtransaction(inputs=[], outputs=[]) assert_raises_rpc_error( -8, "Data must be hexadecimal string", self.nodes[0].createrawtransaction, [], {"data": "foo"}, ) assert_raises_rpc_error( -5, "Invalid Bitcoin address", self.nodes[0].createrawtransaction, [], {"foo": 0}, ) assert_raises_rpc_error( -3, "Invalid amount", self.nodes[0].createrawtransaction, [], {address: "foo"}, ) assert_raises_rpc_error( -3, "Amount out of range", self.nodes[0].createrawtransaction, [], {address: -1}, ) assert_raises_rpc_error( -8, f"Invalid parameter, duplicated address: {address}", self.nodes[0].createrawtransaction, [], multidict([(address, 1), (address, 1)]), ) assert_raises_rpc_error( -8, f"Invalid parameter, duplicated address: {address}", self.nodes[0].createrawtransaction, [], [{address: 1}, {address: 1}], ) assert_raises_rpc_error( -8, "Invalid parameter, duplicate key: data", self.nodes[0].createrawtransaction, [], [{"data": "aa"}, {"data": "bb"}], ) assert_raises_rpc_error( -8, "Invalid parameter, duplicate key: data", self.nodes[0].createrawtransaction, [], multidict([("data", "aa"), ("data", "bb")]), ) assert_raises_rpc_error( -8, "Invalid parameter, key-value pair must contain exactly one key", self.nodes[0].createrawtransaction, [], [{"a": 1, "b": 2}], ) assert_raises_rpc_error( -8, "Invalid parameter, key-value pair not an object as expected", self.nodes[0].createrawtransaction, [], [["key-value pair1"], ["2"]], ) # Test `createrawtransaction` invalid `locktime` assert_raises_rpc_error( -3, "Expected type number", self.nodes[0].createrawtransaction, [], {}, "foo", ) assert_raises_rpc_error( -8, "Invalid parameter, locktime out of range", self.nodes[0].createrawtransaction, [], {}, -1, ) assert_raises_rpc_error( -8, "Invalid parameter, locktime out of range", self.nodes[0].createrawtransaction, [], {}, 4294967296, ) self.log.info( "Check that createrawtransaction accepts an array and object as outputs" ) tx = CTransaction() # One output tx.deserialize( BytesIO( bytes.fromhex( self.nodes[2].createrawtransaction( inputs=[{"txid": txid, "vout": 9}], outputs={address: 99} ) ) ) ) assert_equal(len(tx.vout), 1) assert_equal( tx.serialize().hex(), self.nodes[2].createrawtransaction( inputs=[{"txid": txid, "vout": 9}], outputs=[{address: 99}] ), ) # Two outputs tx.deserialize( BytesIO( bytes.fromhex( self.nodes[2].createrawtransaction( inputs=[{"txid": txid, "vout": 9}], outputs=OrderedDict([(address, 99), (address2, 99)]), ) ) ) ) assert_equal(len(tx.vout), 2) assert_equal( tx.serialize().hex(), self.nodes[2].createrawtransaction( inputs=[{"txid": txid, "vout": 9}], outputs=[{address: 99}, {address2: 99}], ), ) # Multiple mixed outputs tx.deserialize( BytesIO( bytes.fromhex( self.nodes[2].createrawtransaction( inputs=[{"txid": txid, "vout": 9}], outputs=multidict( [(address, 99), (address2, 99), ("data", "99")] ), ) ) ) ) assert_equal(len(tx.vout), 3) assert_equal( tx.serialize().hex(), self.nodes[2].createrawtransaction( inputs=[{"txid": txid, "vout": 9}], outputs=[{address: 99}, {address2: 99}, {"data": "99"}], ), ) for addr_type in ["legacy"]: addr = self.nodes[0].getnewaddress("", addr_type) addrinfo = self.nodes[0].getaddressinfo(addr) pubkey = addrinfo["scriptPubKey"] self.log.info(f"sendrawtransaction with missing prevtx info ({addr_type})") # Test `signrawtransactionwithwallet` invalid `prevtxs` inputs = [{"txid": txid, "vout": 3, "sequence": 1000}] outputs = {self.nodes[0].getnewaddress(): 1} rawtx = self.nodes[0].createrawtransaction(inputs, outputs) prevtx = {"txid": txid, "scriptPubKey": pubkey, "vout": 3, "amount": 1} succ = self.nodes[0].signrawtransactionwithwallet(rawtx, [prevtx]) assert succ["complete"] assert_raises_rpc_error( -8, "Missing amount", self.nodes[0].signrawtransactionwithwallet, rawtx, [ { "txid": txid, "scriptPubKey": pubkey, "vout": 3, } ], ) assert_raises_rpc_error( -3, "Missing vout", self.nodes[0].signrawtransactionwithwallet, rawtx, [ { "txid": txid, "scriptPubKey": pubkey, "amount": 1, } ], ) assert_raises_rpc_error( -3, "Missing txid", self.nodes[0].signrawtransactionwithwallet, rawtx, [ { "scriptPubKey": pubkey, "vout": 3, "amount": 1, } ], ) assert_raises_rpc_error( -3, "Missing scriptPubKey", self.nodes[0].signrawtransactionwithwallet, rawtx, [{"txid": txid, "vout": 3, "amount": 1}], ) ######################################### # sendrawtransaction with missing input # ######################################### self.log.info("sendrawtransaction with missing input") # won't exists inputs = [ { "txid": ( "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000" ), "vout": 1, } ] outputs = {self.nodes[0].getnewaddress(): 4998000} rawtx = self.nodes[2].createrawtransaction(inputs, outputs) rawtx = pad_raw_tx(rawtx) rawtx = self.nodes[2].signrawtransactionwithwallet(rawtx) # This will raise an exception since there are missing inputs assert_raises_rpc_error( -25, "bad-txns-inputs-missingorspent", self.nodes[2].sendrawtransaction, rawtx["hex"], ) ##################################### # getrawtransaction with block hash # ##################################### # make a tx by sending then generate 2 blocks; block1 has the tx in it tx = self.nodes[2].sendtoaddress(self.nodes[1].getnewaddress(), 1000000) block1, block2 = self.generate(self.nodes[2], 2) self.sync_all() # We should be able to get the raw transaction by providing the correct # block gottx = self.nodes[0].getrawtransaction(tx, True, block1) assert_equal(gottx["txid"], tx) assert_equal(gottx["in_active_chain"], True) # We should not have the 'in_active_chain' flag when we don't provide a # block gottx = self.nodes[0].getrawtransaction(tx, True) assert_equal(gottx["txid"], tx) assert "in_active_chain" not in gottx # We should not get the tx if we provide an unrelated block assert_raises_rpc_error( -5, "No such transaction found", self.nodes[0].getrawtransaction, tx, True, block2, ) # An invalid block hash should raise the correct errors assert_raises_rpc_error( -1, "JSON value is not a string as expected", self.nodes[0].getrawtransaction, tx, True, True, ) assert_raises_rpc_error( -8, "parameter 3 must be of length 64 (not 6, for 'foobar')", self.nodes[0].getrawtransaction, tx, True, "foobar", ) assert_raises_rpc_error( -8, "parameter 3 must be of length 64 (not 8, for 'abcd1234')", self.nodes[0].getrawtransaction, tx, True, "abcd1234", ) assert_raises_rpc_error( -8, "parameter 3 must be hexadecimal string (not" " 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", self.nodes[0].getrawtransaction, tx, True, "ZZZ0000000000000000000000000000000000000000000000000000000000000", ) assert_raises_rpc_error( -5, "Block hash not found", self.nodes[0].getrawtransaction, tx, True, "0000000000000000000000000000000000000000000000000000000000000000", ) # Undo the blocks and check in_active_chain self.nodes[0].invalidateblock(block1) gottx = self.nodes[0].getrawtransaction(txid=tx, verbose=True, blockhash=block1) assert_equal(gottx["in_active_chain"], False) self.nodes[0].reconsiderblock(block1) assert_equal(self.nodes[0].getbestblockhash(), block2) if not self.options.descriptors: # The traditional multisig workflow does not work with descriptor # wallets so these are legacy only. # The multisig workflow with descriptor wallets uses PSBTs and is # tested elsewhere, no need to do them here. # # RAW TX MULTISIG TESTS # # # 2of2 test addr1 = self.nodes[2].getnewaddress() addr2 = self.nodes[2].getnewaddress() addr1Obj = self.nodes[2].getaddressinfo(addr1) addr2Obj = self.nodes[2].getaddressinfo(addr2) # Tests for createmultisig and addmultisigaddress assert_raises_rpc_error( -5, "Invalid public key", self.nodes[0].createmultisig, 1, ["01020304"] ) # createmultisig can only take public keys self.nodes[0].createmultisig(2, [addr1Obj["pubkey"], addr2Obj["pubkey"]]) # addmultisigaddress can take both pubkeys and addresses so long as # they are in the wallet, which is tested here. assert_raises_rpc_error( -5, "Invalid public key", self.nodes[0].createmultisig, 2, [addr1Obj["pubkey"], addr1], ) mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj["pubkey"], addr1])[ "address" ] # use balance deltas instead of absolute values bal = self.nodes[2].getbalance() # send 1,200,000 XEC to msig adr txId = self.nodes[0].sendtoaddress(mSigObj, 1200000) self.sync_all() self.generate(self.nodes[0], 1) self.sync_all() # node2 has both keys of the 2of2 ms addr., tx should affect the # balance assert_equal(self.nodes[2].getbalance(), bal + Decimal("1200000.00")) # 2of3 test from different nodes bal = self.nodes[2].getbalance() addr1 = self.nodes[1].getnewaddress() addr2 = self.nodes[2].getnewaddress() addr3 = self.nodes[2].getnewaddress() addr1Obj = self.nodes[1].getaddressinfo(addr1) addr2Obj = self.nodes[2].getaddressinfo(addr2) addr3Obj = self.nodes[2].getaddressinfo(addr3) mSigObj = self.nodes[2].addmultisigaddress( 2, [addr1Obj["pubkey"], addr2Obj["pubkey"], addr3Obj["pubkey"]] )["address"] txId = self.nodes[0].sendtoaddress(mSigObj, 2200000) decTx = self.nodes[0].gettransaction(txId) rawTx = self.nodes[0].decoderawtransaction(decTx["hex"]) self.sync_all() self.generate(self.nodes[0], 1) self.sync_all() # THIS IS AN INCOMPLETE FEATURE # NODE2 HAS TWO OF THREE KEY AND THE FUNDS SHOULD BE SPENDABLE AND # COUNT AT BALANCE CALCULATION # for now, assume the funds of a 2of3 multisig tx are not marked as # spendable assert_equal(self.nodes[2].getbalance(), bal) txDetails = self.nodes[0].gettransaction(txId, True) rawTx = self.nodes[0].decoderawtransaction(txDetails["hex"]) vout = next(o for o in rawTx["vout"] if o["value"] == Decimal("2200000.00")) bal = self.nodes[0].getbalance() inputs = [ { "txid": txId, "vout": vout["n"], "scriptPubKey": vout["scriptPubKey"]["hex"], "amount": vout["value"], } ] outputs = {self.nodes[0].getnewaddress(): 2190000} rawTx = self.nodes[2].createrawtransaction(inputs, outputs) rawTxPartialSigned = self.nodes[1].signrawtransactionwithwallet( rawTx, inputs ) # node1 only has one key, can't comp. sign the tx assert_equal(rawTxPartialSigned["complete"], False) rawTxSigned = self.nodes[2].signrawtransactionwithwallet(rawTx, inputs) # node2 can sign the tx compl., own two of three keys assert_equal(rawTxSigned["complete"], True) self.nodes[2].sendrawtransaction(rawTxSigned["hex"]) rawTx = self.nodes[0].decoderawtransaction(rawTxSigned["hex"]) self.sync_all() self.generate(self.nodes[0], 1) self.sync_all() assert_equal( self.nodes[0].getbalance(), bal + Decimal("50000000.00") + Decimal("2190000.00"), ) # block reward + tx rawTxBlock = self.nodes[0].getblock(self.nodes[0].getbestblockhash()) # 2of2 test for combining transactions bal = self.nodes[2].getbalance() addr1 = self.nodes[1].getnewaddress() addr2 = self.nodes[2].getnewaddress() addr1Obj = self.nodes[1].getaddressinfo(addr1) addr2Obj = self.nodes[2].getaddressinfo(addr2) self.nodes[1].addmultisigaddress( 2, [addr1Obj["pubkey"], addr2Obj["pubkey"]] )["address"] mSigObj = self.nodes[2].addmultisigaddress( 2, [addr1Obj["pubkey"], addr2Obj["pubkey"]] )["address"] mSigObjValid = self.nodes[2].getaddressinfo(mSigObj) txId = self.nodes[0].sendtoaddress(mSigObj, 2200000) decTx = self.nodes[0].gettransaction(txId) rawTx2 = self.nodes[0].decoderawtransaction(decTx["hex"]) self.sync_all() self.generate(self.nodes[0], 1) self.sync_all() # the funds of a 2of2 multisig tx should not be marked as spendable assert_equal(self.nodes[2].getbalance(), bal) txDetails = self.nodes[0].gettransaction(txId, True) rawTx2 = self.nodes[0].decoderawtransaction(txDetails["hex"]) vout = next( o for o in rawTx2["vout"] if o["value"] == Decimal("2200000.00") ) bal = self.nodes[0].getbalance() inputs = [ { "txid": txId, "vout": vout["n"], "scriptPubKey": vout["scriptPubKey"]["hex"], "redeemScript": mSigObjValid["hex"], "amount": vout["value"], } ] outputs = {self.nodes[0].getnewaddress(): 2190000} rawTx2 = self.nodes[2].createrawtransaction(inputs, outputs) rawTxPartialSigned1 = self.nodes[1].signrawtransactionwithwallet( rawTx2, inputs ) self.log.debug(rawTxPartialSigned1) # node1 only has one key, can't comp. sign the tx assert_equal(rawTxPartialSigned1["complete"], False) rawTxPartialSigned2 = self.nodes[2].signrawtransactionwithwallet( rawTx2, inputs ) self.log.debug(rawTxPartialSigned2) # node2 only has one key, can't comp. sign the tx assert_equal(rawTxPartialSigned2["complete"], False) rawTxComb = self.nodes[2].combinerawtransaction( [rawTxPartialSigned1["hex"], rawTxPartialSigned2["hex"]] ) self.log.debug(rawTxComb) self.nodes[2].sendrawtransaction(rawTxComb) rawTx2 = self.nodes[0].decoderawtransaction(rawTxComb) self.sync_all() self.generate(self.nodes[0], 1) self.sync_all() # block reward + tx assert_equal( self.nodes[0].getbalance(), bal + Decimal("50000000.00") + Decimal("2190000.00"), ) # Sanity checks on verbose getrawtransaction output txId = rawTx["txid"] rawTxOutput = self.nodes[0].getrawtransaction(txId, True) assert_equal(rawTxOutput["hex"], rawTxSigned["hex"]) assert_equal(rawTxOutput["txid"], txId) assert_equal(rawTxOutput["hash"], txId) assert_greater_than(rawTxOutput["size"], 300) assert_equal(rawTxOutput["version"], 0x02) assert_equal(rawTxOutput["locktime"], 0) assert_equal(len(rawTxOutput["vin"]), 1) assert_equal(len(rawTxOutput["vout"]), 1) assert_equal(rawTxOutput["blockhash"], rawTxBlock["hash"]) assert_equal(rawTxOutput["confirmations"], 3) assert_equal(rawTxOutput["time"], rawTxBlock["time"]) assert_equal(rawTxOutput["blocktime"], rawTxBlock["time"]) # Basic signrawtransaction test addr = self.nodes[1].getnewaddress() txid = self.nodes[0].sendtoaddress(addr, 10_000_000) self.generate(self.nodes[0], 1) self.sync_all() vout = find_vout_for_address(self.nodes[1], txid, addr) rawTx = self.nodes[1].createrawtransaction( [{"txid": txid, "vout": vout}], {self.nodes[1].getnewaddress(): 9_999_000} ) rawTxSigned = self.nodes[1].signrawtransactionwithwallet(rawTx) txId = self.nodes[1].sendrawtransaction(rawTxSigned["hex"]) self.generate(self.nodes[0], 1) self.sync_all() # getrawtransaction tests # 1. valid parameters - only supply txid assert_equal(self.nodes[0].getrawtransaction(txId), rawTxSigned["hex"]) # 2. valid parameters - supply txid and 0 for non-verbose assert_equal(self.nodes[0].getrawtransaction(txId, 0), rawTxSigned["hex"]) # 3. valid parameters - supply txid and False for non-verbose assert_equal(self.nodes[0].getrawtransaction(txId, False), rawTxSigned["hex"]) # 4. valid parameters - supply txid and 1 for verbose. # We only check the "hex" field of the output so we don't need to # update this test every time the output format changes. assert_equal( self.nodes[0].getrawtransaction(txId, 1)["hex"], rawTxSigned["hex"] ) # 5. valid parameters - supply txid and True for non-verbose assert_equal( self.nodes[0].getrawtransaction(txId, True)["hex"], rawTxSigned["hex"] ) # 6. invalid parameters - supply txid and string "Flase" assert_raises_rpc_error( -1, "not a boolean", self.nodes[0].getrawtransaction, txId, "Flase" ) # 7. invalid parameters - supply txid and empty array assert_raises_rpc_error( -1, "not a boolean", self.nodes[0].getrawtransaction, txId, [] ) # 8. invalid parameters - supply txid and empty dict assert_raises_rpc_error( -1, "not a boolean", self.nodes[0].getrawtransaction, txId, {} ) inputs = [ { "txid": ( "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000" ), "sequence": 1000, } ] outputs = {self.nodes[0].getnewaddress(): 1} assert_raises_rpc_error( -8, "Invalid parameter, missing vout key", self.nodes[0].createrawtransaction, inputs, outputs, ) inputs[0]["vout"] = "1" assert_raises_rpc_error( -8, "Invalid parameter, vout must be a number", self.nodes[0].createrawtransaction, inputs, outputs, ) inputs[0]["vout"] = -1 assert_raises_rpc_error( -8, "Invalid parameter, vout cannot be negative", self.nodes[0].createrawtransaction, inputs, outputs, ) inputs[0]["vout"] = 1 rawtx = self.nodes[0].createrawtransaction(inputs, outputs) decrawtx = self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx["vin"][0]["sequence"], 1000) # 9. invalid parameters - sequence number out of range inputs[0]["sequence"] = -1 assert_raises_rpc_error( -8, "Invalid parameter, sequence number is out of range", self.nodes[0].createrawtransaction, inputs, outputs, ) # 10. invalid parameters - sequence number out of range inputs[0]["sequence"] = 4294967296 assert_raises_rpc_error( -8, "Invalid parameter, sequence number is out of range", self.nodes[0].createrawtransaction, inputs, outputs, ) inputs[0]["sequence"] = 4294967294 rawtx = self.nodes[0].createrawtransaction(inputs, outputs) decrawtx = self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx["vin"][0]["sequence"], 4294967294) #################################### # TRANSACTION VERSION NUMBER TESTS # #################################### # Test the minimum transaction version number that fits in a signed # 32-bit integer. # As transaction version is unsigned, this should convert to its # unsigned equivalent. tx = CTransaction() tx.nVersion = -0x80000000 rawtx = ToHex(tx) decrawtx = self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx["version"], 0x80000000) # Test the maximum transaction version number that fits in a signed # 32-bit integer. tx = CTransaction() tx.nVersion = 0x7FFFFFFF rawtx = ToHex(tx) decrawtx = self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx["version"], 0x7FFFFFFF) self.log.info("sendrawtransaction/testmempoolaccept with maxfeerate") # Test a transaction with a small fee. txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1000000) rawTx = self.nodes[0].getrawtransaction(txId, True) vout = next(o for o in rawTx["vout"] if o["value"] == Decimal("1000000.00")) self.sync_all() inputs = [{"txid": txId, "vout": vout["n"]}] # Fee 10,000 satoshis, (1,000,000 - (10000 sat * 0.01 XEC/sat)) = # 999900 outputs = {self.nodes[0].getnewaddress(): Decimal("999900.00")} rawTx = self.nodes[2].createrawtransaction(inputs, outputs) rawTxSigned = self.nodes[2].signrawtransactionwithwallet(rawTx) assert_equal(rawTxSigned["complete"], True) # Fee 10,000 satoshis, ~200 b transaction, fee rate should land around 50 sat/byte = 500 XEC/kB # Thus, testmempoolaccept should reject testres = self.nodes[2].testmempoolaccept([rawTxSigned["hex"]], 500.00)[0] assert_equal(testres["allowed"], False) assert_equal(testres["reject-reason"], "max-fee-exceeded") # and sendrawtransaction should throw assert_raises_rpc_error( -25, "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)", self.nodes[2].sendrawtransaction, rawTxSigned["hex"], 10.00, ) # and the following calls should both succeed testres = self.nodes[2].testmempoolaccept(rawtxs=[rawTxSigned["hex"]])[0] assert_equal(testres["allowed"], True) self.nodes[2].sendrawtransaction(hexstring=rawTxSigned["hex"]) # Test a transaction with a large fee. txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1000000) rawTx = self.nodes[0].getrawtransaction(txId, True) vout = next(o for o in rawTx["vout"] if o["value"] == Decimal("1000000.00")) self.sync_all() inputs = [{"txid": txId, "vout": vout["n"]}] # Fee 2,000,000 satoshis, (1,000,000 - (2,000,000 sat * 0.01 XEC/sat)) = # 980000 outputs = {self.nodes[0].getnewaddress(): Decimal("980000.00")} rawTx = self.nodes[2].createrawtransaction(inputs, outputs) rawTxSigned = self.nodes[2].signrawtransactionwithwallet(rawTx) assert_equal(rawTxSigned["complete"], True) # Fee 2,000,000 satoshis, ~100 b transaction, fee rate should land around 20,000 sat/byte = 200,000 XEC/kB # Thus, testmempoolaccept should reject testres = self.nodes[2].testmempoolaccept([rawTxSigned["hex"]])[0] assert_equal(testres["allowed"], False) assert_equal(testres["reject-reason"], "max-fee-exceeded") # and sendrawtransaction should throw assert_raises_rpc_error( -25, "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)", self.nodes[2].sendrawtransaction, rawTxSigned["hex"], ) # and the following calls should both succeed testres = self.nodes[2].testmempoolaccept( rawtxs=[rawTxSigned["hex"]], maxfeerate="200000.00" )[0] assert_equal(testres["allowed"], True) self.nodes[2].sendrawtransaction( hexstring=rawTxSigned["hex"], maxfeerate="200000.00" ) self.log.info( "sendrawtransaction/testmempoolaccept with tx that is already in the chain" ) self.generate(self.nodes[2], 1) for node in self.nodes: testres = node.testmempoolaccept([rawTxSigned["hex"]])[0] assert_equal(testres["allowed"], False) assert_equal(testres["reject-reason"], "txn-already-known") assert_raises_rpc_error( -27, "Transaction already in block chain", node.sendrawtransaction, rawTxSigned["hex"], ) ########################################## # Decoding weird scripts in transactions # ########################################## self.log.info("Decode correctly-formatted but weird transactions") tx = CTransaction() # empty self.nodes[0].decoderawtransaction(ToHex(tx)) # truncated push tx.vin.append(CTxIn(COutPoint(42, 0), b"\x4e\x00\x00")) tx.vin.append(CTxIn(COutPoint(42, 0), b"\x4c\x10TRUNC")) tx.vout.append(CTxOut(0, b"\x4e\x00\x00")) tx.vout.append(CTxOut(0, b"\x4c\x10TRUNC")) self.nodes[0].decoderawtransaction(ToHex(tx)) # giant pushes and long scripts tx.vin.append(CTxIn(COutPoint(42, 0), CScript([b"giant push" * 10000]))) tx.vout.append(CTxOut(0, CScript([b"giant push" * 10000]))) self.nodes[0].decoderawtransaction(ToHex(tx)) self.log.info("Refuse garbage after transaction") assert_raises_rpc_error( -22, "TX decode failed", self.nodes[0].decoderawtransaction, f"{ToHex(tx)}00", ) if __name__ == "__main__": RawTransactionsTest().main() diff --git a/test/functional/rpc_txoutproof.py b/test/functional/rpc_txoutproof.py index 11707191f..f5736303b 100644 --- a/test/functional/rpc_txoutproof.py +++ b/test/functional/rpc_txoutproof.py @@ -1,201 +1,196 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test gettxoutproof and verifytxoutproof RPCs.""" from test_framework.messages import CMerkleBlock, FromHex, ToHex from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error from test_framework.wallet import MiniWallet class MerkleBlockTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 - self.setup_clean_chain = True self.extra_args = [ [], ["-txindex"], ] def run_test(self): miniwallet = MiniWallet(self.nodes[0]) - # Add enough mature utxos to the wallet, so that all txs spend - # confirmed coins - self.generate(miniwallet, 5) - self.generate(self.nodes[0], 100) chain_height = self.nodes[1].getblockcount() - assert_equal(chain_height, 105) + assert_equal(chain_height, 200) txid1 = miniwallet.send_self_transfer(from_node=self.nodes[0])["txid"] txid2 = miniwallet.send_self_transfer(from_node=self.nodes[0])["txid"] # This will raise an exception because the transaction is not yet in a # block assert_raises_rpc_error( -5, "Transaction not yet in block", self.nodes[0].gettxoutproof, [txid1] ) self.generate(self.nodes[0], 1) blockhash = self.nodes[0].getblockhash(chain_height + 1) txlist = [] blocktxn = self.nodes[0].getblock(blockhash, True)["tx"] txlist.append(blocktxn[1]) txlist.append(blocktxn[2]) assert_equal( self.nodes[0].verifytxoutproof(self.nodes[0].gettxoutproof([txid1])), [txid1], ) assert_equal( self.nodes[0].verifytxoutproof(self.nodes[0].gettxoutproof([txid1, txid2])), txlist, ) assert_equal( self.nodes[0].verifytxoutproof( self.nodes[0].gettxoutproof([txid1, txid2], blockhash) ), txlist, ) # Get the change from txid2 txin_spent = miniwallet.get_utxo(txid=txid2) tx3 = miniwallet.send_self_transfer( from_node=self.nodes[0], utxo_to_spend=txin_spent ) txid3 = tx3["txid"] self.generate(self.nodes[0], 1) txid_spent = txin_spent["txid"] txid_unspent = txid1 # Input was change from txid2, so txid1 should be unspent # Invalid txids assert_raises_rpc_error( -8, "txid must be of length 64 (not 32, for" " '00000000000000000000000000000000')", self.nodes[0].gettxoutproof, ["00000000000000000000000000000000"], blockhash, ) assert_raises_rpc_error( -8, "txid must be hexadecimal string (not" " 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", self.nodes[0].gettxoutproof, ["ZZZ0000000000000000000000000000000000000000000000000000000000000"], blockhash, ) # Invalid blockhashes assert_raises_rpc_error( -8, "blockhash must be of length 64 (not 32, for" " '00000000000000000000000000000000')", self.nodes[0].gettxoutproof, [txid_spent], "00000000000000000000000000000000", ) assert_raises_rpc_error( -8, "blockhash must be hexadecimal string (not" " 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", self.nodes[0].gettxoutproof, [txid_spent], "ZZZ0000000000000000000000000000000000000000000000000000000000000", ) # We can't find the block from a fully-spent tx assert_raises_rpc_error( -5, "Transaction not yet in block", self.nodes[0].gettxoutproof, [txid_spent], ) # We can get the proof if we specify the block assert_equal( self.nodes[0].verifytxoutproof( self.nodes[0].gettxoutproof([txid_spent], blockhash) ), [txid_spent], ) # We can't get the proof if we specify a non-existent block assert_raises_rpc_error( -5, "Block not found", self.nodes[0].gettxoutproof, [txid_spent], "0000000000000000000000000000000000000000000000000000000000000000", ) # We can get the proof if the transaction is unspent assert_equal( self.nodes[0].verifytxoutproof(self.nodes[0].gettxoutproof([txid_unspent])), [txid_unspent], ) # We can get the proof if we provide a list of transactions and one of # them is unspent. The ordering of the list should not matter. assert_equal( sorted( self.nodes[0].verifytxoutproof( self.nodes[0].gettxoutproof([txid1, txid2]) ) ), sorted(txlist), ) assert_equal( sorted( self.nodes[0].verifytxoutproof( self.nodes[0].gettxoutproof([txid2, txid1]) ) ), sorted(txlist), ) # We can always get a proof if we have a -txindex assert_equal( self.nodes[0].verifytxoutproof(self.nodes[1].gettxoutproof([txid_spent])), [txid_spent], ) # We can't get a proof if we specify transactions from different blocks assert_raises_rpc_error( -5, "Not all transactions found in specified or retrieved block", self.nodes[0].gettxoutproof, [txid1, txid3], ) # Test empty list assert_raises_rpc_error( -5, "Transaction not yet in block", self.nodes[0].gettxoutproof, [] ) # Test duplicate txid assert_raises_rpc_error( -8, "Invalid parameter, duplicated txid", self.nodes[0].gettxoutproof, [txid1, txid1], ) # Now we'll try tweaking a proof. proof = self.nodes[1].gettxoutproof([txid1, txid2]) assert txid1 in self.nodes[0].verifytxoutproof(proof) assert txid2 in self.nodes[1].verifytxoutproof(proof) tweaked_proof = FromHex(CMerkleBlock(), proof) # Make sure that our serialization/deserialization is working assert txid1 in self.nodes[0].verifytxoutproof(ToHex(tweaked_proof)) # Check to see if we can go up the merkle tree and pass this off as a # single-transaction block tweaked_proof.txn.nTransactions = 1 tweaked_proof.txn.vHash = [tweaked_proof.header.hashMerkleRoot] tweaked_proof.txn.vBits = [True] + [False] * 7 for n in self.nodes: assert not n.verifytxoutproof(ToHex(tweaked_proof)) # TODO: try more variants, eg transactions at different depths, and # verify that the proofs are invalid if __name__ == "__main__": MerkleBlockTest().main()