diff --git a/.arclint b/.arclint index 3226d2151..ed4e9b77e 100644 --- a/.arclint +++ b/.arclint @@ -1,341 +1,341 @@ { "linters": { "generated": { "type": "generated" }, "clang-format": { "type": "clang-format", "version": ">=12.0", "bin": [ "clang-format-12", "clang-format" ], "include": "(^(src|chronik)/.*\\.(h|c|cpp|mm)$)", "exclude": [ "(^src/(secp256k1|univalue|leveldb)/)", "(^src/bench/nanobench.h$)" ] }, "autopep8": { "type": "autopep8", "version": ">=1.3.4", "include": "(\\.py$)", "exclude": [ "(^contrib/gitian-builder/)", "(^contrib/apple-sdk-tools/)", - "(^test/functional/[a-qt].*\\.py$)" + "(^test/functional/[a-v].*\\.py$)" ], "flags": [ "--aggressive", "--ignore=W503,W504", "--max-line-length=88" ] }, "black": { "type": "black", "version": ">=23.0.0", "include": [ - "(^test/functional/[a-qt].*\\.py$)" + "(^test/functional/[a-v].*\\.py$)" ], "flags": [ "--preview" ] }, "flake8": { "type": "flake8", "version": ">=5.0", "include": "(\\.py$)", "exclude": [ "(^contrib/gitian-builder/)", "(^contrib/apple-sdk-tools/)" ], "flags": [ "--ignore=A003,E203,E303,E305,E501,E704,W503,W504", "--require-plugins=flake8-comprehensions,flake8-builtins" ] }, "lint-format-strings": { "type": "lint-format-strings", "include": "(^(src|chronik)/.*\\.(h|c|cpp)$)", "exclude": [ "(^src/(secp256k1|univalue|leveldb)/)", "(^src/bench/nanobench.h$)", "(^src/test/fuzz/strprintf.cpp$)" ] }, "check-doc": { "type": "check-doc", "include": "(^(src|chronik)/.*\\.(h|c|cpp)$)" }, "lint-tests": { "type": "lint-tests", "include": "(^src/(seeder/|rpc/|wallet/)?test/.*\\.(cpp)$)" }, "phpcs": { "type": "phpcs", "include": "(\\.php$)", "exclude": [ "(^arcanist/__phutil_library_.+\\.php$)" ], "phpcs.standard": "arcanist/phpcs.xml" }, "lint-locale-dependence": { "type": "lint-locale-dependence", "include": "(^(src|chronik)/.*\\.(h|cpp)$)", "exclude": [ "(^src/(crypto/ctaes/|leveldb/|secp256k1/|tinyformat.h|univalue/))", "(^src/bench/nanobench.h$)" ] }, "lint-cheader": { "type": "lint-cheader", "include": "(^(src|chronik)/.*\\.(h|cpp)$)", "exclude": [ "(^src/(crypto/ctaes|secp256k1|univalue|leveldb)/)", "(^src/bench/nanobench.h$)" ] }, "spelling": { "type": "spelling", "exclude": [ "(^build-aux/m4/)", "(^depends/)", "(^doc/release-notes/)", "(^contrib/gitian-builder/)", "(^src/(qt/locale|secp256k1|univalue|leveldb)/)", "(^test/lint/dictionary/)", "(package-lock.json)" ], "spelling.dictionaries": [ "test/lint/dictionary/english.json" ] }, "lint-assert-with-side-effects": { "type": "lint-assert-with-side-effects", "include": "(^(src|chronik)/.*\\.(h|cpp)$)", "exclude": [ "(^src/(secp256k1|univalue|leveldb)/)", "(^src/bench/nanobench.h$)" ] }, "lint-include-quotes": { "type": "lint-include-quotes", "include": "(^(src|chronik)/.*\\.(h|cpp)$)", "exclude": [ "(^src/(secp256k1|univalue|leveldb)/)", "(^src/bench/nanobench.h$)" ] }, "lint-include-guard": { "type": "lint-include-guard", "include": "(^(src|chronik)/.*\\.h$)", "exclude": [ "(^src/(crypto/ctaes|secp256k1|univalue|leveldb)/)", "(^src/bench/nanobench.h$)", "(^src/tinyformat.h$)" ] }, "lint-include-source": { "type": "lint-include-source", "include": "(^(src|chronik)/.*\\.(h|c|cpp)$)", "exclude": [ "(^src/(secp256k1|univalue|leveldb)/)", "(^src/bench/nanobench.h$)" ] }, "lint-std-chrono": { "type": "lint-std-chrono", "include": "(^(src|chronik)/.*\\.(h|cpp)$)" }, "lint-stdint": { "type": "lint-stdint", "include": "(^(src|chronik)/.*\\.(h|c|cpp)$)", "exclude": [ "(^src/(secp256k1|univalue|leveldb)/)", "(^src/bench/nanobench.h$)", "(^src/compat/assumptions.h$)" ] }, "lint-source-filename": { "type": "lint-source-filename", "include": "(^(src|chronik)/.*\\.(h|c|cpp)$)", "exclude": [ "(^src/(secp256k1|univalue|leveldb)/)", "(^src/bench/nanobench.h$)" ] }, "lint-boost-dependencies": { "type": "lint-boost-dependencies", "include": "(^(src|chronik)/.*\\.(h|cpp)$)" }, "lint-python-encoding": { "type": "lint-python-encoding", "include": "(\\.py$)", "exclude": [ "(^contrib/gitian-builder/)", "(^contrib/apple-sdk-tools/)" ] }, "lint-python-shebang": { "type": "lint-python-shebang", "include": "(\\.py$)", "exclude": [ "(__init__\\.py$)", "(^contrib/gitian-builder/)", "(^contrib/apple-sdk-tools/)" ] }, "lint-bash-shebang": { "type": "lint-bash-shebang", "include": "(\\.sh$)", "exclude": [ "(^contrib/gitian-builder/)" ] }, "shellcheck": { "type": "shellcheck", "version": ">=0.7.0", "flags": [ "--external-sources", "--source-path=SCRIPTDIR" ], "include": "(\\.sh$)", "exclude": [ "(^contrib/gitian-builder/)", "(^src/(secp256k1|univalue)/)" ] }, "lint-shell-locale": { "type": "lint-shell-locale", "include": "(\\.sh$)", "exclude": [ "(^contrib/gitian-builder/)", "(^src/(secp256k1|univalue)/)", "(^cmake/utils/log-and-print-on-failure.sh)" ] }, "lint-cpp-void-parameters": { "type": "lint-cpp-void-parameters", "include": "(^(src|chronik)/.*\\.(h|cpp)$)", "exclude": [ "(^src/(crypto/ctaes|secp256k1|univalue|leveldb)/)", "(^src/bench/nanobench.h$)", "(^src/compat/glibc_compat.cpp$)" ] }, "lint-logs": { "type": "lint-logs", "include": "(^(src|chronik)/.*\\.(h|cpp|rs)$)" }, "lint-qt": { "type": "lint-qt", "include": "(^src/qt/.*\\.(h|cpp)$)", "exclude": [ "(^src/qt/(locale|forms|res)/)" ] }, "lint-doxygen": { "type": "lint-doxygen", "include": "(^(src|chronik)/.*\\.(h|c|cpp)$)", "exclude": [ "(^src/(crypto/ctaes|secp256k1|univalue|leveldb)/)", "(^src/bench/nanobench.h$)" ] }, "lint-whitespace": { "type": "lint-whitespace", "include": "(\\.(ac|am|cmake|conf|in|include|json|m4|md|openrc|php|pl|rs|sh|txt|yml)$)", "exclude": [ "(^contrib/gitian-builder/)", "(^src/(secp256k1|univalue|leveldb)/)", "(^src/bench/nanobench.h$)" ] }, "yamllint": { "type": "yamllint", "include": "(\\.(yml|yaml)$)", "exclude": "(^src/(secp256k1|univalue|leveldb)/)" }, "lint-check-nonfatal": { "type": "lint-check-nonfatal", "include": [ "(^src/rpc/.*\\.(h|c|cpp)$)", "(^src/wallet/rpc*.*\\.(h|c|cpp)$)" ], "exclude": "(^src/rpc/server.cpp)" }, "lint-markdown": { "type": "lint-markdown", "include": [ "(\\.md$)" ], "exclude": "(^contrib/gitian-builder/)" }, "lint-python-mypy": { "type": "lint-python-mypy", "version": ">=0.910", "include": "(\\.py$)", "exclude": [ "(^contrib/gitian-builder/)", "(^contrib/apple-sdk-tools/)", "(^contrib/macdeploy/)" ], "flags": [ "--ignore-missing-imports", "--install-types", "--non-interactive" ] }, "lint-python-mutable-default": { "type": "lint-python-mutable-default", "include": "(\\.py$)", "exclude": [ "(^contrib/gitian-builder/)", "(^contrib/apple-sdk-tools/)" ] }, "prettier": { "type": "prettier", "version": ">=2.6.0", "include": [ "(^cashtab/.*\\.(css|html|js|json|jsx|md|scss|ts|tsx)$)", "(^web/.*\\.(css|html|js|json|jsx|md|scss|ts|tsx)$)" ], "exclude": "(^web/.*/translations/.*\\.json$)" }, "lint-python-isort": { "type": "lint-python-isort", "version": ">=5.6.4", "include": "(\\.py$)", "exclude": [ "(^contrib/gitian-builder/)", "(^contrib/apple-sdk-tools/)" ] }, "rustfmt": { "type": "rustfmt", "version": ">=1.5.1", "include": "(\\.rs$)" }, "eslint": { "type": "eslint", "version": ">=8.0.0", "include": [ "(cashtab/.*\\.js$)", "(apps/alias-server/.*\\.js$)", "(modules/ecashaddrjs/.*\\.js$)", "(apps/ecash-herald/.*\\.js$)", "(modules/chronik-client/.*\\.(js|jsx|ts|tsx)$)" ] }, "lint-python-flynt": { "type": "lint-python-flynt", "version": ">=0.78", "include": "(\\.py$)", "exclude": [ "(^contrib/gitian-builder/)", "(^contrib/apple-sdk-tools/)" ] } } } diff --git a/test/functional/rpc_bind.py b/test/functional/rpc_bind.py index 44b2d9b54..45c8d2735 100755 --- a/test/functional/rpc_bind.py +++ b/test/functional/rpc_bind.py @@ -1,189 +1,232 @@ #!/usr/bin/env python3 # 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 running bitcoind with the -rpcbind and -rpcallowip options.""" import sys from platform import uname from test_framework.netutil import ( addr_to_hex, all_interfaces, get_bind_addrs, test_ipv6_local, ) from test_framework.test_framework import BitcoinTestFramework, SkipTest from test_framework.util import ( assert_equal, assert_raises_rpc_error, get_rpc_proxy, rpc_port, rpc_url, ) class RPCBindTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.bind_to_localhost_only = False self.num_nodes = 1 self.supports_cli = False def setup_network(self): self.add_nodes(self.num_nodes, None) def add_options(self, parser): parser.add_argument( "--ipv4", - action='store_true', + action="store_true", dest="run_ipv4", help="Run ipv4 tests only", - default=False) + default=False, + ) parser.add_argument( "--ipv6", - action='store_true', + action="store_true", dest="run_ipv6", help="Run ipv6 tests only", - default=False) + default=False, + ) parser.add_argument( "--nonloopback", - action='store_true', + action="store_true", dest="run_nonloopback", help="Run non-loopback tests only", - default=False) + default=False, + ) def run_bind_test(self, allow_ips, connect_to, addresses, expected): - ''' + """ Start a node with requested rpcallowip and rpcbind parameters, then try to connect, and check if the set of bound addresses matches the expected set. - ''' + """ self.log.info(f"Bind test for {str(addresses)}") expected = [(addr_to_hex(addr), port) for (addr, port) in expected] - base_args = ['-disablewallet', '-nolisten'] + base_args = ["-disablewallet", "-nolisten"] if allow_ips: base_args += [f"-rpcallowip={x}" for x in allow_ips] binds = [f"-rpcbind={addr}" for addr in addresses] - parts = connect_to.split(':') + parts = connect_to.split(":") if len(parts) == 2: self.nodes[0].host = parts[0] self.nodes[0].rpc_port = parts[1] else: self.nodes[0].host = connect_to self.nodes[0].rpc_port = rpc_port(self.nodes[0].index) self.start_node(0, base_args + binds) pid = self.nodes[0].process.pid assert_equal(set(get_bind_addrs(pid)), set(expected)) self.stop_nodes() def run_allowip_test(self, allow_ips, rpchost, rpcport): - ''' + """ Start a node with rpcallow IP, and request getnetworkinfo at a non-localhost IP. - ''' + """ self.log.info(f"Allow IP test for {rpchost}:{rpcport}") node_args = ( - ['-disablewallet', '-nolisten'] + - [f"-rpcallowip={x}" for x in allow_ips] + + ["-disablewallet", "-nolisten"] + + [f"-rpcallowip={x}" for x in allow_ips] + + # Bind to localhost as well so start_nodes doesn't hang - [f"-rpcbind={addr}" for addr in ['127.0.0.1', f"{rpchost}:{rpcport}"]] + [f"-rpcbind={addr}" for addr in ["127.0.0.1", f"{rpchost}:{rpcport}"]] ) self.nodes[0].host = None self.start_nodes([node_args]) # connect to node through non-loopback interface url = rpc_url(self.nodes[0].datadir, self.chain, rpchost, rpcport) node = get_rpc_proxy(url, 0, coveragedir=self.options.coveragedir) node.getnetworkinfo() self.stop_nodes() def run_test(self): # due to OS-specific network stats queries, this test works only on # Linux - if sum([self.options.run_ipv4, self.options.run_ipv6, - self.options.run_nonloopback]) > 1: + if ( + sum( + [ + self.options.run_ipv4, + self.options.run_ipv6, + self.options.run_nonloopback, + ] + ) + > 1 + ): raise AssertionError( - "Only one of --ipv4, --ipv6 and --nonloopback can be set") + "Only one of --ipv4, --ipv6 and --nonloopback can be set" + ) self.log.info("Check for linux") - if not sys.platform.startswith('linux'): + if not sys.platform.startswith("linux"): raise SkipTest("This test can only be run on linux.") # WSL in currently not supported (refer to # https://reviews.bitcoinabc.org/T400 for details). # This condition should be removed once netstat support is provided by # Microsoft. if "microsoft" in uname().version.lower(): - raise SkipTest( - "Running this test on WSL is currently not supported") + raise SkipTest("Running this test on WSL is currently not supported") self.log.info("Check for ipv6") have_ipv6 = test_ipv6_local() if not have_ipv6 and not ( - self.options.run_ipv4 or self.options.run_nonloopback): + self.options.run_ipv4 or self.options.run_nonloopback + ): raise SkipTest("This test requires ipv6 support.") self.log.info("Check for non-loopback interface") self.non_loopback_ip = None for name, ip in all_interfaces(): - if ip != '127.0.0.1': + if ip != "127.0.0.1": self.non_loopback_ip = ip break if self.non_loopback_ip is None and self.options.run_nonloopback: raise SkipTest("This test requires a non-loopback ip address.") self.defaultport = rpc_port(0) if not self.options.run_nonloopback: self._run_loopback_tests() if not self.options.run_ipv4 and not self.options.run_ipv6: self._run_nonloopback_tests() def _run_loopback_tests(self): if self.options.run_ipv4: # check only IPv4 localhost (explicit) - self.run_bind_test(['127.0.0.1'], '127.0.0.1', ['127.0.0.1'], - [('127.0.0.1', self.defaultport)]) + self.run_bind_test( + ["127.0.0.1"], + "127.0.0.1", + ["127.0.0.1"], + [("127.0.0.1", self.defaultport)], + ) # check only IPv4 localhost (explicit) with alternative port - self.run_bind_test(['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171'], - [('127.0.0.1', 32171)]) + self.run_bind_test( + ["127.0.0.1"], + "127.0.0.1:32171", + ["127.0.0.1:32171"], + [("127.0.0.1", 32171)], + ) # check only IPv4 localhost (explicit) with multiple alternative # ports on same host - self.run_bind_test(['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171', '127.0.0.1:32172'], - [('127.0.0.1', 32171), ('127.0.0.1', 32172)]) + self.run_bind_test( + ["127.0.0.1"], + "127.0.0.1:32171", + ["127.0.0.1:32171", "127.0.0.1:32172"], + [("127.0.0.1", 32171), ("127.0.0.1", 32172)], + ) else: # check default without rpcallowip (IPv4 and IPv6 localhost) - self.run_bind_test(None, '127.0.0.1', [], - [('127.0.0.1', self.defaultport), ('::1', self.defaultport)]) + self.run_bind_test( + None, + "127.0.0.1", + [], + [("127.0.0.1", self.defaultport), ("::1", self.defaultport)], + ) # check default with rpcallowip (IPv4 and IPv6 localhost) self.run_bind_test( - ['127.0.0.1'], '127.0.0.1', [], [ - ('127.0.0.1', self.defaultport), ('::1', self.defaultport)]) + ["127.0.0.1"], + "127.0.0.1", + [], + [("127.0.0.1", self.defaultport), ("::1", self.defaultport)], + ) # check only IPv6 localhost (explicit) - self.run_bind_test(['[::1]'], '[::1]', ['[::1]'], - [('::1', self.defaultport)]) + self.run_bind_test( + ["[::1]"], "[::1]", ["[::1]"], [("::1", self.defaultport)] + ) # check both IPv4 and IPv6 localhost (explicit) - self.run_bind_test(['127.0.0.1'], '127.0.0.1', ['127.0.0.1', '[::1]'], - [('127.0.0.1', self.defaultport), ('::1', self.defaultport)]) + self.run_bind_test( + ["127.0.0.1"], + "127.0.0.1", + ["127.0.0.1", "[::1]"], + [("127.0.0.1", self.defaultport), ("::1", self.defaultport)], + ) def _run_nonloopback_tests(self): - self.log.info( - f"Using interface {self.non_loopback_ip} for testing") + self.log.info(f"Using interface {self.non_loopback_ip} for testing") # check only non-loopback interface - self.run_bind_test([self.non_loopback_ip], self.non_loopback_ip, [self.non_loopback_ip], - [(self.non_loopback_ip, self.defaultport)]) + self.run_bind_test( + [self.non_loopback_ip], + self.non_loopback_ip, + [self.non_loopback_ip], + [(self.non_loopback_ip, self.defaultport)], + ) # Check that with invalid rpcallowip, we are denied - self.run_allowip_test([self.non_loopback_ip], - self.non_loopback_ip, self.defaultport) - assert_raises_rpc_error(-342, - "non-JSON HTTP response with '403 Forbidden' from server", - self.run_allowip_test, - ['1.1.1.1'], - self.non_loopback_ip, - self.defaultport) + self.run_allowip_test( + [self.non_loopback_ip], self.non_loopback_ip, self.defaultport + ) + assert_raises_rpc_error( + -342, + "non-JSON HTTP response with '403 Forbidden' from server", + self.run_allowip_test, + ["1.1.1.1"], + self.non_loopback_ip, + self.defaultport, + ) -if __name__ == '__main__': +if __name__ == "__main__": RPCBindTest().main() diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index 349ea529f..65cdd3850 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -1,516 +1,560 @@ #!/usr/bin/env python3 # 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 RPCs related to blockchainstate. Test the following RPCs: - getblockchaininfo - getchaintxstats - gettxoutsetinfo - getblockheader - getdifficulty - getnetworkhashps - waitforblockheight - getblock - getblockhash - getbestblockhash - verifychain Tests correspond to code in rpc/blockchain.cpp. """ import http.client import os import subprocess from decimal import Decimal from test_framework.address import ADDRESS_ECREG_P2SH_OP_TRUE from test_framework.blocktools import ( MAX_FUTURE_BLOCK_TIME, TIME_GENESIS_BLOCK, create_block, create_coinbase, ) from test_framework.messages import CBlockHeader, FromHex, msg_block from test_framework.p2p import P2PInterface from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, assert_greater_than, assert_greater_than_or_equal, assert_is_hash_string, assert_is_hex_string, assert_raises, assert_raises_rpc_error, get_datadir_path, ) from test_framework.wallet import MiniWallet # blocks mined HEIGHT = 200 # ten-minute steps TIME_RANGE_STEP = 600 TIME_RANGE_MTP = TIME_GENESIS_BLOCK + (HEIGHT - 6) * TIME_RANGE_STEP TIME_RANGE_TIP = TIME_GENESIS_BLOCK + (HEIGHT - 1) * TIME_RANGE_STEP TIME_RANGE_END = TIME_GENESIS_BLOCK + HEIGHT * TIME_RANGE_STEP class BlockchainTest(BitcoinTestFramework): - def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 self.supports_cli = False def run_test(self): self.mine_chain() self._test_max_future_block_time() # Set extra args with pruning after rescan is complete - self.restart_node(0, extra_args=['-stopatheight=207', '-prune=1']) + self.restart_node(0, extra_args=["-stopatheight=207", "-prune=1"]) self._test_getblockchaininfo() self._test_getchaintxstats() self._test_gettxoutsetinfo() self._test_getblockheader() self._test_getdifficulty() self._test_getnetworkhashps() self._test_stopatheight() self._test_waitforblockheight() if self.is_wallet_compiled(): self._test_getblock() self._test_getblock_txfee() assert self.nodes[0].verifychain(4, 0) def mine_chain(self): self.log.info( - f"Generate {HEIGHT} blocks after the genesis block in ten-minute steps") + f"Generate {HEIGHT} blocks after the genesis block in ten-minute steps" + ) for t in range(TIME_GENESIS_BLOCK, TIME_RANGE_END, TIME_RANGE_STEP): self.nodes[0].setmocktime(t) - self.generatetoaddress( - self.nodes[0], 1, ADDRESS_ECREG_P2SH_OP_TRUE) - assert_equal(self.nodes[0].getblockchaininfo()['blocks'], HEIGHT) + self.generatetoaddress(self.nodes[0], 1, ADDRESS_ECREG_P2SH_OP_TRUE) + assert_equal(self.nodes[0].getblockchaininfo()["blocks"], HEIGHT) def _test_max_future_block_time(self): self.stop_node(0) self.log.info( - "A block tip of more than MAX_FUTURE_BLOCK_TIME in the future raises an error") + "A block tip of more than MAX_FUTURE_BLOCK_TIME in the future raises an" + " error" + ) self.nodes[0].assert_start_raises_init_error( - extra_args=[ - f"-mocktime={TIME_RANGE_TIP - MAX_FUTURE_BLOCK_TIME - 1}"], - expected_msg=": The block database contains a block which appears to be from the future." - " This may be due to your computer's date and time being set incorrectly." - f" Only rebuild the block database if you are sure that your computer's date and time are correct.{os.linesep}" - "Please restart with -reindex or -reindex-chainstate to recover.", + extra_args=[f"-mocktime={TIME_RANGE_TIP - MAX_FUTURE_BLOCK_TIME - 1}"], + expected_msg=( + ": The block database contains a block which appears to be from the" + " future. This may be due to your computer's date and time being set" + " incorrectly. Only rebuild the block database if you are sure that" + f" your computer's date and time are correct.{os.linesep}Please restart" + " with -reindex or -reindex-chainstate to recover." + ), ) - self.log.info( - "A block tip of MAX_FUTURE_BLOCK_TIME in the future is fine") + self.log.info("A block tip of MAX_FUTURE_BLOCK_TIME in the future is fine") self.start_node( - 0, extra_args=[f"-mocktime={TIME_RANGE_TIP - MAX_FUTURE_BLOCK_TIME}"]) + 0, extra_args=[f"-mocktime={TIME_RANGE_TIP - MAX_FUTURE_BLOCK_TIME}"] + ) def _test_getblockchaininfo(self): self.log.info("Test getblockchaininfo") keys = [ - 'bestblockhash', - 'blocks', - 'chain', - 'chainwork', - 'difficulty', - 'headers', - 'initialblockdownload', - 'mediantime', - 'pruned', - 'size_on_disk', - 'time', - 'verificationprogress', - 'warnings', + "bestblockhash", + "blocks", + "chain", + "chainwork", + "difficulty", + "headers", + "initialblockdownload", + "mediantime", + "pruned", + "size_on_disk", + "time", + "verificationprogress", + "warnings", ] res = self.nodes[0].getblockchaininfo() - assert_equal(res['time'], TIME_RANGE_END - TIME_RANGE_STEP) - assert_equal(res['mediantime'], TIME_RANGE_MTP) + assert_equal(res["time"], TIME_RANGE_END - TIME_RANGE_STEP) + assert_equal(res["mediantime"], TIME_RANGE_MTP) # result should have these additional pruning keys if manual pruning is # enabled - assert_equal(sorted(res.keys()), sorted( - ['pruneheight', 'automatic_pruning'] + keys)) + assert_equal( + sorted(res.keys()), sorted(["pruneheight", "automatic_pruning"] + keys) + ) # size_on_disk should be > 0 - assert_greater_than(res['size_on_disk'], 0) + assert_greater_than(res["size_on_disk"], 0) # pruneheight should be greater or equal to 0 - assert_greater_than_or_equal(res['pruneheight'], 0) + assert_greater_than_or_equal(res["pruneheight"], 0) # check other pruning fields given that prune=1 - assert res['pruned'] - assert not res['automatic_pruning'] + assert res["pruned"] + assert not res["automatic_pruning"] - self.restart_node(0, ['-stopatheight=207']) + self.restart_node(0, ["-stopatheight=207"]) res = self.nodes[0].getblockchaininfo() # should have exact keys assert_equal(sorted(res.keys()), keys) - self.restart_node(0, ['-stopatheight=207', '-prune=550']) + self.restart_node(0, ["-stopatheight=207", "-prune=550"]) res = self.nodes[0].getblockchaininfo() # result should have these additional pruning keys if prune=550 - assert_equal(sorted(res.keys()), sorted( - ['pruneheight', 'automatic_pruning', 'prune_target_size'] + keys)) + assert_equal( + sorted(res.keys()), + sorted(["pruneheight", "automatic_pruning", "prune_target_size"] + keys), + ) # check related fields - assert res['pruned'] - assert_equal(res['pruneheight'], 0) - assert res['automatic_pruning'] - assert_equal(res['prune_target_size'], 576716800) - assert_greater_than(res['size_on_disk'], 0) + assert res["pruned"] + assert_equal(res["pruneheight"], 0) + assert res["automatic_pruning"] + assert_equal(res["prune_target_size"], 576716800) + assert_greater_than(res["size_on_disk"], 0) def _test_getchaintxstats(self): self.log.info("Test getchaintxstats") # Test `getchaintxstats` invalid extra parameters assert_raises_rpc_error( - -1, 'getchaintxstats', self.nodes[0].getchaintxstats, 0, '', 0) + -1, "getchaintxstats", self.nodes[0].getchaintxstats, 0, "", 0 + ) # Test `getchaintxstats` invalid `nblocks` assert_raises_rpc_error( - -1, "JSON value is not an integer as expected", self.nodes[0].getchaintxstats, '') + -1, + "JSON value is not an integer as expected", + self.nodes[0].getchaintxstats, + "", + ) assert_raises_rpc_error( - -8, "Invalid block count: should be between 0 and the block's height - 1", self.nodes[0].getchaintxstats, -1) - assert_raises_rpc_error(-8, "Invalid block count: should be between 0 and the block's height - 1", self.nodes[ - 0].getchaintxstats, self.nodes[0].getblockcount()) + -8, + "Invalid block count: should be between 0 and the block's height - 1", + self.nodes[0].getchaintxstats, + -1, + ) + assert_raises_rpc_error( + -8, + "Invalid block count: should be between 0 and the block's height - 1", + self.nodes[0].getchaintxstats, + self.nodes[0].getblockcount(), + ) # Test `getchaintxstats` invalid `blockhash` assert_raises_rpc_error( - -1, "JSON value is not a string as expected", self.nodes[0].getchaintxstats, blockhash=0) - assert_raises_rpc_error(-8, - "blockhash must be of length 64 (not 1, for '0')", - self.nodes[0].getchaintxstats, - blockhash='0') + -1, + "JSON value is not a string as expected", + self.nodes[0].getchaintxstats, + blockhash=0, + ) + assert_raises_rpc_error( + -8, + "blockhash must be of length 64 (not 1, for '0')", + self.nodes[0].getchaintxstats, + blockhash="0", + ) assert_raises_rpc_error( -8, - "blockhash must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", + ( + "blockhash must be hexadecimal string (not" + " 'ZZZ0000000000000000000000000000000000000000000000000000000000000')" + ), self.nodes[0].getchaintxstats, - blockhash='ZZZ0000000000000000000000000000000000000000000000000000000000000') + blockhash=( + "ZZZ0000000000000000000000000000000000000000000000000000000000000" + ), + ) assert_raises_rpc_error( -5, "Block not found", self.nodes[0].getchaintxstats, - blockhash='0000000000000000000000000000000000000000000000000000000000000000') + blockhash=( + "0000000000000000000000000000000000000000000000000000000000000000" + ), + ) blockhash = self.nodes[0].getblockhash(HEIGHT) self.nodes[0].invalidateblock(blockhash) assert_raises_rpc_error( - -8, "Block is not in main chain", self.nodes[0].getchaintxstats, blockhash=blockhash) + -8, + "Block is not in main chain", + self.nodes[0].getchaintxstats, + blockhash=blockhash, + ) self.nodes[0].reconsiderblock(blockhash) chaintxstats = self.nodes[0].getchaintxstats(nblocks=1) # 200 txs plus genesis tx - assert_equal(chaintxstats['txcount'], HEIGHT + 1) + assert_equal(chaintxstats["txcount"], HEIGHT + 1) # tx rate should be 1 per 10 minutes, or 1/600 # we have to round because of binary math - assert_equal( - round( - chaintxstats['txrate'] * - TIME_RANGE_STEP, - 10), - Decimal(1)) + assert_equal(round(chaintxstats["txrate"] * TIME_RANGE_STEP, 10), Decimal(1)) b1_hash = self.nodes[0].getblockhash(1) b1 = self.nodes[0].getblock(b1_hash) b200_hash = self.nodes[0].getblockhash(HEIGHT) b200 = self.nodes[0].getblock(b200_hash) - time_diff = b200['mediantime'] - b1['mediantime'] + time_diff = b200["mediantime"] - b1["mediantime"] chaintxstats = self.nodes[0].getchaintxstats() - assert_equal(chaintxstats['time'], b200['time']) - assert_equal(chaintxstats['txcount'], HEIGHT + 1) - assert_equal(chaintxstats['window_final_block_hash'], b200_hash) - assert_equal(chaintxstats['window_final_block_height'], HEIGHT) - assert_equal(chaintxstats['window_block_count'], HEIGHT - 1) - assert_equal(chaintxstats['window_tx_count'], HEIGHT - 1) - assert_equal(chaintxstats['window_interval'], time_diff) - assert_equal( - round(chaintxstats['txrate'] * time_diff, 10), Decimal(HEIGHT - 1)) + assert_equal(chaintxstats["time"], b200["time"]) + assert_equal(chaintxstats["txcount"], HEIGHT + 1) + assert_equal(chaintxstats["window_final_block_hash"], b200_hash) + assert_equal(chaintxstats["window_final_block_height"], HEIGHT) + assert_equal(chaintxstats["window_block_count"], HEIGHT - 1) + assert_equal(chaintxstats["window_tx_count"], HEIGHT - 1) + assert_equal(chaintxstats["window_interval"], time_diff) + assert_equal(round(chaintxstats["txrate"] * time_diff, 10), Decimal(HEIGHT - 1)) chaintxstats = self.nodes[0].getchaintxstats(blockhash=b1_hash) - assert_equal(chaintxstats['time'], b1['time']) - assert_equal(chaintxstats['txcount'], 2) - assert_equal(chaintxstats['window_final_block_hash'], b1_hash) - assert_equal(chaintxstats['window_final_block_height'], 1) - assert_equal(chaintxstats['window_block_count'], 0) - assert 'window_tx_count' not in chaintxstats - assert 'window_interval' not in chaintxstats - assert 'txrate' not in chaintxstats + assert_equal(chaintxstats["time"], b1["time"]) + assert_equal(chaintxstats["txcount"], 2) + assert_equal(chaintxstats["window_final_block_hash"], b1_hash) + assert_equal(chaintxstats["window_final_block_height"], 1) + assert_equal(chaintxstats["window_block_count"], 0) + assert "window_tx_count" not in chaintxstats + assert "window_interval" not in chaintxstats + assert "txrate" not in chaintxstats def _test_gettxoutsetinfo(self): node = self.nodes[0] res = node.gettxoutsetinfo() - assert_equal(res['total_amount'], Decimal('8725000000.00')) - assert_equal(res['transactions'], HEIGHT) - assert_equal(res['height'], HEIGHT) - assert_equal(res['txouts'], HEIGHT) - assert_equal(res['bogosize'], 14600), - assert_equal(res['bestblock'], node.getblockhash(HEIGHT)) - size = res['disk_size'] + assert_equal(res["total_amount"], Decimal("8725000000.00")) + assert_equal(res["transactions"], HEIGHT) + assert_equal(res["height"], HEIGHT) + assert_equal(res["txouts"], HEIGHT) + assert_equal(res["bogosize"], 14600), + assert_equal(res["bestblock"], node.getblockhash(HEIGHT)) + size = res["disk_size"] assert size > 6400 assert size < 64000 - assert_equal(len(res['bestblock']), 64) - assert_equal(len(res['hash_serialized']), 64) + assert_equal(len(res["bestblock"]), 64) + assert_equal(len(res["hash_serialized"]), 64) self.log.info( - "Test gettxoutsetinfo works for blockchain with just the genesis block") + "Test gettxoutsetinfo works for blockchain with just the genesis block" + ) b1hash = node.getblockhash(1) node.invalidateblock(b1hash) res2 = node.gettxoutsetinfo() - assert_equal(res2['transactions'], 0) - assert_equal(res2['total_amount'], Decimal('0')) - assert_equal(res2['height'], 0) - assert_equal(res2['txouts'], 0) - assert_equal(res2['bogosize'], 0), - assert_equal(res2['bestblock'], node.getblockhash(0)) - assert_equal(len(res2['hash_serialized']), 64) + assert_equal(res2["transactions"], 0) + assert_equal(res2["total_amount"], Decimal("0")) + assert_equal(res2["height"], 0) + assert_equal(res2["txouts"], 0) + assert_equal(res2["bogosize"], 0), + assert_equal(res2["bestblock"], node.getblockhash(0)) + assert_equal(len(res2["hash_serialized"]), 64) self.log.info( - "Test gettxoutsetinfo returns the same result after invalidate/reconsider block") + "Test gettxoutsetinfo returns the same result after invalidate/reconsider" + " block" + ) node.reconsiderblock(b1hash) res3 = node.gettxoutsetinfo() # The field 'disk_size' is non-deterministic and can thus not be # compared between res and res3. Everything else should be the same. - del res['disk_size'], res3['disk_size'] + del res["disk_size"], res3["disk_size"] assert_equal(res, res3) self.log.info("Test gettxoutsetinfo hash_type option") # Adding hash_type 'hash_serialized', which is the default, should not # change the result. - res4 = node.gettxoutsetinfo(hash_type='hash_serialized') - del res4['disk_size'] + res4 = node.gettxoutsetinfo(hash_type="hash_serialized") + del res4["disk_size"] assert_equal(res, res4) # hash_type none should not return a UTXO set hash. - res5 = node.gettxoutsetinfo(hash_type='none') - assert 'hash_serialized' not in res5 + res5 = node.gettxoutsetinfo(hash_type="none") + assert "hash_serialized" not in res5 # hash_type muhash should return a different UTXO set hash. - res6 = node.gettxoutsetinfo(hash_type='muhash') - assert 'muhash' in res6 - assert res['hash_serialized'] != res6['muhash'] + res6 = node.gettxoutsetinfo(hash_type="muhash") + assert "muhash" in res6 + assert res["hash_serialized"] != res6["muhash"] # muhash should not be returned unless requested. for r in [res, res2, res3, res4, res5]: - assert 'muhash' not in r + assert "muhash" not in r # Unknown hash_type raises an error - assert_raises_rpc_error(-8, - "foohash is not a valid hash_type", - node.gettxoutsetinfo, - "foohash") + assert_raises_rpc_error( + -8, "foohash is not a valid hash_type", node.gettxoutsetinfo, "foohash" + ) def _test_getblockheader(self): self.log.info("Test getblockheader") node = self.nodes[0] - assert_raises_rpc_error(-8, - "hash must be of length 64 (not 8, for 'nonsense')", - node.getblockheader, - "nonsense") assert_raises_rpc_error( -8, - "hash must be hexadecimal string (not 'ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844')", + "hash must be of length 64 (not 8, for 'nonsense')", + node.getblockheader, + "nonsense", + ) + assert_raises_rpc_error( + -8, + ( + "hash must be hexadecimal string (not" + " 'ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844')" + ), node.getblockheader, - "ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844") - assert_raises_rpc_error(-5, "Block not found", node.getblockheader, - "0cf7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844") + "ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844", + ) + assert_raises_rpc_error( + -5, + "Block not found", + node.getblockheader, + "0cf7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844", + ) besthash = node.getbestblockhash() secondbesthash = node.getblockhash(HEIGHT - 1) header = node.getblockheader(blockhash=besthash) - assert_equal(header['hash'], besthash) - assert_equal(header['height'], HEIGHT) - assert_equal(header['confirmations'], 1) - assert_equal(header['previousblockhash'], secondbesthash) - assert_is_hex_string(header['chainwork']) - assert_equal(header['nTx'], 1) - assert_is_hash_string(header['hash']) - assert_is_hash_string(header['previousblockhash']) - assert_is_hash_string(header['merkleroot']) - assert_is_hash_string(header['bits'], length=None) - assert isinstance(header['time'], int) - assert_equal(header['mediantime'], TIME_RANGE_MTP) - assert isinstance(header['nonce'], int) - assert isinstance(header['version'], int) - assert isinstance(int(header['versionHex'], 16), int) - assert isinstance(header['difficulty'], Decimal) + assert_equal(header["hash"], besthash) + assert_equal(header["height"], HEIGHT) + assert_equal(header["confirmations"], 1) + assert_equal(header["previousblockhash"], secondbesthash) + assert_is_hex_string(header["chainwork"]) + assert_equal(header["nTx"], 1) + assert_is_hash_string(header["hash"]) + assert_is_hash_string(header["previousblockhash"]) + assert_is_hash_string(header["merkleroot"]) + assert_is_hash_string(header["bits"], length=None) + assert isinstance(header["time"], int) + assert_equal(header["mediantime"], TIME_RANGE_MTP) + assert isinstance(header["nonce"], int) + assert isinstance(header["version"], int) + assert isinstance(int(header["versionHex"], 16), int) + assert isinstance(header["difficulty"], Decimal) # Test with verbose=False, which should return the header as hex. header_hex = node.getblockheader(blockhash=besthash, verbose=False) assert_is_hex_string(header_hex) header = FromHex(CBlockHeader(), header_hex) header.calc_sha256() assert_equal(header.hash, besthash) - assert 'previousblockhash' not in node.getblockheader( - node.getblockhash(0)) - assert 'nextblockhash' not in node.getblockheader( - node.getbestblockhash()) + assert "previousblockhash" not in node.getblockheader(node.getblockhash(0)) + assert "nextblockhash" not in node.getblockheader(node.getbestblockhash()) def _test_getdifficulty(self): self.log.info("Test getdifficulty") difficulty = self.nodes[0].getdifficulty() # 1 hash in 2 should be valid, so difficulty should be 1/2**31 # binary => decimal => binary math is why we do this check assert abs(difficulty * 2**31 - 1) < 0.0001 def _test_getnetworkhashps(self): self.log.info("Test getnetworkhashps") hashes_per_second = self.nodes[0].getnetworkhashps() # This should be 2 hashes every 10 minutes or 1/300 assert abs(hashes_per_second * 300 - 1) < 0.0001 def _test_stopatheight(self): self.log.info("Test stopping at height") assert_equal(self.nodes[0].getblockcount(), HEIGHT) self.generatetoaddress(self.nodes[0], 6, ADDRESS_ECREG_P2SH_OP_TRUE) assert_equal(self.nodes[0].getblockcount(), HEIGHT + 6) - self.log.debug('Node should not stop at this height') - assert_raises(subprocess.TimeoutExpired, - lambda: self.nodes[0].process.wait(timeout=3)) + self.log.debug("Node should not stop at this height") + assert_raises( + subprocess.TimeoutExpired, lambda: self.nodes[0].process.wait(timeout=3) + ) try: self.generatetoaddress( - self.nodes[0], 1, ADDRESS_ECREG_P2SH_OP_TRUE, sync_fun=self.no_op) + self.nodes[0], 1, ADDRESS_ECREG_P2SH_OP_TRUE, sync_fun=self.no_op + ) except (ConnectionError, http.client.BadStatusLine): pass # The node already shut down before response - self.log.debug('Node should stop at this height...') + self.log.debug("Node should stop at this height...") self.nodes[0].wait_until_stopped() self.start_node(0) assert_equal(self.nodes[0].getblockcount(), HEIGHT + 7) def _test_waitforblockheight(self): self.log.info("Test waitforblockheight") node = self.nodes[0] peer = node.add_p2p_connection(P2PInterface()) - current_height = node.getblock(node.getbestblockhash())['height'] + current_height = node.getblock(node.getbestblockhash())["height"] # Create a fork somewhere below our current height, invalidate the tip # of that fork, and then ensure that waitforblockheight still # works as expected. # # (Previously this was broken based on setting # `rpc/blockchain.cpp:latestblock` incorrectly.) # b20hash = node.getblockhash(20) b20 = node.getblock(b20hash) def solve_and_send_block(prevhash, height, time): b = create_block(prevhash, create_coinbase(height), time) b.solve() peer.send_and_ping(msg_block(b)) return b - b21f = solve_and_send_block(int(b20hash, 16), 21, b20['time'] + 1) + b21f = solve_and_send_block(int(b20hash, 16), 21, b20["time"] + 1) b22f = solve_and_send_block(b21f.sha256, 22, b21f.nTime + 1) node.invalidateblock(b22f.hash) def assert_waitforheight(height, timeout=2): assert_equal( - node.waitforblockheight( - height=height, timeout=timeout)['height'], - current_height) + node.waitforblockheight(height=height, timeout=timeout)["height"], + current_height, + ) assert_waitforheight(0) assert_waitforheight(current_height - 1) assert_waitforheight(current_height) assert_waitforheight(current_height + 1) def _test_getblock(self): # Checks for getblock verbose outputs node = self.nodes[0] blockcount = node.getblockcount() blockhash = node.getblockhash(blockcount - 1) nextblockhash = node.getblockhash(blockcount) blockinfo = node.getblock(blockhash, 2) blockheaderinfo = node.getblockheader(blockhash, True) - assert_equal(blockinfo['hash'], blockhash) - assert_equal(blockinfo['confirmations'], 2) - assert_equal(blockinfo['height'], blockheaderinfo['height']) - assert_equal(blockinfo['versionHex'], blockheaderinfo['versionHex']) - assert_equal(blockinfo['version'], blockheaderinfo['version']) - assert_equal(blockinfo['size'], 181) - assert_equal(blockinfo['merkleroot'], blockheaderinfo['merkleroot']) + assert_equal(blockinfo["hash"], blockhash) + assert_equal(blockinfo["confirmations"], 2) + assert_equal(blockinfo["height"], blockheaderinfo["height"]) + assert_equal(blockinfo["versionHex"], blockheaderinfo["versionHex"]) + assert_equal(blockinfo["version"], blockheaderinfo["version"]) + assert_equal(blockinfo["size"], 181) + assert_equal(blockinfo["merkleroot"], blockheaderinfo["merkleroot"]) # Verify transaction data by check the hex values - for tx in blockinfo['tx']: + for tx in blockinfo["tx"]: rawtransaction = node.getrawtransaction( - txid=tx['txid'], verbose=True, blockhash=blockhash) - assert_equal(tx['hex'], rawtransaction['hex']) - assert_equal(blockinfo['time'], blockheaderinfo['time']) - assert_equal(blockinfo['mediantime'], blockheaderinfo['mediantime']) - assert_equal(blockinfo['nonce'], blockheaderinfo['nonce']) - assert_equal(blockinfo['bits'], blockheaderinfo['bits']) - assert_equal(blockinfo['difficulty'], blockheaderinfo['difficulty']) - assert_equal(blockinfo['chainwork'], blockheaderinfo['chainwork']) - assert_equal( - blockinfo['previousblockhash'], - blockheaderinfo['previousblockhash']) - assert_equal(blockinfo['nextblockhash'], nextblockhash) + txid=tx["txid"], verbose=True, blockhash=blockhash + ) + assert_equal(tx["hex"], rawtransaction["hex"]) + assert_equal(blockinfo["time"], blockheaderinfo["time"]) + assert_equal(blockinfo["mediantime"], blockheaderinfo["mediantime"]) + assert_equal(blockinfo["nonce"], blockheaderinfo["nonce"]) + assert_equal(blockinfo["bits"], blockheaderinfo["bits"]) + assert_equal(blockinfo["difficulty"], blockheaderinfo["difficulty"]) + assert_equal(blockinfo["chainwork"], blockheaderinfo["chainwork"]) assert_equal( - blockinfo['nextblockhash'], - blockheaderinfo['nextblockhash']) + blockinfo["previousblockhash"], blockheaderinfo["previousblockhash"] + ) + assert_equal(blockinfo["nextblockhash"], nextblockhash) + assert_equal(blockinfo["nextblockhash"], blockheaderinfo["nextblockhash"]) - assert 'previousblockhash' not in node.getblock(node.getblockhash(0)) - assert 'nextblockhash' not in node.getblock(node.getbestblockhash()) + assert "previousblockhash" not in node.getblock(node.getblockhash(0)) + assert "nextblockhash" not in node.getblock(node.getbestblockhash()) def _test_getblock_txfee(self): node = self.nodes[0] miniwallet = MiniWallet(node) miniwallet.rescan_utxos() - fee_per_byte = Decimal('0.1') + fee_per_byte = Decimal("0.1") fee_per_kb = 1000 * fee_per_byte - txid = miniwallet.send_self_transfer( - fee_rate=fee_per_kb, from_node=node)['txid'] + txid = miniwallet.send_self_transfer(fee_rate=fee_per_kb, from_node=node)[ + "txid" + ] blockhash = self.generate(node, 1)[0] - self.log.info( - "Test getblock with verbosity 1 only includes the txid") + self.log.info("Test getblock with verbosity 1 only includes the txid") block = node.getblock(blockhash, 1) - assert_equal(block['tx'][1], txid) + assert_equal(block["tx"][1], txid) - self.log.info('Test getblock with verbosity 2 includes expected fee') + self.log.info("Test getblock with verbosity 2 includes expected fee") block = node.getblock(blockhash, 2) - tx = block['tx'][1] - assert_equal(tx['fee'], tx['size'] * fee_per_byte) + tx = block["tx"][1] + assert_equal(tx["fee"], tx["size"] * fee_per_byte) self.log.info( - "Test getblock with verbosity 2 still works with pruned Undo data") + "Test getblock with verbosity 2 still works with pruned Undo data" + ) datadir = get_datadir_path(self.options.tmpdir, 0) def move_block_file(old, new): - old_path = os.path.join(datadir, self.chain, 'blocks', old) - new_path = os.path.join(datadir, self.chain, 'blocks', new) + old_path = os.path.join(datadir, self.chain, "blocks", old) + new_path = os.path.join(datadir, self.chain, "blocks", new) os.rename(old_path, new_path) # Move instead of deleting so we can restore chain state afterwards - move_block_file('rev00000.dat', 'rev_wrong') + move_block_file("rev00000.dat", "rev_wrong") block = node.getblock(blockhash, 2) - assert 'fee' not in block['tx'][1] + assert "fee" not in block["tx"][1] # Restore chain state - move_block_file('rev_wrong', 'rev00000.dat') + move_block_file("rev_wrong", "rev00000.dat") -if __name__ == '__main__': +if __name__ == "__main__": BlockchainTest().main() diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py index 8d66c0da3..336430d3e 100755 --- a/test/functional/rpc_createmultisig.py +++ b/test/functional/rpc_createmultisig.py @@ -1,198 +1,219 @@ #!/usr/bin/env python3 # Copyright (c) 2015-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 multisig RPCs""" import decimal import itertools import json import os from test_framework.authproxy import JSONRPCException from test_framework.descriptors import descsum_create, drop_origins from test_framework.key import ECKey, ECPubKey from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error from test_framework.wallet_util import bytes_to_wif class RpcCreateMultiSigTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 self.supports_cli = False def skip_test_if_missing_module(self): self.skip_if_no_wallet() def get_keys(self): self.pub = [] self.priv = [] node0, node1, node2 = self.nodes for _ in range(self.nkeys): k = ECKey() k.generate() self.pub.append(k.get_pubkey().get_bytes().hex()) self.priv.append(bytes_to_wif(k.get_bytes(), k.is_compressed)) self.final = node2.getnewaddress() def run_test(self): node0, node1, node2 = self.nodes self.check_addmultisigaddress_errors() - self.log.info('Generating blocks ...') + self.log.info("Generating blocks ...") self.generate(node0, 149) self.moved = 0 for self.nkeys in [3, 5]: for self.nsigs in [2, 3]: self.get_keys() self.do_multisig() self.checkbalances() - # Test mixed compressed and uncompressed pubkeys - self.log.info( - 'Mixed compressed and uncompressed multisigs are not allowed') - pk0 = node0.getaddressinfo(node0.getnewaddress())['pubkey'] - pk1 = node1.getaddressinfo(node1.getnewaddress())['pubkey'] - pk2 = node2.getaddressinfo(node2.getnewaddress())['pubkey'] + # Test mixed compressed and uncompressed pubkeys + self.log.info("Mixed compressed and uncompressed multisigs are not allowed") + pk0 = node0.getaddressinfo(node0.getnewaddress())["pubkey"] + pk1 = node1.getaddressinfo(node1.getnewaddress())["pubkey"] + pk2 = node2.getaddressinfo(node2.getnewaddress())["pubkey"] # decompress pk2 pk_obj = ECPubKey() pk_obj.set(bytes.fromhex(pk2)) pk_obj.compressed = False pk2 = pk_obj.get_bytes().hex() - node0.createwallet(wallet_name='wmulti0', disable_private_keys=True) - wmulti0 = node0.get_wallet_rpc('wmulti0') + node0.createwallet(wallet_name="wmulti0", disable_private_keys=True) + wmulti0 = node0.get_wallet_rpc("wmulti0") # Check all permutations of keys because order matters apparently for keys in itertools.permutations([pk0, pk1, pk2]): # Results should be the same as this legacy one - legacy_addr = node0.createmultisig(2, keys)['address'] - assert_equal(legacy_addr, - wmulti0.addmultisigaddress(2, keys, '')['address']) - - self.log.info( - 'Testing sortedmulti descriptors with BIP 67 test vectors') - with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data/rpc_bip67.json'), encoding='utf-8') as f: + legacy_addr = node0.createmultisig(2, keys)["address"] + assert_equal( + legacy_addr, wmulti0.addmultisigaddress(2, keys, "")["address"] + ) + + self.log.info("Testing sortedmulti descriptors with BIP 67 test vectors") + with open( + os.path.join( + os.path.dirname(os.path.realpath(__file__)), "data/rpc_bip67.json" + ), + encoding="utf-8", + ) as f: vectors = json.load(f) for t in vectors: - key_str = ','.join(t['keys']) - desc = descsum_create(f'sh(sortedmulti(2,{key_str}))') - assert_equal(self.nodes[0].deriveaddresses(desc)[0], t['address']) - sorted_key_str = ','.join(t['sorted_keys']) - sorted_key_desc = descsum_create( - f'sh(multi(2,{sorted_key_str}))') - assert_equal(self.nodes[0].deriveaddresses( - sorted_key_desc)[0], t['address']) + key_str = ",".join(t["keys"]) + desc = descsum_create(f"sh(sortedmulti(2,{key_str}))") + assert_equal(self.nodes[0].deriveaddresses(desc)[0], t["address"]) + sorted_key_str = ",".join(t["sorted_keys"]) + sorted_key_desc = descsum_create(f"sh(multi(2,{sorted_key_str}))") + assert_equal( + self.nodes[0].deriveaddresses(sorted_key_desc)[0], t["address"] + ) def check_addmultisigaddress_errors(self): if self.options.descriptors: return self.log.info( - 'Check that addmultisigaddress fails when the private keys are missing') - addresses = [self.nodes[1].getnewaddress( - address_type='legacy') for _ in range(2)] - assert_raises_rpc_error(-5, - 'no full public key for address', - lambda: self.nodes[0].addmultisigaddress(nrequired=1, - keys=addresses)) + "Check that addmultisigaddress fails when the private keys are missing" + ) + addresses = [ + self.nodes[1].getnewaddress(address_type="legacy") for _ in range(2) + ] + assert_raises_rpc_error( + -5, + "no full public key for address", + lambda: self.nodes[0].addmultisigaddress(nrequired=1, keys=addresses), + ) for a in addresses: # Importing all addresses should not change the result self.nodes[0].importaddress(a) - assert_raises_rpc_error(-5, - 'no full public key for address', - lambda: self.nodes[0].addmultisigaddress(nrequired=1, - keys=addresses)) + assert_raises_rpc_error( + -5, + "no full public key for address", + lambda: self.nodes[0].addmultisigaddress(nrequired=1, keys=addresses), + ) def checkbalances(self): node0, node1, node2 = self.nodes self.generate(node0, 100) bal0 = node0.getbalance() bal1 = node1.getbalance() bal2 = node2.getbalance() height = node0.getblockchaininfo()["blocks"] assert 150 < height < 350 total = 149 * 50000000 + (height - 149 - 100) * 25000000 assert bal1 == 0 assert bal2 == self.moved assert bal0 + bal1 + bal2 == total def do_multisig(self): node0, node1, node2 = self.nodes - if 'wmulti' not in node1.listwallets(): + if "wmulti" not in node1.listwallets(): try: - node1.loadwallet('wmulti') + node1.loadwallet("wmulti") except JSONRPCException as e: - path = os.path.join(self.options.tmpdir, "node1", "regtest", - "wallets", "wmulti") - if e.error['code'] == -18 and ( - "Wallet file verification failed. Failed to load database " - f"path '{path}'. Path does not exist." in e.error['message']): - node1.createwallet(wallet_name='wmulti', - disable_private_keys=True) + path = os.path.join( + self.options.tmpdir, "node1", "regtest", "wallets", "wmulti" + ) + if e.error["code"] == -18 and ( + "Wallet file verification failed. Failed to load database " + f"path '{path}'. Path does not exist." + in e.error["message"] + ): + node1.createwallet(wallet_name="wmulti", disable_private_keys=True) else: raise - wmulti = node1.get_wallet_rpc('wmulti') + wmulti = node1.get_wallet_rpc("wmulti") # Construct the expected descriptor desc = f"multi({self.nsigs},{','.join(self.pub)})" - desc = f'sh({desc})' + desc = f"sh({desc})" desc = descsum_create(desc) msig = node2.createmultisig(self.nsigs, self.pub) madd = msig["address"] mredeem = msig["redeemScript"] - assert_equal(desc, msig['descriptor']) + assert_equal(desc, msig["descriptor"]) # compare against addmultisigaddress msigw = wmulti.addmultisigaddress(self.nsigs, self.pub, None) maddw = msigw["address"] mredeemw = msigw["redeemScript"] - assert_equal(desc, drop_origins(msigw['descriptor'])) + assert_equal(desc, drop_origins(msigw["descriptor"])) # addmultisigiaddress and createmultisig work the same assert maddw == madd assert mredeemw == mredeem txid = node0.sendtoaddress(madd, 40000000) tx = node0.getrawtransaction(txid, True) - vout = [v["n"] for v in tx["vout"] - if madd in v["scriptPubKey"].get("addresses", [])] + vout = [ + v["n"] for v in tx["vout"] if madd in v["scriptPubKey"].get("addresses", []) + ] assert len(vout) == 1 vout = vout[0] scriptPubKey = tx["vout"][vout]["scriptPubKey"]["hex"] value = tx["vout"][vout]["value"] - prevtxs = [{"txid": txid, "vout": vout, "scriptPubKey": scriptPubKey, - "redeemScript": mredeem, "amount": value}] + prevtxs = [ + { + "txid": txid, + "vout": vout, + "scriptPubKey": scriptPubKey, + "redeemScript": mredeem, + "amount": value, + } + ] self.generate(node0, 1) outval = value - decimal.Decimal("10.00") rawtx = node2.createrawtransaction( - [{"txid": txid, "vout": vout}], [{self.final: outval}]) + [{"txid": txid, "vout": vout}], [{self.final: outval}] + ) rawtx2 = node2.signrawtransactionwithkey( - rawtx, self.priv[0:self.nsigs - 1], prevtxs) + rawtx, self.priv[0 : self.nsigs - 1], prevtxs + ) rawtx3 = node2.signrawtransactionwithkey( - rawtx2["hex"], [self.priv[-1]], prevtxs) + rawtx2["hex"], [self.priv[-1]], prevtxs + ) self.moved += outval tx = node0.sendrawtransaction(rawtx3["hex"], 0) blk = self.generate(node0, 1)[0] assert tx in node0.getblock(blk)["tx"] txinfo = node0.getrawtransaction(tx, True, blk) self.log.info(f"n/m={self.nsigs}/{self.nkeys} size={txinfo['size']}") wmulti.unloadwallet() -if __name__ == '__main__': +if __name__ == "__main__": RpcCreateMultiSigTest().main() diff --git a/test/functional/rpc_decodescript.py b/test/functional/rpc_decodescript.py index adb101e06..9bb576738 100755 --- a/test/functional/rpc_decodescript.py +++ b/test/functional/rpc_decodescript.py @@ -1,216 +1,264 @@ #!/usr/bin/env python3 # Copyright (c) 2015-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 decoding scripts via decodescript RPC command.""" from test_framework.messages import CTransaction, FromHex, ToHex from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal class DecodeScriptTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 def decodescript_script_sig(self): - signature = '304502207fa7a6d1e0ee81132a269ad84e68d695483745cde8b541e3bf630749894e342a022100c1f7ab20e13e22fb95281a870f3dcf38d782e53023ee313d741ad0cfbc0c509001' + signature = "304502207fa7a6d1e0ee81132a269ad84e68d695483745cde8b541e3bf630749894e342a022100c1f7ab20e13e22fb95281a870f3dcf38d782e53023ee313d741ad0cfbc0c509001" push_signature = f"48{signature}" - public_key = '03b0da749730dc9b4b1f4a14d6902877a92541f5368778853d9c4a0cb7802dcfb2' + public_key = ( + "03b0da749730dc9b4b1f4a14d6902877a92541f5368778853d9c4a0cb7802dcfb2" + ) push_public_key = f"21{public_key}" # below are test cases for all of the standard transaction types # 1) P2PK scriptSig # the scriptSig of a public key scriptPubKey simply pushes a signature # onto the stack rpc_result = self.nodes[0].decodescript(push_signature) - assert_equal(signature, rpc_result['asm']) + assert_equal(signature, rpc_result["asm"]) # 2) P2PKH scriptSig - rpc_result = self.nodes[0].decodescript( - push_signature + push_public_key) - assert_equal(f"{signature} {public_key}", rpc_result['asm']) + rpc_result = self.nodes[0].decodescript(push_signature + push_public_key) + assert_equal(f"{signature} {public_key}", rpc_result["asm"]) # 3) multisig scriptSig # this also tests the leading portion of a P2SH multisig scriptSig # OP_0 - rpc_result = self.nodes[0].decodescript( - f"00{push_signature}{push_signature}") - assert_equal(f"0 {signature} {signature}", rpc_result['asm']) + rpc_result = self.nodes[0].decodescript(f"00{push_signature}{push_signature}") + assert_equal(f"0 {signature} {signature}", rpc_result["asm"]) # 4) P2SH scriptSig # an empty P2SH redeemScript is valid and makes for a very simple test case. # thus, such a spending scriptSig would just need to pass the outer redeemScript # hash test and leave true on the top of the stack. - rpc_result = self.nodes[0].decodescript('5100') - assert_equal('1 0', rpc_result['asm']) + rpc_result = self.nodes[0].decodescript("5100") + assert_equal("1 0", rpc_result["asm"]) # 5) null data scriptSig - no such thing because null data scripts can not be spent. # thus, no test case for that standard transaction type is here. def decodescript_script_pub_key(self): - public_key = '03b0da749730dc9b4b1f4a14d6902877a92541f5368778853d9c4a0cb7802dcfb2' + public_key = ( + "03b0da749730dc9b4b1f4a14d6902877a92541f5368778853d9c4a0cb7802dcfb2" + ) push_public_key = f"21{public_key}" - public_key_hash = '11695b6cd891484c2d49ec5aa738ec2b2f897777' + public_key_hash = "11695b6cd891484c2d49ec5aa738ec2b2f897777" push_public_key_hash = f"14{public_key_hash}" # below are test cases for all of the standard transaction types # 1) P2PK scriptPubKey # OP_CHECKSIG rpc_result = self.nodes[0].decodescript(f"{push_public_key}ac") - assert_equal(f"{public_key} OP_CHECKSIG", rpc_result['asm']) + assert_equal(f"{public_key} OP_CHECKSIG", rpc_result["asm"]) # 2) P2PKH scriptPubKey # OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG - rpc_result = self.nodes[0].decodescript( - f"76a9{push_public_key_hash}88ac") - assert_equal('OP_DUP OP_HASH160 ' + public_key_hash + - ' OP_EQUALVERIFY OP_CHECKSIG', rpc_result['asm']) + rpc_result = self.nodes[0].decodescript(f"76a9{push_public_key_hash}88ac") + assert_equal( + "OP_DUP OP_HASH160 " + public_key_hash + " OP_EQUALVERIFY OP_CHECKSIG", + rpc_result["asm"], + ) # 3) multisig scriptPubKey # OP_CHECKMULTISIG # just imagine that the pub keys used below are different. # for our purposes here it does not matter that they are the same even # though it is unrealistic. rpc_result = self.nodes[0].decodescript( - f"52{push_public_key}{push_public_key}{push_public_key}53ae") - assert_equal('2 ' + public_key + ' ' + public_key + ' ' + - public_key + ' 3 OP_CHECKMULTISIG', rpc_result['asm']) + f"52{push_public_key}{push_public_key}{push_public_key}53ae" + ) + assert_equal( + "2 " + + public_key + + " " + + public_key + + " " + + public_key + + " 3 OP_CHECKMULTISIG", + rpc_result["asm"], + ) # 4) P2SH scriptPubKey # OP_HASH160 OP_EQUAL. # push_public_key_hash here should actually be the hash of a redeem script. # but this works the same for purposes of this test. - rpc_result = self.nodes[0].decodescript( - f"a9{push_public_key_hash}87") - assert_equal( - f"OP_HASH160 {public_key_hash} OP_EQUAL", rpc_result['asm']) + rpc_result = self.nodes[0].decodescript(f"a9{push_public_key_hash}87") + assert_equal(f"OP_HASH160 {public_key_hash} OP_EQUAL", rpc_result["asm"]) # 5) null data scriptPubKey # use a signature look-alike here to make sure that we do not decode random data as a signature. # this matters if/when signature sighash decoding comes along. # would want to make sure that no such decoding takes place in this # case. - signature_imposter = '48304502207fa7a6d1e0ee81132a269ad84e68d695483745cde8b541e3bf630749894e342a022100c1f7ab20e13e22fb95281a870f3dcf38d782e53023ee313d741ad0cfbc0c509001' + signature_imposter = "48304502207fa7a6d1e0ee81132a269ad84e68d695483745cde8b541e3bf630749894e342a022100c1f7ab20e13e22fb95281a870f3dcf38d782e53023ee313d741ad0cfbc0c509001" # OP_RETURN rpc_result = self.nodes[0].decodescript(f"6a{signature_imposter}") - assert_equal(f"OP_RETURN {signature_imposter[2:]}", rpc_result['asm']) + assert_equal(f"OP_RETURN {signature_imposter[2:]}", rpc_result["asm"]) # 6) a CLTV redeem script. redeem scripts are in-effect scriptPubKey scripts, so adding a test here. # OP_NOP2 is also known as OP_CHECKLOCKTIMEVERIFY. # just imagine that the pub keys used below are different. # for our purposes here it does not matter that they are the same even though it is unrealistic. # # OP_IF # OP_CHECKSIGVERIFY # OP_ELSE # OP_CHECKLOCKTIMEVERIFY OP_DROP # OP_ENDIF # OP_CHECKSIG # # lock until block 500,000 rpc_result = self.nodes[0].decodescript( - f"63{push_public_key}ad670320a107b17568{push_public_key}ac") - assert_equal('OP_IF ' + public_key + ' OP_CHECKSIGVERIFY OP_ELSE 500000 OP_CHECKLOCKTIMEVERIFY OP_DROP OP_ENDIF ' + - public_key + ' OP_CHECKSIG', rpc_result['asm']) + f"63{push_public_key}ad670320a107b17568{push_public_key}ac" + ) + assert_equal( + "OP_IF " + + public_key + + " OP_CHECKSIGVERIFY OP_ELSE 500000 OP_CHECKLOCKTIMEVERIFY OP_DROP" + " OP_ENDIF " + + public_key + + " OP_CHECKSIG", + rpc_result["asm"], + ) def decoderawtransaction_asm_sighashtype(self): """Test decoding scripts via RPC command "decoderawtransaction". This test is in with the "decodescript" tests because they are testing the same "asm" script decodes. """ # this test case uses a random plain vanilla mainnet transaction with a # single P2PKH input and output - tx = '0100000001696a20784a2c70143f634e95227dbdfdf0ecd51647052e70854512235f5986ca010000008a47304402207174775824bec6c2700023309a168231ec80b82c6069282f5133e6f11cbb04460220570edc55c7c5da2ca687ebd0372d3546ebc3f810516a002350cac72dfe192dfb014104d3f898e6487787910a690410b7a917ef198905c27fb9d3b0a42da12aceae0544fc7088d239d9a48f2828a15a09e84043001f27cc80d162cb95404e1210161536ffffffff0100e1f505000000001976a914eb6c6e0cdb2d256a32d97b8df1fc75d1920d9bca88ac00000000' + tx = "0100000001696a20784a2c70143f634e95227dbdfdf0ecd51647052e70854512235f5986ca010000008a47304402207174775824bec6c2700023309a168231ec80b82c6069282f5133e6f11cbb04460220570edc55c7c5da2ca687ebd0372d3546ebc3f810516a002350cac72dfe192dfb014104d3f898e6487787910a690410b7a917ef198905c27fb9d3b0a42da12aceae0544fc7088d239d9a48f2828a15a09e84043001f27cc80d162cb95404e1210161536ffffffff0100e1f505000000001976a914eb6c6e0cdb2d256a32d97b8df1fc75d1920d9bca88ac00000000" rpc_result = self.nodes[0].decoderawtransaction(tx) assert_equal( - '304402207174775824bec6c2700023309a168231ec80b82c6069282f5133e6f11cbb04460220570edc55c7c5da2ca687ebd0372d3546ebc3f810516a002350cac72dfe192dfb[ALL] 04d3f898e6487787910a690410b7a917ef198905c27fb9d3b0a42da12aceae0544fc7088d239d9a48f2828a15a09e84043001f27cc80d162cb95404e1210161536', rpc_result['vin'][0]['scriptSig']['asm']) + ( + "304402207174775824bec6c2700023309a168231ec80b82c6069282f5133e6f11cbb04460220570edc55c7c5da2ca687ebd0372d3546ebc3f810516a002350cac72dfe192dfb[ALL]" + " 04d3f898e6487787910a690410b7a917ef198905c27fb9d3b0a42da12aceae0544fc7088d239d9a48f2828a15a09e84043001f27cc80d162cb95404e1210161536" + ), + rpc_result["vin"][0]["scriptSig"]["asm"], + ) # this test case uses a mainnet transaction that has a P2SH input and both P2PKH and P2SH outputs. # it's from James D'Angelo's awesome introductory videos about multisig: https://www.youtube.com/watch?v=zIbUSaZBJgU and https://www.youtube.com/watch?v=OSA1pwlaypc # verify that we have not altered scriptPubKey decoding. - tx = '01000000018d1f5635abd06e2c7e2ddf58dc85b3de111e4ad6e0ab51bb0dcf5e84126d927300000000fdfe0000483045022100ae3b4e589dfc9d48cb82d41008dc5fa6a86f94d5c54f9935531924602730ab8002202f88cf464414c4ed9fa11b773c5ee944f66e9b05cc1e51d97abc22ce098937ea01483045022100b44883be035600e9328a01b66c7d8439b74db64187e76b99a68f7893b701d5380220225bf286493e4c4adcf928c40f785422572eb232f84a0b83b0dea823c3a19c75014c695221020743d44be989540d27b1b4bbbcfd17721c337cb6bc9af20eb8a32520b393532f2102c0120a1dda9e51a938d39ddd9fe0ebc45ea97e1d27a7cbd671d5431416d3dd87210213820eb3d5f509d7438c9eeecb4157b2f595105e7cd564b3cdbb9ead3da41eed53aeffffffff02611e0000000000001976a914dc863734a218bfe83ef770ee9d41a27f824a6e5688acee2a02000000000017a9142a5edea39971049a540474c6a99edf0aa4074c588700000000' + tx = "01000000018d1f5635abd06e2c7e2ddf58dc85b3de111e4ad6e0ab51bb0dcf5e84126d927300000000fdfe0000483045022100ae3b4e589dfc9d48cb82d41008dc5fa6a86f94d5c54f9935531924602730ab8002202f88cf464414c4ed9fa11b773c5ee944f66e9b05cc1e51d97abc22ce098937ea01483045022100b44883be035600e9328a01b66c7d8439b74db64187e76b99a68f7893b701d5380220225bf286493e4c4adcf928c40f785422572eb232f84a0b83b0dea823c3a19c75014c695221020743d44be989540d27b1b4bbbcfd17721c337cb6bc9af20eb8a32520b393532f2102c0120a1dda9e51a938d39ddd9fe0ebc45ea97e1d27a7cbd671d5431416d3dd87210213820eb3d5f509d7438c9eeecb4157b2f595105e7cd564b3cdbb9ead3da41eed53aeffffffff02611e0000000000001976a914dc863734a218bfe83ef770ee9d41a27f824a6e5688acee2a02000000000017a9142a5edea39971049a540474c6a99edf0aa4074c588700000000" rpc_result = self.nodes[0].decoderawtransaction(tx) assert_equal( - '8e3730608c3b0bb5df54f09076e196bc292a8e39a78e73b44b6ba08c78f5cbb0', rpc_result['txid']) + "8e3730608c3b0bb5df54f09076e196bc292a8e39a78e73b44b6ba08c78f5cbb0", + rpc_result["txid"], + ) assert_equal( - '0 3045022100ae3b4e589dfc9d48cb82d41008dc5fa6a86f94d5c54f9935531924602730ab8002202f88cf464414c4ed9fa11b773c5ee944f66e9b05cc1e51d97abc22ce098937ea[ALL] 3045022100b44883be035600e9328a01b66c7d8439b74db64187e76b99a68f7893b701d5380220225bf286493e4c4adcf928c40f785422572eb232f84a0b83b0dea823c3a19c75[ALL] 5221020743d44be989540d27b1b4bbbcfd17721c337cb6bc9af20eb8a32520b393532f2102c0120a1dda9e51a938d39ddd9fe0ebc45ea97e1d27a7cbd671d5431416d3dd87210213820eb3d5f509d7438c9eeecb4157b2f595105e7cd564b3cdbb9ead3da41eed53ae', rpc_result['vin'][0]['scriptSig']['asm']) + ( + "0 3045022100ae3b4e589dfc9d48cb82d41008dc5fa6a86f94d5c54f9935531924602730ab8002202f88cf464414c4ed9fa11b773c5ee944f66e9b05cc1e51d97abc22ce098937ea[ALL]" + " 3045022100b44883be035600e9328a01b66c7d8439b74db64187e76b99a68f7893b701d5380220225bf286493e4c4adcf928c40f785422572eb232f84a0b83b0dea823c3a19c75[ALL]" + " 5221020743d44be989540d27b1b4bbbcfd17721c337cb6bc9af20eb8a32520b393532f2102c0120a1dda9e51a938d39ddd9fe0ebc45ea97e1d27a7cbd671d5431416d3dd87210213820eb3d5f509d7438c9eeecb4157b2f595105e7cd564b3cdbb9ead3da41eed53ae" + ), + rpc_result["vin"][0]["scriptSig"]["asm"], + ) assert_equal( - 'OP_DUP OP_HASH160 dc863734a218bfe83ef770ee9d41a27f824a6e56 OP_EQUALVERIFY OP_CHECKSIG', - rpc_result['vout'][0]['scriptPubKey']['asm']) + ( + "OP_DUP OP_HASH160 dc863734a218bfe83ef770ee9d41a27f824a6e56" + " OP_EQUALVERIFY OP_CHECKSIG" + ), + rpc_result["vout"][0]["scriptPubKey"]["asm"], + ) assert_equal( - 'OP_HASH160 2a5edea39971049a540474c6a99edf0aa4074c58 OP_EQUAL', - rpc_result['vout'][1]['scriptPubKey']['asm']) + "OP_HASH160 2a5edea39971049a540474c6a99edf0aa4074c58 OP_EQUAL", + rpc_result["vout"][1]["scriptPubKey"]["asm"], + ) txSave = FromHex(CTransaction(), tx) # make sure that a specifically crafted op_return value will not pass # all the IsDERSignature checks and then get decoded as a sighash type - tx = '01000000015ded05872fdbda629c7d3d02b194763ce3b9b1535ea884e3c8e765d42e316724020000006b48304502204c10d4064885c42638cbff3585915b322de33762598321145ba033fc796971e2022100bb153ad3baa8b757e30a2175bd32852d2e1cb9080f84d7e32fcdfd667934ef1b012103163c0ff73511ea1743fb5b98384a2ff09dd06949488028fd819f4d83f56264efffffffff0200000000000000000b6a0930060201000201000180380100000000001976a9141cabd296e753837c086da7a45a6c2fe0d49d7b7b88ac00000000' + tx = "01000000015ded05872fdbda629c7d3d02b194763ce3b9b1535ea884e3c8e765d42e316724020000006b48304502204c10d4064885c42638cbff3585915b322de33762598321145ba033fc796971e2022100bb153ad3baa8b757e30a2175bd32852d2e1cb9080f84d7e32fcdfd667934ef1b012103163c0ff73511ea1743fb5b98384a2ff09dd06949488028fd819f4d83f56264efffffffff0200000000000000000b6a0930060201000201000180380100000000001976a9141cabd296e753837c086da7a45a6c2fe0d49d7b7b88ac00000000" rpc_result = self.nodes[0].decoderawtransaction(tx) - assert_equal('OP_RETURN 300602010002010001', - rpc_result['vout'][0]['scriptPubKey']['asm']) + assert_equal( + "OP_RETURN 300602010002010001", rpc_result["vout"][0]["scriptPubKey"]["asm"] + ) # verify that we have not altered scriptPubKey processing even of a # specially crafted P2PKH pubkeyhash and P2SH redeem script hash that # is made to pass the der signature checks - tx = '01000000018d1f5635abd06e2c7e2ddf58dc85b3de111e4ad6e0ab51bb0dcf5e84126d927300000000fdfe0000483045022100ae3b4e589dfc9d48cb82d41008dc5fa6a86f94d5c54f9935531924602730ab8002202f88cf464414c4ed9fa11b773c5ee944f66e9b05cc1e51d97abc22ce098937ea01483045022100b44883be035600e9328a01b66c7d8439b74db64187e76b99a68f7893b701d5380220225bf286493e4c4adcf928c40f785422572eb232f84a0b83b0dea823c3a19c75014c695221020743d44be989540d27b1b4bbbcfd17721c337cb6bc9af20eb8a32520b393532f2102c0120a1dda9e51a938d39ddd9fe0ebc45ea97e1d27a7cbd671d5431416d3dd87210213820eb3d5f509d7438c9eeecb4157b2f595105e7cd564b3cdbb9ead3da41eed53aeffffffff02611e0000000000001976a914301102070101010101010102060101010101010188acee2a02000000000017a91430110207010101010101010206010101010101018700000000' + tx = "01000000018d1f5635abd06e2c7e2ddf58dc85b3de111e4ad6e0ab51bb0dcf5e84126d927300000000fdfe0000483045022100ae3b4e589dfc9d48cb82d41008dc5fa6a86f94d5c54f9935531924602730ab8002202f88cf464414c4ed9fa11b773c5ee944f66e9b05cc1e51d97abc22ce098937ea01483045022100b44883be035600e9328a01b66c7d8439b74db64187e76b99a68f7893b701d5380220225bf286493e4c4adcf928c40f785422572eb232f84a0b83b0dea823c3a19c75014c695221020743d44be989540d27b1b4bbbcfd17721c337cb6bc9af20eb8a32520b393532f2102c0120a1dda9e51a938d39ddd9fe0ebc45ea97e1d27a7cbd671d5431416d3dd87210213820eb3d5f509d7438c9eeecb4157b2f595105e7cd564b3cdbb9ead3da41eed53aeffffffff02611e0000000000001976a914301102070101010101010102060101010101010188acee2a02000000000017a91430110207010101010101010206010101010101018700000000" rpc_result = self.nodes[0].decoderawtransaction(tx) assert_equal( - 'OP_DUP OP_HASH160 3011020701010101010101020601010101010101 OP_EQUALVERIFY OP_CHECKSIG', - rpc_result['vout'][0]['scriptPubKey']['asm']) + ( + "OP_DUP OP_HASH160 3011020701010101010101020601010101010101" + " OP_EQUALVERIFY OP_CHECKSIG" + ), + rpc_result["vout"][0]["scriptPubKey"]["asm"], + ) assert_equal( - 'OP_HASH160 3011020701010101010101020601010101010101 OP_EQUAL', - rpc_result['vout'][1]['scriptPubKey']['asm']) + "OP_HASH160 3011020701010101010101020601010101010101 OP_EQUAL", + rpc_result["vout"][1]["scriptPubKey"]["asm"], + ) # some more full transaction tests of varying specific scriptSigs. used instead of # tests in decodescript_script_sig because the decodescript RPC is specifically # for working on scriptPubKeys (argh!). - push_signature = txSave.vin[0].scriptSig.hex()[2:(0x48 * 2 + 4)] + push_signature = txSave.vin[0].scriptSig.hex()[2 : (0x48 * 2 + 4)] signature = push_signature[2:] der_signature = signature[:-2] signature_sighash_decoded = f"{der_signature}[ALL]" signature_2 = f"{der_signature}82" push_signature_2 = f"48{signature_2}" signature_2_sighash_decoded = f"{der_signature}[NONE|ANYONECANPAY]" # 1) P2PK scriptSig txSave.vin[0].scriptSig = bytes.fromhex(push_signature) rpc_result = self.nodes[0].decoderawtransaction(ToHex(txSave)) - assert_equal(signature_sighash_decoded, - rpc_result['vin'][0]['scriptSig']['asm']) + assert_equal( + signature_sighash_decoded, rpc_result["vin"][0]["scriptSig"]["asm"] + ) # make sure that the sighash decodes come out correctly for a more # complex / lesser used case. txSave.vin[0].scriptSig = bytes.fromhex(push_signature_2) rpc_result = self.nodes[0].decoderawtransaction(ToHex(txSave)) - assert_equal(signature_2_sighash_decoded, - rpc_result['vin'][0]['scriptSig']['asm']) + assert_equal( + signature_2_sighash_decoded, rpc_result["vin"][0]["scriptSig"]["asm"] + ) # 2) multisig scriptSig - txSave.vin[0].scriptSig = bytes.fromhex( - f"00{push_signature}{push_signature_2}") + txSave.vin[0].scriptSig = bytes.fromhex(f"00{push_signature}{push_signature_2}") rpc_result = self.nodes[0].decoderawtransaction(ToHex(txSave)) - assert_equal('0 ' + signature_sighash_decoded + ' ' + - signature_2_sighash_decoded, rpc_result['vin'][0]['scriptSig']['asm']) + assert_equal( + "0 " + signature_sighash_decoded + " " + signature_2_sighash_decoded, + rpc_result["vin"][0]["scriptSig"]["asm"], + ) # 3) test a scriptSig that contains more than push operations. # in fact, it contains an OP_RETURN with data specially crafted to # cause improper decode if the code does not catch it. txSave.vin[0].scriptSig = bytes.fromhex( - '6a143011020701010101010101020601010101010101') + "6a143011020701010101010101020601010101010101" + ) rpc_result = self.nodes[0].decoderawtransaction(ToHex(txSave)) - assert_equal('OP_RETURN 3011020701010101010101020601010101010101', - rpc_result['vin'][0]['scriptSig']['asm']) + assert_equal( + "OP_RETURN 3011020701010101010101020601010101010101", + rpc_result["vin"][0]["scriptSig"]["asm"], + ) def run_test(self): self.decodescript_script_sig() self.decodescript_script_pub_key() self.decoderawtransaction_asm_sighashtype() -if __name__ == '__main__': +if __name__ == "__main__": DecodeScriptTest().main() diff --git a/test/functional/rpc_deprecated.py b/test/functional/rpc_deprecated.py index 43a6dd9f1..9e521d266 100755 --- a/test/functional/rpc_deprecated.py +++ b/test/functional/rpc_deprecated.py @@ -1,163 +1,184 @@ #!/usr/bin/env python3 # Copyright (c) 2017-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 deprecation of RPC calls.""" import random from test_framework.avatools import AvaP2PInterface from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, assert_greater_than, assert_raises_rpc_error, uint256_hex, ) from test_framework.wallet import MiniWallet QUORUM_NODE_COUNT = 16 FAR_IN_THE_FUTURE = 2000000000 class DeprecatedRpcTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 - self.extra_args = [[ - '-avaproofstakeutxodustthreshold=1000000', - '-avaproofstakeutxoconfirmations=1', - '-avacooldown=0', - '-avaminquorumstake=0', - '-avaminavaproofsnodecount=0', - '-avaminquorumconnectedstakeratio=0', - ], + self.extra_args = [ [ - '-avaproofstakeutxodustthreshold=1000000', - '-avaproofstakeutxoconfirmations=1', - '-avacooldown=0', - '-avaminquorumstake=0', - '-avaminavaproofsnodecount=0', - '-avaminquorumconnectedstakeratio=0', - "-deprecatedrpc=isfinalblock_noerror", - "-deprecatedrpc=isfinaltransaction_noerror", - "-deprecatedrpc=getblocktemplate_sigops", - "-deprecatedrpc=softforks", - # This test checks for the presence of the ancestor count in - # the listunspent output. However this is only displayed if the - # count is non-null, which will no longer be the case after - # wellington activation. - f"-wellingtonactivationtime={FAR_IN_THE_FUTURE}", - "-deprecatedrpc=mempool_ancestors_descendants", - ]] + "-avaproofstakeutxodustthreshold=1000000", + "-avaproofstakeutxoconfirmations=1", + "-avacooldown=0", + "-avaminquorumstake=0", + "-avaminavaproofsnodecount=0", + "-avaminquorumconnectedstakeratio=0", + ], + [ + "-avaproofstakeutxodustthreshold=1000000", + "-avaproofstakeutxoconfirmations=1", + "-avacooldown=0", + "-avaminquorumstake=0", + "-avaminavaproofsnodecount=0", + "-avaminquorumconnectedstakeratio=0", + "-deprecatedrpc=isfinalblock_noerror", + "-deprecatedrpc=isfinaltransaction_noerror", + "-deprecatedrpc=getblocktemplate_sigops", + "-deprecatedrpc=softforks", + # This test checks for the presence of the ancestor count in + # the listunspent output. However this is only displayed if the + # count is non-null, which will no longer be the case after + # wellington activation. + f"-wellingtonactivationtime={FAR_IN_THE_FUTURE}", + "-deprecatedrpc=mempool_ancestors_descendants", + ], + ] def skip_test_if_missing_module(self): self.skip_if_no_wallet() def run_test(self): # This test should be used to verify correct behaviour of deprecated # RPC methods with and without the -deprecatedrpc flags. For example: # # In set_test_params: # self.extra_args = [[], ["-deprecatedrpc=generate"]] # # In run_test: # self.log.info("Test generate RPC") # assert_raises_rpc_error(-32, 'The wallet generate rpc method is deprecated', self.nodes[0].rpc.generate, 1) # self.nodes[1].generate(1) self.log.info( - "Check isfinaltransaction returns false when looking for unknown transactions") + "Check isfinaltransaction returns false when looking for unknown" + " transactions" + ) self.sync_all() random_txid = uint256_hex(random.randint(0, 2**256 - 1)) blockhash = self.nodes[0].getbestblockhash() self.log.info( - "Check isfinalblock and isfinaltransaction returns false when the quorum is not established") + "Check isfinalblock and isfinaltransaction returns false when the quorum is" + " not established" + ) assert_raises_rpc_error( -1, "Avalanche is not ready to poll yet.", self.nodes[0].isfinalblock, blockhash, ) assert not self.nodes[1].isfinalblock(blockhash) - cb_txid = self.nodes[0].getblock(blockhash)['tx'][0] + cb_txid = self.nodes[0].getblock(blockhash)["tx"][0] assert_raises_rpc_error( -1, "Avalanche is not ready to poll yet.", self.nodes[0].isfinaltransaction, cb_txid, blockhash, ) assert not self.nodes[1].isfinaltransaction(cb_txid, blockhash) - [[node.add_p2p_connection(AvaP2PInterface(self, node)) - for _ in range(0, QUORUM_NODE_COUNT)] for node in self.nodes] - - self.wait_until(lambda: all( - node.getavalancheinfo()['ready_to_poll'] is True for node in self.nodes)) + [ + [ + node.add_p2p_connection(AvaP2PInterface(self, node)) + for _ in range(0, QUORUM_NODE_COUNT) + ] + for node in self.nodes + ] + + self.wait_until( + lambda: all( + node.getavalancheinfo()["ready_to_poll"] is True for node in self.nodes + ) + ) assert_raises_rpc_error( -5, "No such transaction found in the provided block.", self.nodes[0].isfinaltransaction, random_txid, blockhash, ) assert not self.nodes[1].isfinaltransaction(random_txid, blockhash) self.log.info( - "Check the getblocktemplate output with and without -deprecatedrpc=getblocktemplate_sigops") + "Check the getblocktemplate output with and without" + " -deprecatedrpc=getblocktemplate_sigops" + ) # Add a transaction to both nodes mempool wallet = MiniWallet(self.nodes[0]) self.generate(wallet, 1) unconfirmed_tx = wallet.send_self_transfer(from_node=self.nodes[0]) - self.nodes[1].sendrawtransaction(unconfirmed_tx['hex']) - assert unconfirmed_tx['txid'] in self.nodes[0].getrawmempool() - assert unconfirmed_tx['txid'] in self.nodes[1].getrawmempool() + self.nodes[1].sendrawtransaction(unconfirmed_tx["hex"]) + assert unconfirmed_tx["txid"] in self.nodes[0].getrawmempool() + assert unconfirmed_tx["txid"] in self.nodes[1].getrawmempool() block_template = self.nodes[0].getblocktemplate() - block_template_txs = block_template.get('transactions', []) + block_template_txs = block_template.get("transactions", []) assert_greater_than(len(block_template_txs), 0) for tx in block_template_txs: - assert 'sigchecks' in tx - assert 'sigops' not in tx - assert 'sigchecklimit' in block_template - assert 'sigoplimit' not in block_template + assert "sigchecks" in tx + assert "sigops" not in tx + assert "sigchecklimit" in block_template + assert "sigoplimit" not in block_template deprecated_block_template = self.nodes[1].getblocktemplate() deprecated_block_template_txs = deprecated_block_template.get( - 'transactions', []) + "transactions", [] + ) assert_greater_than(len(deprecated_block_template_txs), 0) for tx in deprecated_block_template_txs: - assert 'sigchecks' in tx - assert 'sigops' in tx - assert 'sigchecklimit' in deprecated_block_template - assert 'sigoplimit' in deprecated_block_template + assert "sigchecks" in tx + assert "sigops" in tx + assert "sigchecklimit" in deprecated_block_template + assert "sigoplimit" in deprecated_block_template self.log.info( - "Check the getblockchaininfo output with and without -deprecatedrpc=softforks") - assert 'softforks' not in self.nodes[0].getblockchaininfo() + "Check the getblockchaininfo output with and without" + " -deprecatedrpc=softforks" + ) + assert "softforks" not in self.nodes[0].getblockchaininfo() res = self.nodes[1].getblockchaininfo() - assert_equal(res['softforks'], {}) + assert_equal(res["softforks"], {}) txid = self.nodes[0].sendtoaddress( - self.nodes[0].get_deterministic_priv_key().address, 1000000) + self.nodes[0].get_deterministic_priv_key().address, 1000000 + ) assert txid in self.nodes[0].getrawmempool() utxo = self.nodes[0].listunspent(minconf=0, maxconf=0)[0] - assert 'ancestorcount' not in utxo - assert 'ancestorsize' not in utxo - assert 'ancestorfees' not in utxo + assert "ancestorcount" not in utxo + assert "ancestorsize" not in utxo + assert "ancestorfees" not in utxo txid = self.nodes[1].sendtoaddress( - self.nodes[1].get_deterministic_priv_key().address, 1000000) + self.nodes[1].get_deterministic_priv_key().address, 1000000 + ) assert txid in self.nodes[1].getrawmempool() utxo = self.nodes[1].listunspent(minconf=0, maxconf=0)[0] - assert 'ancestorcount' in utxo - assert 'ancestorsize' in utxo - assert 'ancestorfees' in utxo + assert "ancestorcount" in utxo + assert "ancestorsize" in utxo + assert "ancestorfees" in utxo -if __name__ == '__main__': +if __name__ == "__main__": DeprecatedRpcTest().main() diff --git a/test/functional/rpc_deriveaddresses.py b/test/functional/rpc_deriveaddresses.py index 9b5313149..8a86964c9 100755 --- a/test/functional/rpc_deriveaddresses.py +++ b/test/functional/rpc_deriveaddresses.py @@ -1,109 +1,140 @@ #!/usr/bin/env python3 # Copyright (c) 2018 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 deriveaddresses rpc call.""" from test_framework.descriptors import descsum_create from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error class DeriveaddressesTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 def run_test(self): - assert_raises_rpc_error(-5, "Missing checksum", - self.nodes[0].deriveaddresses, "a") + assert_raises_rpc_error( + -5, "Missing checksum", self.nodes[0].deriveaddresses, "a" + ) descriptor = "pkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/0)#rdfjd0a9" address = "ecregtest:qzgrvmwc8vevauc25j86hgfpduz8j98yvvta4yv3yw" assert_equal(self.nodes[0].deriveaddresses(descriptor), [address]) descriptor = descriptor[:-9] - assert_raises_rpc_error(-5, - "Missing checksum", - self.nodes[0].deriveaddresses, - descriptor) + assert_raises_rpc_error( + -5, "Missing checksum", self.nodes[0].deriveaddresses, descriptor + ) descriptor_pubkey = "pkh(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/0)#7st8eans" address = "ecregtest:qzgrvmwc8vevauc25j86hgfpduz8j98yvvta4yv3yw" - assert_equal(self.nodes[0].deriveaddresses( - descriptor_pubkey), [address]) + assert_equal(self.nodes[0].deriveaddresses(descriptor_pubkey), [address]) ranged_descriptor = "pkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)#77vpsvm5" assert_equal( self.nodes[0].deriveaddresses(ranged_descriptor, [1, 2]), - ["ecregtest:qz7mjsvr6gglnl389gnfxmqx0asxp0hcvqacaw0x8k", "ecregtest:qq9q9wefpjzuna7qhuzz7rvck9tuhrzp3gracfdgm2"]) + [ + "ecregtest:qz7mjsvr6gglnl389gnfxmqx0asxp0hcvqacaw0x8k", + "ecregtest:qq9q9wefpjzuna7qhuzz7rvck9tuhrzp3gracfdgm2", + ], + ) assert_equal( self.nodes[0].deriveaddresses(ranged_descriptor, 2), - [address, "ecregtest:qz7mjsvr6gglnl389gnfxmqx0asxp0hcvqacaw0x8k", "ecregtest:qq9q9wefpjzuna7qhuzz7rvck9tuhrzp3gracfdgm2"]) + [ + address, + "ecregtest:qz7mjsvr6gglnl389gnfxmqx0asxp0hcvqacaw0x8k", + "ecregtest:qq9q9wefpjzuna7qhuzz7rvck9tuhrzp3gracfdgm2", + ], + ) assert_raises_rpc_error( -8, "Range should not be specified for an un-ranged descriptor", self.nodes[0].deriveaddresses, descsum_create( - "pkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/0)"), - [0, 2]) + "pkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/0)" + ), + [0, 2], + ) assert_raises_rpc_error( -8, "Range must be specified for a ranged descriptor", self.nodes[0].deriveaddresses, - descsum_create("pkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)")) + descsum_create( + "pkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)" + ), + ) assert_raises_rpc_error( -8, "End of range is too high", self.nodes[0].deriveaddresses, descsum_create( - "pkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)"), - 10000000000) + "pkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)" + ), + 10000000000, + ) assert_raises_rpc_error( -8, "Range is too large", self.nodes[0].deriveaddresses, descsum_create( - "pkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)"), - [1000000000, 2000000000]) + "pkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)" + ), + [1000000000, 2000000000], + ) assert_raises_rpc_error( -8, "Range specified as [begin,end] must not have begin after end", self.nodes[0].deriveaddresses, descsum_create( - "pkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)"), - [2, 0]) + "pkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)" + ), + [2, 0], + ) assert_raises_rpc_error( -8, "Range should be greater or equal than 0", self.nodes[0].deriveaddresses, descsum_create( - "pkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)"), - [-1, 0]) + "pkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)" + ), + [-1, 0], + ) combo_descriptor = descsum_create( - "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/0)") - assert_equal(self.nodes[0].deriveaddresses( - combo_descriptor), [address, address]) + "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/0)" + ) + assert_equal( + self.nodes[0].deriveaddresses(combo_descriptor), [address, address] + ) hardened_without_privkey_descriptor = descsum_create( - "pkh(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1'/1/0)") - assert_raises_rpc_error(-5, - "Cannot derive script without private keys", - self.nodes[0].deriveaddresses, - hardened_without_privkey_descriptor) + "pkh(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1'/1/0)" + ) + assert_raises_rpc_error( + -5, + "Cannot derive script without private keys", + self.nodes[0].deriveaddresses, + hardened_without_privkey_descriptor, + ) bare_multisig_descriptor = descsum_create( - "multi(1, tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/0, tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/1)") - assert_raises_rpc_error(-5, - "Descriptor does not have a corresponding address", - self.nodes[0].deriveaddresses, - bare_multisig_descriptor) + "multi(1," + " tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/0," + " tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/1)" + ) + assert_raises_rpc_error( + -5, + "Descriptor does not have a corresponding address", + self.nodes[0].deriveaddresses, + bare_multisig_descriptor, + ) -if __name__ == '__main__': +if __name__ == "__main__": DeriveaddressesTest().main() diff --git a/test/functional/rpc_dumptxoutset.py b/test/functional/rpc_dumptxoutset.py index 757733738..2ddc0dce3 100755 --- a/test/functional/rpc_dumptxoutset.py +++ b/test/functional/rpc_dumptxoutset.py @@ -1,53 +1,56 @@ #!/usr/bin/env python3 # Copyright (c) 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 generation of UTXO snapshots using `dumptxoutset`. """ import hashlib from pathlib import Path from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error class DumptxoutsetTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 def run_test(self): """Test a trivial usage of the dumptxoutset RPC command.""" node = self.nodes[0] - mocktime = node.getblockheader(node.getblockhash(0))['time'] + 1 + mocktime = node.getblockheader(node.getblockhash(0))["time"] + 1 node.setmocktime(mocktime) self.generate(node, 100) - FILENAME = 'txoutset.dat' + FILENAME = "txoutset.dat" out = node.dumptxoutset(FILENAME) expected_path = Path(node.datadir) / self.chain / FILENAME assert expected_path.is_file() - assert_equal(out['coins_written'], 100) - assert_equal(out['base_height'], 100) - assert_equal(out['path'], str(expected_path)) + assert_equal(out["coins_written"], 100) + assert_equal(out["base_height"], 100) + assert_equal(out["path"], str(expected_path)) # Blockhash should be deterministic based on mocked time. assert_equal( - out['base_hash'], - '65d0aec2439aae14373c153f596fb90a87b643d9bff3e65f250aa8f055e6816b') + out["base_hash"], + "65d0aec2439aae14373c153f596fb90a87b643d9bff3e65f250aa8f055e6816b", + ) - with open(str(expected_path), 'rb') as f: + with open(str(expected_path), "rb") as f: digest = hashlib.sha256(f.read()).hexdigest() # UTXO snapshot hash should be deterministic based on mocked time. assert_equal( digest, - 'a92dc32a15975b3c84bb1e6ac5218ff94194b4ea7d1b9372fb80184a7533a89f') + "a92dc32a15975b3c84bb1e6ac5218ff94194b4ea7d1b9372fb80184a7533a89f", + ) # Specifying a path to an existing file will fail. assert_raises_rpc_error( - -8, f'{FILENAME} already exists', node.dumptxoutset, FILENAME) + -8, f"{FILENAME} already exists", node.dumptxoutset, FILENAME + ) -if __name__ == '__main__': +if __name__ == "__main__": DumptxoutsetTest().main() diff --git a/test/functional/rpc_estimatefee.py b/test/functional/rpc_estimatefee.py index cd6062b3b..892cb9896 100755 --- a/test/functional/rpc_estimatefee.py +++ b/test/functional/rpc_estimatefee.py @@ -1,49 +1,61 @@ #!/usr/bin/env python3 # Copyright (c) 2019 The Bitcoin developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. from decimal import Decimal from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error class EstimateFeeTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 self.extra_args = [ [], ["-minrelaytxfee=1000"], ["-mintxfee=20", "-maxtxfee=25"], ] def skip_test_if_missing_module(self): self.skip_if_no_wallet() def run_test(self): default_node = self.nodes[0] diff_relay_fee_node = self.nodes[1] diff_tx_fee_node = self.nodes[2] for i in range(5): self.generate(self.nodes[0], 1) # estimatefee is 0.00001 by default, regardless of block contents - assert_equal(default_node.estimatefee(), Decimal('10.00')) + assert_equal(default_node.estimatefee(), Decimal("10.00")) # estimatefee may be different for nodes that set it in their # config - assert_equal(diff_relay_fee_node.estimatefee(), Decimal('1000.00')) + assert_equal(diff_relay_fee_node.estimatefee(), Decimal("1000.00")) # Check the reasonableness of settxfee - assert_raises_rpc_error(-8, "txfee cannot be less than min relay tx fee", - diff_tx_fee_node.settxfee, Decimal('5.00')) - assert_raises_rpc_error(-8, "txfee cannot be less than wallet min fee", - diff_tx_fee_node.settxfee, Decimal('15.00')) - assert_raises_rpc_error(-8, "txfee cannot be more than wallet max tx fee", - diff_tx_fee_node.settxfee, Decimal('30.00')) - - -if __name__ == '__main__': + assert_raises_rpc_error( + -8, + "txfee cannot be less than min relay tx fee", + diff_tx_fee_node.settxfee, + Decimal("5.00"), + ) + assert_raises_rpc_error( + -8, + "txfee cannot be less than wallet min fee", + diff_tx_fee_node.settxfee, + Decimal("15.00"), + ) + assert_raises_rpc_error( + -8, + "txfee cannot be more than wallet max tx fee", + diff_tx_fee_node.settxfee, + Decimal("30.00"), + ) + + +if __name__ == "__main__": EstimateFeeTest().main() diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py index a9993d672..076b44fcc 100755 --- a/test/functional/rpc_fundrawtransaction.py +++ b/test/functional/rpc_fundrawtransaction.py @@ -1,910 +1,1007 @@ #!/usr/bin/env python3 # 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. from decimal import Decimal from test_framework.descriptors import descsum_create from test_framework.messages import CTransaction, FromHex from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, assert_fee_amount, assert_greater_than, assert_greater_than_or_equal, assert_raises_rpc_error, find_vout_for_address, ) def get_unspent(listunspent, amount): for utx in listunspent: - if utx['amount'] == amount: + if utx["amount"] == amount: return utx - raise AssertionError( - f'Could not find unspent with amount={amount}') + raise AssertionError(f"Could not find unspent with amount={amount}") class RawTransactionsTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 4 self.setup_clean_chain = True # This test isn't testing tx relay. Set whitelist on the peers for # instant tx relay. - self.extra_args = [['-whitelist=noban@127.0.0.1']] * self.num_nodes + self.extra_args = [["-whitelist=noban@127.0.0.1"]] * self.num_nodes def skip_test_if_missing_module(self): self.skip_if_no_wallet() def setup_network(self): self.setup_nodes() self.connect_nodes(0, 1) self.connect_nodes(1, 2) self.connect_nodes(0, 2) self.connect_nodes(0, 3) def run_test(self): self.log.info("Connect nodes, set fees, generate blocks, and sync") - self.min_relay_tx_fee = self.nodes[0].getnetworkinfo()['relayfee'] + self.min_relay_tx_fee = self.nodes[0].getnetworkinfo()["relayfee"] # This test is not meant to test fee estimation and we'd like # to be sure all txs are sent at a consistent desired feerate for node in self.nodes: node.settxfee(self.min_relay_tx_fee) # if the fee's positive delta is higher than this value tests will fail, # neg. delta always fail the tests. # The size of the signature of every input may be at most 2 bytes larger # than a minimum sized signature. # = 2 bytes * minRelayTxFeePerByte self.fee_tolerance = 2 * self.min_relay_tx_fee / 1000 self.generate(self.nodes[2], 1) self.generate(self.nodes[0], 121) self.test_change_position() self.test_simple() self.test_simple_two_coins() self.test_simple_two_outputs() self.test_change() self.test_no_change() self.test_invalid_option() self.test_invalid_change_address() self.test_valid_change_address() self.test_coin_selection() self.test_two_vin() self.test_two_vin_two_vout() self.test_invalid_input() self.test_fee_p2pkh() self.test_fee_p2pkh_multi_out() self.test_fee_p2sh() self.test_fee_4of5() self.test_spend_2of2() self.test_locked_wallet() self.test_many_inputs_fee() self.test_many_inputs_send() self.test_op_return() self.test_watchonly() self.test_all_watched_funds() self.test_option_feerate() self.test_address_reuse() self.test_option_subtract_fee_from_outputs() self.test_subtract_fee_with_presets() def test_change_position(self): """Ensure setting changePosition in fundraw with an exact match is handled properly.""" self.log.info("Test fundrawtxn changePosition option") rawmatch = self.nodes[2].createrawtransaction( - [], {self.nodes[2].getnewaddress(): 50000000}) + [], {self.nodes[2].getnewaddress(): 50000000} + ) rawmatch = self.nodes[2].fundrawtransaction( - rawmatch, {"changePosition": 1, "subtractFeeFromOutputs": [0]}) + rawmatch, {"changePosition": 1, "subtractFeeFromOutputs": [0]} + ) assert_equal(rawmatch["changepos"], -1) - self.nodes[3].createwallet(wallet_name="wwatch", - disable_private_keys=True) - wwatch = self.nodes[3].get_wallet_rpc('wwatch') + self.nodes[3].createwallet(wallet_name="wwatch", disable_private_keys=True) + wwatch = self.nodes[3].get_wallet_rpc("wwatch") watchonly_address = self.nodes[0].getnewaddress() - watchonly_pubkey = self.nodes[0].getaddressinfo(watchonly_address)[ - "pubkey"] + watchonly_pubkey = self.nodes[0].getaddressinfo(watchonly_address)["pubkey"] self.watchonly_amount = Decimal(200000000) wwatch.importpubkey(watchonly_pubkey, "", True) self.watchonly_txid = self.nodes[0].sendtoaddress( - watchonly_address, self.watchonly_amount) + watchonly_address, self.watchonly_amount + ) # Lock UTXO so nodes[0] doesn't accidentally spend it self.watchonly_vout = find_vout_for_address( - self.nodes[0], self.watchonly_txid, watchonly_address) + self.nodes[0], self.watchonly_txid, watchonly_address + ) self.nodes[0].lockunspent( - False, [{"txid": self.watchonly_txid, "vout": self.watchonly_vout}]) + False, [{"txid": self.watchonly_txid, "vout": self.watchonly_vout}] + ) self.nodes[0].sendtoaddress( - self.nodes[3].get_wallet_rpc( - self.default_wallet_name).getnewaddress(), - self.watchonly_amount / 10) + self.nodes[3].get_wallet_rpc(self.default_wallet_name).getnewaddress(), + self.watchonly_amount / 10, + ) 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.generate(self.nodes[0], 1) wwatch.unloadwallet() def test_simple(self): self.log.info("Test fundrawtxn") inputs = [] outputs = {self.nodes[0].getnewaddress(): 1000000} rawTx = self.nodes[2].createrawtransaction(inputs, outputs) dec_tx = self.nodes[2].decoderawtransaction(rawTx) rawtxfund = self.nodes[2].fundrawtransaction(rawTx) - dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex']) + dec_tx = self.nodes[2].decoderawtransaction(rawtxfund["hex"]) # test that we have enough inputs - assert len(dec_tx['vin']) > 0 + assert len(dec_tx["vin"]) > 0 def test_simple_two_coins(self): self.log.info("Test fundrawtxn with 2 coins") inputs = [] outputs = {self.nodes[0].getnewaddress(): 2200000} rawTx = self.nodes[2].createrawtransaction(inputs, outputs) dec_tx = self.nodes[2].decoderawtransaction(rawTx) rawtxfund = self.nodes[2].fundrawtransaction(rawTx) - dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex']) + dec_tx = self.nodes[2].decoderawtransaction(rawtxfund["hex"]) # test if we have enough inputs - assert len(dec_tx['vin']) > 0 - assert_equal(dec_tx['vin'][0]['scriptSig']['hex'], '') + assert len(dec_tx["vin"]) > 0 + assert_equal(dec_tx["vin"][0]["scriptSig"]["hex"], "") def test_simple_two_outputs(self): self.log.info("Test fundrawtxn with 2 outputs") inputs = [] outputs = { - self.nodes[0].getnewaddress(): 2600000, self.nodes[1].getnewaddress(): 2500000} + self.nodes[0].getnewaddress(): 2600000, + self.nodes[1].getnewaddress(): 2500000, + } rawTx = self.nodes[2].createrawtransaction(inputs, outputs) dec_tx = self.nodes[2].decoderawtransaction(rawTx) rawtxfund = self.nodes[2].fundrawtransaction(rawTx) - dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex']) + dec_tx = self.nodes[2].decoderawtransaction(rawtxfund["hex"]) totalOut = 0 - for out in dec_tx['vout']: - totalOut += out['value'] + for out in dec_tx["vout"]: + totalOut += out["value"] - assert len(dec_tx['vin']) > 0 - assert_equal(dec_tx['vin'][0]['scriptSig']['hex'], '') + assert len(dec_tx["vin"]) > 0 + assert_equal(dec_tx["vin"][0]["scriptSig"]["hex"], "") def test_change(self): self.log.info("Test fundrawtxn with a vin > required amount") utx = get_unspent(self.nodes[2].listunspent(), 5000000) - inputs = [{'txid': utx['txid'], 'vout': utx['vout']}] + inputs = [{"txid": utx["txid"], "vout": utx["vout"]}] outputs = {self.nodes[0].getnewaddress(): 1000000} rawTx = self.nodes[2].createrawtransaction(inputs, outputs) dec_tx = self.nodes[2].decoderawtransaction(rawTx) - assert_equal(utx['txid'], dec_tx['vin'][0]['txid']) + assert_equal(utx["txid"], dec_tx["vin"][0]["txid"]) rawtxfund = self.nodes[2].fundrawtransaction(rawTx) - fee = rawtxfund['fee'] + fee = rawtxfund["fee"] # Use the same fee for the next tx self.test_no_change_fee = fee - dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex']) + dec_tx = self.nodes[2].decoderawtransaction(rawtxfund["hex"]) totalOut = 0 - for out in dec_tx['vout']: - totalOut += out['value'] + for out in dec_tx["vout"]: + totalOut += out["value"] # compare vin total and totalout+fee - assert_equal(fee + totalOut, utx['amount']) + assert_equal(fee + totalOut, utx["amount"]) def test_no_change(self): self.log.info("Test fundrawtxn not having a change output") utx = get_unspent(self.nodes[2].listunspent(), 5000000) - inputs = [{'txid': utx['txid'], 'vout': utx['vout']}] + inputs = [{"txid": utx["txid"], "vout": utx["vout"]}] outputs = { - self.nodes[0].getnewaddress(): Decimal(5000000) - - self.test_no_change_fee - - self.fee_tolerance} + self.nodes[0].getnewaddress(): ( + Decimal(5000000) - self.test_no_change_fee - self.fee_tolerance + ) + } rawTx = self.nodes[2].createrawtransaction(inputs, outputs) dec_tx = self.nodes[2].decoderawtransaction(rawTx) - assert_equal(utx['txid'], dec_tx['vin'][0]['txid']) + assert_equal(utx["txid"], dec_tx["vin"][0]["txid"]) rawtxfund = self.nodes[2].fundrawtransaction(rawTx) - fee = rawtxfund['fee'] - dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex']) + fee = rawtxfund["fee"] + dec_tx = self.nodes[2].decoderawtransaction(rawtxfund["hex"]) totalOut = 0 - for out in dec_tx['vout']: - totalOut += out['value'] + for out in dec_tx["vout"]: + totalOut += out["value"] - assert_equal(rawtxfund['changepos'], -1) + assert_equal(rawtxfund["changepos"], -1) # compare vin total and totalout+fee - assert_equal(fee + totalOut, utx['amount']) + assert_equal(fee + totalOut, utx["amount"]) def test_invalid_option(self): self.log.info("Test fundrawtxn with an invalid option") utx = get_unspent(self.nodes[2].listunspent(), 5000000) - inputs = [{'txid': utx['txid'], 'vout': utx['vout']}] + inputs = [{"txid": utx["txid"], "vout": utx["vout"]}] outputs = {self.nodes[0].getnewaddress(): Decimal(4000000)} rawTx = self.nodes[2].createrawtransaction(inputs, outputs) dec_tx = self.nodes[2].decoderawtransaction(rawTx) - assert_equal(utx['txid'], dec_tx['vin'][0]['txid']) + assert_equal(utx["txid"], dec_tx["vin"][0]["txid"]) - assert_raises_rpc_error(-3, "Unexpected key foo", self.nodes[ - 2].fundrawtransaction, rawTx, {'foo': 'bar'}) + assert_raises_rpc_error( + -3, + "Unexpected key foo", + self.nodes[2].fundrawtransaction, + rawTx, + {"foo": "bar"}, + ) # reserveChangeKey was deprecated and is now removed - assert_raises_rpc_error(-3, - "Unexpected key reserveChangeKey", - lambda: self.nodes[2].fundrawtransaction(hexstring=rawTx, - options={'reserveChangeKey': True})) + assert_raises_rpc_error( + -3, + "Unexpected key reserveChangeKey", + lambda: self.nodes[2].fundrawtransaction( + hexstring=rawTx, options={"reserveChangeKey": True} + ), + ) def test_invalid_change_address(self): self.log.info("Test fundrawtxn with an invalid change address") utx = get_unspent(self.nodes[2].listunspent(), 5000000) - inputs = [{'txid': utx['txid'], 'vout': utx['vout']}] + inputs = [{"txid": utx["txid"], "vout": utx["vout"]}] outputs = {self.nodes[0].getnewaddress(): Decimal(4000000)} rawTx = self.nodes[2].createrawtransaction(inputs, outputs) dec_tx = self.nodes[2].decoderawtransaction(rawTx) - assert_equal(utx['txid'], dec_tx['vin'][0]['txid']) + assert_equal(utx["txid"], dec_tx["vin"][0]["txid"]) assert_raises_rpc_error( - -5, "Change address must be a valid bitcoin address", - self.nodes[2].fundrawtransaction, rawTx, {'changeAddress': 'foobar'}) + -5, + "Change address must be a valid bitcoin address", + self.nodes[2].fundrawtransaction, + rawTx, + {"changeAddress": "foobar"}, + ) def test_valid_change_address(self): self.log.info("Test fundrawtxn with a provided change address") utx = get_unspent(self.nodes[2].listunspent(), 5000000) - inputs = [{'txid': utx['txid'], 'vout': utx['vout']}] + inputs = [{"txid": utx["txid"], "vout": utx["vout"]}] outputs = {self.nodes[0].getnewaddress(): Decimal(4000000)} rawTx = self.nodes[2].createrawtransaction(inputs, outputs) dec_tx = self.nodes[2].decoderawtransaction(rawTx) - assert_equal(utx['txid'], dec_tx['vin'][0]['txid']) + assert_equal(utx["txid"], dec_tx["vin"][0]["txid"]) change = self.nodes[2].getnewaddress() - assert_raises_rpc_error(-8, "changePosition out of bounds", self.nodes[ - 2].fundrawtransaction, rawTx, {'changeAddress': change, 'changePosition': 2}) + assert_raises_rpc_error( + -8, + "changePosition out of bounds", + self.nodes[2].fundrawtransaction, + rawTx, + {"changeAddress": change, "changePosition": 2}, + ) rawtxfund = self.nodes[2].fundrawtransaction( - rawTx, {'changeAddress': change, 'changePosition': 0}) - dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex']) - out = dec_tx['vout'][0] - assert_equal(change, out['scriptPubKey']['addresses'][0]) + rawTx, {"changeAddress": change, "changePosition": 0} + ) + dec_tx = self.nodes[2].decoderawtransaction(rawtxfund["hex"]) + out = dec_tx["vout"][0] + assert_equal(change, out["scriptPubKey"]["addresses"][0]) def test_coin_selection(self): self.log.info("Test fundrawtxn with a vin < required amount") utx = get_unspent(self.nodes[2].listunspent(), 1000000) - inputs = [{'txid': utx['txid'], 'vout': utx['vout']}] + inputs = [{"txid": utx["txid"], "vout": utx["vout"]}] outputs = {self.nodes[0].getnewaddress(): 1000000} rawTx = self.nodes[2].createrawtransaction(inputs, outputs) # 4-byte version + 1-byte vin count + 36-byte prevout then script_len rawTx = f"{rawTx[:82]}0100{rawTx[84:]}" dec_tx = self.nodes[2].decoderawtransaction(rawTx) - assert_equal(utx['txid'], dec_tx['vin'][0]['txid']) - assert_equal("00", dec_tx['vin'][0]['scriptSig']['hex']) + assert_equal(utx["txid"], dec_tx["vin"][0]["txid"]) + assert_equal("00", dec_tx["vin"][0]["scriptSig"]["hex"]) # Should fail without add_inputs: - assert_raises_rpc_error(-4, - "Insufficient funds", - self.nodes[2].fundrawtransaction, - rawTx, - {"add_inputs": False}) + assert_raises_rpc_error( + -4, + "Insufficient funds", + self.nodes[2].fundrawtransaction, + rawTx, + {"add_inputs": False}, + ) # add_inputs is enabled by default rawtxfund = self.nodes[2].fundrawtransaction(rawTx) - dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex']) + dec_tx = self.nodes[2].decoderawtransaction(rawtxfund["hex"]) totalOut = 0 matchingOuts = 0 - for i, out in enumerate(dec_tx['vout']): - totalOut += out['value'] - if out['scriptPubKey']['addresses'][0] in outputs: + for i, out in enumerate(dec_tx["vout"]): + totalOut += out["value"] + if out["scriptPubKey"]["addresses"][0] in outputs: matchingOuts += 1 else: - assert_equal(i, rawtxfund['changepos']) + assert_equal(i, rawtxfund["changepos"]) - assert_equal(utx['txid'], dec_tx['vin'][0]['txid']) - assert_equal("00", dec_tx['vin'][0]['scriptSig']['hex']) + assert_equal(utx["txid"], dec_tx["vin"][0]["txid"]) + assert_equal("00", dec_tx["vin"][0]["scriptSig"]["hex"]) assert_equal(matchingOuts, 1) - assert_equal(len(dec_tx['vout']), 2) + assert_equal(len(dec_tx["vout"]), 2) def test_two_vin(self): self.log.info("Test fundrawtxn with 2 vins") utx = get_unspent(self.nodes[2].listunspent(), 1000000) utx2 = get_unspent(self.nodes[2].listunspent(), 5000000) - inputs = [{'txid': utx['txid'], 'vout': utx['vout']}, - {'txid': utx2['txid'], 'vout': utx2['vout']}] + inputs = [ + {"txid": utx["txid"], "vout": utx["vout"]}, + {"txid": utx2["txid"], "vout": utx2["vout"]}, + ] outputs = {self.nodes[0].getnewaddress(): 6000000} rawTx = self.nodes[2].createrawtransaction(inputs, outputs) dec_tx = self.nodes[2].decoderawtransaction(rawTx) - assert_equal(utx['txid'], dec_tx['vin'][0]['txid']) + assert_equal(utx["txid"], dec_tx["vin"][0]["txid"]) # Should fail without add_inputs: - assert_raises_rpc_error(-4, - "Insufficient funds", - self.nodes[2].fundrawtransaction, - rawTx, - {"add_inputs": False}) - rawtxfund = self.nodes[2].fundrawtransaction( - rawTx, {"add_inputs": True}) + assert_raises_rpc_error( + -4, + "Insufficient funds", + self.nodes[2].fundrawtransaction, + rawTx, + {"add_inputs": False}, + ) + rawtxfund = self.nodes[2].fundrawtransaction(rawTx, {"add_inputs": True}) - dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex']) + dec_tx = self.nodes[2].decoderawtransaction(rawtxfund["hex"]) totalOut = 0 matchingOuts = 0 - for out in dec_tx['vout']: - totalOut += out['value'] - if out['scriptPubKey']['addresses'][0] in outputs: + for out in dec_tx["vout"]: + totalOut += out["value"] + if out["scriptPubKey"]["addresses"][0] in outputs: matchingOuts += 1 assert_equal(matchingOuts, 1) - assert_equal(len(dec_tx['vout']), 2) + assert_equal(len(dec_tx["vout"]), 2) matchingIns = 0 - for vinOut in dec_tx['vin']: + for vinOut in dec_tx["vin"]: for vinIn in inputs: - if vinIn['txid'] == vinOut['txid']: + if vinIn["txid"] == vinOut["txid"]: matchingIns += 1 # we now must see two vins identical to vins given as params assert_equal(matchingIns, 2) def test_two_vin_two_vout(self): self.log.info("Test fundrawtxn with 2 vins and 2 vouts") utx = get_unspent(self.nodes[2].listunspent(), 1000000) utx2 = get_unspent(self.nodes[2].listunspent(), 5000000) - inputs = [{'txid': utx['txid'], 'vout': utx['vout']}, - {'txid': utx2['txid'], 'vout': utx2['vout']}] + inputs = [ + {"txid": utx["txid"], "vout": utx["vout"]}, + {"txid": utx2["txid"], "vout": utx2["vout"]}, + ] outputs = { - self.nodes[0].getnewaddress(): 6000000, self.nodes[0].getnewaddress(): 1000000} + self.nodes[0].getnewaddress(): 6000000, + self.nodes[0].getnewaddress(): 1000000, + } rawTx = self.nodes[2].createrawtransaction(inputs, outputs) dec_tx = self.nodes[2].decoderawtransaction(rawTx) - assert_equal(utx['txid'], dec_tx['vin'][0]['txid']) + assert_equal(utx["txid"], dec_tx["vin"][0]["txid"]) # Should fail without add_inputs: - assert_raises_rpc_error(-4, - "Insufficient funds", - self.nodes[2].fundrawtransaction, - rawTx, - {"add_inputs": False}) - rawtxfund = self.nodes[2].fundrawtransaction( - rawTx, {"add_inputs": True}) + assert_raises_rpc_error( + -4, + "Insufficient funds", + self.nodes[2].fundrawtransaction, + rawTx, + {"add_inputs": False}, + ) + rawtxfund = self.nodes[2].fundrawtransaction(rawTx, {"add_inputs": True}) - dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex']) + dec_tx = self.nodes[2].decoderawtransaction(rawtxfund["hex"]) totalOut = 0 matchingOuts = 0 - for out in dec_tx['vout']: - totalOut += out['value'] - if out['scriptPubKey']['addresses'][0] in outputs: + for out in dec_tx["vout"]: + totalOut += out["value"] + if out["scriptPubKey"]["addresses"][0] in outputs: matchingOuts += 1 assert_equal(matchingOuts, 2) - assert_equal(len(dec_tx['vout']), 3) + assert_equal(len(dec_tx["vout"]), 3) def test_invalid_input(self): self.log.info("Test fundrawtxn with an invalid vin") inputs = [ - {'txid': "1c7f966dab21119bac53213a2bc7532bff1fa844c124fd750a7d0b1332440bd1", 'vout': 0}] + { + "txid": ( + "1c7f966dab21119bac53213a2bc7532bff1fa844c124fd750a7d0b1332440bd1" + ), + "vout": 0, + } + ] outputs = {self.nodes[0].getnewaddress(): 1000000} rawTx = self.nodes[2].createrawtransaction(inputs, outputs) assert_raises_rpc_error( - -4, "Insufficient funds", self.nodes[2].fundrawtransaction, rawTx) + -4, "Insufficient funds", self.nodes[2].fundrawtransaction, rawTx + ) def test_fee_p2pkh(self): """Compare fee of a standard pubkeyhash transaction.""" self.log.info("Test fundrawtxn p2pkh fee") inputs = [] outputs = {self.nodes[1].getnewaddress(): 1100000} rawTx = self.nodes[0].createrawtransaction(inputs, outputs) fundedTx = self.nodes[0].fundrawtransaction(rawTx) # Create same transaction over sendtoaddress. - txId = self.nodes[0].sendtoaddress( - self.nodes[1].getnewaddress(), 1100000) - signedFee = self.nodes[0].getrawmempool(True)[txId]['fees']['base'] + txId = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1100000) + signedFee = self.nodes[0].getrawmempool(True)[txId]["fees"]["base"] # Compare fee. - feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee) + feeDelta = Decimal(fundedTx["fee"]) - Decimal(signedFee) assert feeDelta >= 0 and feeDelta <= self.fee_tolerance def test_fee_p2pkh_multi_out(self): """Compare fee of a standard pubkeyhash transaction with multiple outputs.""" self.log.info("Test fundrawtxn p2pkh fee with multiple outputs") inputs = [] outputs = { self.nodes[1].getnewaddress(): 1100000, self.nodes[1].getnewaddress(): 1200000, self.nodes[1].getnewaddress(): 100000, self.nodes[1].getnewaddress(): 1300000, self.nodes[1].getnewaddress(): 200000, self.nodes[1].getnewaddress(): 300000, } rawtx = self.nodes[0].createrawtransaction(inputs, outputs) fundedTx = self.nodes[0].fundrawtransaction(rawtx) # Create same transaction over sendtoaddress. txId = self.nodes[0].sendmany("", outputs) - signedFee = self.nodes[0].getrawmempool(True)[txId]['fees']['base'] + signedFee = self.nodes[0].getrawmempool(True)[txId]["fees"]["base"] # Compare fee. - feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee) + feeDelta = Decimal(fundedTx["fee"]) - Decimal(signedFee) assert feeDelta >= 0 and feeDelta <= self.fee_tolerance def test_fee_p2sh(self): """Compare fee of a 2-of-2 multisig p2sh transaction.""" # Create 2-of-2 addr. addr1 = self.nodes[1].getnewaddress() addr2 = self.nodes[1].getnewaddress() addr1Obj = self.nodes[1].getaddressinfo(addr1) addr2Obj = self.nodes[1].getaddressinfo(addr2) mSigObj = self.nodes[3].createmultisig( - 2, [addr1Obj['pubkey'], addr2Obj['pubkey']])['address'] + 2, [addr1Obj["pubkey"], addr2Obj["pubkey"]] + )["address"] inputs = [] outputs = {mSigObj: 1100000} rawTx = self.nodes[0].createrawtransaction(inputs, outputs) fundedTx = self.nodes[0].fundrawtransaction(rawTx) # Create same transaction over sendtoaddress. txId = self.nodes[0].sendtoaddress(mSigObj, 1100000) - signedFee = self.nodes[0].getrawmempool(True)[txId]['fees']['base'] + signedFee = self.nodes[0].getrawmempool(True)[txId]["fees"]["base"] # Compare fee. - feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee) + feeDelta = Decimal(fundedTx["fee"]) - Decimal(signedFee) assert feeDelta >= 0 and feeDelta <= self.fee_tolerance def test_fee_4of5(self): """Compare fee of a standard pubkeyhash transaction.""" self.log.info("Test fundrawtxn fee with 4-of-5 addresses") # Create 4-of-5 addr. addr1 = self.nodes[1].getnewaddress() addr2 = self.nodes[1].getnewaddress() addr3 = self.nodes[1].getnewaddress() addr4 = self.nodes[1].getnewaddress() addr5 = self.nodes[1].getnewaddress() addr1Obj = self.nodes[1].getaddressinfo(addr1) addr2Obj = self.nodes[1].getaddressinfo(addr2) addr3Obj = self.nodes[1].getaddressinfo(addr3) addr4Obj = self.nodes[1].getaddressinfo(addr4) addr5Obj = self.nodes[1].getaddressinfo(addr5) mSigObj = self.nodes[1].createmultisig( 4, [ - addr1Obj['pubkey'], - addr2Obj['pubkey'], - addr3Obj['pubkey'], - addr4Obj['pubkey'], - addr5Obj['pubkey'], - ] - )['address'] + addr1Obj["pubkey"], + addr2Obj["pubkey"], + addr3Obj["pubkey"], + addr4Obj["pubkey"], + addr5Obj["pubkey"], + ], + )["address"] inputs = [] outputs = {mSigObj: 1100000} rawTx = self.nodes[0].createrawtransaction(inputs, outputs) fundedTx = self.nodes[0].fundrawtransaction(rawTx) # Create same transaction over sendtoaddress. txId = self.nodes[0].sendtoaddress(mSigObj, 1100000) - signedFee = self.nodes[0].getrawmempool(True)[txId]['fees']['base'] + signedFee = self.nodes[0].getrawmempool(True)[txId]["fees"]["base"] # Compare fee. - feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee) + feeDelta = Decimal(fundedTx["fee"]) - Decimal(signedFee) assert feeDelta >= 0 and feeDelta <= self.fee_tolerance def test_spend_2of2(self): """Spend a 2-of-2 multisig transaction over fundraw.""" self.log.info("Test fundpsbt spending 2-of-2 multisig") # Create 2-of-2 addr. addr1 = self.nodes[2].getnewaddress() addr2 = self.nodes[2].getnewaddress() addr1Obj = self.nodes[2].getaddressinfo(addr1) addr2Obj = self.nodes[2].getaddressinfo(addr2) - self.nodes[2].createwallet(wallet_name='wmulti', - disable_private_keys=True) - wmulti = self.nodes[2].get_wallet_rpc('wmulti') + self.nodes[2].createwallet(wallet_name="wmulti", disable_private_keys=True) + wmulti = self.nodes[2].get_wallet_rpc("wmulti") w2 = self.nodes[2].get_wallet_rpc(self.default_wallet_name) mSigObj = wmulti.addmultisigaddress( 2, [ - addr1Obj['pubkey'], - addr2Obj['pubkey'], - ] - )['address'] + addr1Obj["pubkey"], + addr2Obj["pubkey"], + ], + )["address"] if not self.options.descriptors: wmulti.importaddress(mSigObj) # Send 1,200,000 XEC to msig addr. self.nodes[0].sendtoaddress(mSigObj, 1200000) self.generate(self.nodes[0], 1) oldBalance = self.nodes[1].getbalance() inputs = [] outputs = {self.nodes[1].getnewaddress(): 1100000} funded_psbt = wmulti.walletcreatefundedpsbt( - inputs=inputs, outputs=outputs, - options={'changeAddress': w2.getrawchangeaddress()})['psbt'] + inputs=inputs, + outputs=outputs, + options={"changeAddress": w2.getrawchangeaddress()}, + )["psbt"] signed_psbt = w2.walletprocesspsbt(funded_psbt) - final_psbt = w2.finalizepsbt(signed_psbt['psbt']) - self.nodes[2].sendrawtransaction(final_psbt['hex']) + final_psbt = w2.finalizepsbt(signed_psbt["psbt"]) + self.nodes[2].sendrawtransaction(final_psbt["hex"]) self.generate(self.nodes[2], 1) # Make sure funds are received at node1. - assert_equal( - oldBalance + Decimal('1100000.00'), self.nodes[1].getbalance()) + assert_equal(oldBalance + Decimal("1100000.00"), self.nodes[1].getbalance()) wmulti.unloadwallet() def test_locked_wallet(self): - self.log.info( - "Test fundrawtxn with locked wallet and hardened derivation") + self.log.info("Test fundrawtxn with locked wallet and hardened derivation") self.nodes[1].encryptwallet("test") if self.options.descriptors: - self.nodes[1].walletpassphrase('test', 10) - self.nodes[1].importdescriptors([{ - 'desc': descsum_create('pkh(tprv8ZgxMBicQKsPdYeeZbPSKd2KYLmeVKtcFA7kqCxDvDR13MQ6us8HopUR2wLcS2ZKPhLyKsqpDL2FtL73LMHcgoCL7DXsciA8eX8nbjCR2eG/0h/*h)'), - 'timestamp': 'now', - 'active': True - }, - { - 'desc': descsum_create('pkh(tprv8ZgxMBicQKsPdYeeZbPSKd2KYLmeVKtcFA7kqCxDvDR13MQ6us8HopUR2wLcS2ZKPhLyKsqpDL2FtL73LMHcgoCL7DXsciA8eX8nbjCR2eG/1h/*h)'), - 'timestamp': 'now', - 'active': True, - 'internal': True - }]) + self.nodes[1].walletpassphrase("test", 10) + self.nodes[1].importdescriptors( + [ + { + "desc": descsum_create( + "pkh(tprv8ZgxMBicQKsPdYeeZbPSKd2KYLmeVKtcFA7kqCxDvDR13MQ6us8HopUR2wLcS2ZKPhLyKsqpDL2FtL73LMHcgoCL7DXsciA8eX8nbjCR2eG/0h/*h)" + ), + "timestamp": "now", + "active": True, + }, + { + "desc": descsum_create( + "pkh(tprv8ZgxMBicQKsPdYeeZbPSKd2KYLmeVKtcFA7kqCxDvDR13MQ6us8HopUR2wLcS2ZKPhLyKsqpDL2FtL73LMHcgoCL7DXsciA8eX8nbjCR2eG/1h/*h)" + ), + "timestamp": "now", + "active": True, + "internal": True, + }, + ] + ) self.nodes[1].walletlock() # Drain the keypool. self.nodes[1].getnewaddress() self.nodes[1].getrawchangeaddress() inputs = [] outputs = {self.nodes[0].getnewaddress(): 1099997.00} rawtx = self.nodes[1].createrawtransaction(inputs, outputs) # fund a transaction that does not require a new key for the change # output self.nodes[1].fundrawtransaction(rawtx) # fund a transaction that requires a new key for the change output # creating the key must be impossible because the wallet is locked outputs = {self.nodes[0].getnewaddress(): 1100000} rawtx = self.nodes[1].createrawtransaction(inputs, outputs) assert_raises_rpc_error( -4, - "Transaction needs a change address, but we can't generate it. Please call keypoolrefill first.", + ( + "Transaction needs a change address, but we can't generate it. Please" + " call keypoolrefill first." + ), self.nodes[1].fundrawtransaction, - rawtx) + rawtx, + ) # Refill the keypool. self.nodes[1].walletpassphrase("test", 100) # need to refill the keypool to get an internal change address self.nodes[1].keypoolrefill(8) self.nodes[1].walletlock() - assert_raises_rpc_error(-13, "walletpassphrase", self.nodes[ - 1].sendtoaddress, self.nodes[0].getnewaddress(), 1200000) + assert_raises_rpc_error( + -13, + "walletpassphrase", + self.nodes[1].sendtoaddress, + self.nodes[0].getnewaddress(), + 1200000, + ) oldBalance = self.nodes[0].getbalance() inputs = [] outputs = {self.nodes[0].getnewaddress(): 1100000} rawTx = self.nodes[1].createrawtransaction(inputs, outputs) fundedTx = self.nodes[1].fundrawtransaction(rawTx) # Now we need to unlock. self.nodes[1].walletpassphrase("test", 600) - signedTx = self.nodes[1].signrawtransactionwithwallet(fundedTx['hex']) - self.nodes[1].sendrawtransaction(signedTx['hex']) + signedTx = self.nodes[1].signrawtransactionwithwallet(fundedTx["hex"]) + self.nodes[1].sendrawtransaction(signedTx["hex"]) self.generate(self.nodes[1], 1) # Make sure funds are received at node1. - assert_equal( - oldBalance + Decimal('51100000.00'), self.nodes[0].getbalance()) + assert_equal(oldBalance + Decimal("51100000.00"), self.nodes[0].getbalance()) def test_many_inputs_fee(self): """Multiple (~19) inputs tx test | Compare fee.""" self.log.info("Test fundrawtxn fee with many inputs") # Empty node1, send some small coins from node0 to node1. self.nodes[1].sendtoaddress( - self.nodes[0].getnewaddress(), self.nodes[1].getbalance(), "", "", True) + self.nodes[0].getnewaddress(), self.nodes[1].getbalance(), "", "", True + ) self.generate(self.nodes[1], 1) for _ in range(20): self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 10000) self.generate(self.nodes[0], 1) # Fund a tx with ~20 small inputs. inputs = [] outputs = { - self.nodes[0].getnewaddress(): 150000, self.nodes[0].getnewaddress(): 40000} + self.nodes[0].getnewaddress(): 150000, + self.nodes[0].getnewaddress(): 40000, + } rawTx = self.nodes[1].createrawtransaction(inputs, outputs) fundedTx = self.nodes[1].fundrawtransaction(rawTx) # Create same transaction over sendtoaddress. txId = self.nodes[1].sendmany("", outputs) - signedFee = self.nodes[1].getrawmempool(True)[txId]['fees']['base'] + signedFee = self.nodes[1].getrawmempool(True)[txId]["fees"]["base"] # Compare fee. - feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee) + feeDelta = Decimal(fundedTx["fee"]) - Decimal(signedFee) # ~19 inputs assert feeDelta >= 0 and feeDelta <= self.fee_tolerance * 19 def test_many_inputs_send(self): """Multiple (~19) inputs tx test | sign/send.""" self.log.info("Test fundrawtxn sign+send with many inputs") # Again, empty node1, send some small coins from node0 to node1. self.nodes[1].sendtoaddress( - self.nodes[0].getnewaddress(), self.nodes[1].getbalance(), "", "", True) + self.nodes[0].getnewaddress(), self.nodes[1].getbalance(), "", "", True + ) self.generate(self.nodes[1], 1) for _ in range(20): self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 10000) self.generate(self.nodes[0], 1) # Fund a tx with ~20 small inputs. oldBalance = self.nodes[0].getbalance() inputs = [] outputs = { - self.nodes[0].getnewaddress(): 150000, self.nodes[0].getnewaddress(): 40000} + self.nodes[0].getnewaddress(): 150000, + self.nodes[0].getnewaddress(): 40000, + } rawTx = self.nodes[1].createrawtransaction(inputs, outputs) fundedTx = self.nodes[1].fundrawtransaction(rawTx) - fundedAndSignedTx = self.nodes[1].signrawtransactionwithwallet( - fundedTx['hex']) - self.nodes[1].sendrawtransaction(fundedAndSignedTx['hex']) + fundedAndSignedTx = self.nodes[1].signrawtransactionwithwallet(fundedTx["hex"]) + self.nodes[1].sendrawtransaction(fundedAndSignedTx["hex"]) self.generate(self.nodes[1], 1) - assert_equal(oldBalance + Decimal('50190000.00'), - self.nodes[0].getbalance()) # 0.19+block reward + assert_equal( + oldBalance + Decimal("50190000.00"), self.nodes[0].getbalance() + ) # 0.19+block reward def test_op_return(self): self.log.info("Test fundrawtxn with OP_RETURN and no vin") rawTx = "0100000000010000000000000000066a047465737400000000" dec_tx = self.nodes[2].decoderawtransaction(rawTx) - assert_equal(len(dec_tx['vin']), 0) - assert_equal(len(dec_tx['vout']), 1) + assert_equal(len(dec_tx["vin"]), 0) + assert_equal(len(dec_tx["vout"]), 1) rawtxfund = self.nodes[2].fundrawtransaction(rawTx) - dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex']) + dec_tx = self.nodes[2].decoderawtransaction(rawtxfund["hex"]) # at least one vin - assert_greater_than(len(dec_tx['vin']), 0) + assert_greater_than(len(dec_tx["vin"]), 0) # one change output added - assert_equal(len(dec_tx['vout']), 2) + assert_equal(len(dec_tx["vout"]), 2) def test_watchonly(self): self.log.info("Test fundrawtxn using only watchonly") inputs = [] outputs = {self.nodes[2].getnewaddress(): self.watchonly_amount / 2} rawTx = self.nodes[3].createrawtransaction(inputs, outputs) - self.nodes[3].loadwallet('wwatch') - wwatch = self.nodes[3].get_wallet_rpc('wwatch') + self.nodes[3].loadwallet("wwatch") + wwatch = self.nodes[3].get_wallet_rpc("wwatch") # Setup change addresses for the watchonly wallet - desc_import = [{ - "desc": descsum_create( - "pkh(tpubD6NzVbkrYhZ4YNXVQbNhMK1WqguFsUXceaVJKbmno2aZ3B6QfbMeraaYvnBSGpV3vxLyTTK9DYT1yoEck4XUScMzXoQ2U2oSmE2JyMedq3H/1/*)"), - "timestamp": "now", - "internal": True, - "active": True, - "keypool": True, - "range": [0, 100], - "watchonly": True, - }] + desc_import = [ + { + "desc": descsum_create( + "pkh(tpubD6NzVbkrYhZ4YNXVQbNhMK1WqguFsUXceaVJKbmno2aZ3B6QfbMeraaYvnBSGpV3vxLyTTK9DYT1yoEck4XUScMzXoQ2U2oSmE2JyMedq3H/1/*)" + ), + "timestamp": "now", + "internal": True, + "active": True, + "keypool": True, + "range": [0, 100], + "watchonly": True, + } + ] if self.options.descriptors: wwatch.importdescriptors(desc_import) else: wwatch.importmulti(desc_import) # Backward compatibility test (2nd params is includeWatching) result = wwatch.fundrawtransaction(rawTx, True) res_dec = self.nodes[0].decoderawtransaction(result["hex"]) assert_equal(len(res_dec["vin"]), 1) assert_equal(res_dec["vin"][0]["txid"], self.watchonly_txid) assert "fee" in result.keys() assert_greater_than(result["changepos"], -1) wwatch.unloadwallet() def test_all_watched_funds(self): self.log.info("Test fundrawtxn using entirety of watched funds") inputs = [] outputs = {self.nodes[2].getnewaddress(): self.watchonly_amount} rawTx = self.nodes[3].createrawtransaction(inputs, outputs) - self.nodes[3].loadwallet('wwatch') - wwatch = self.nodes[3].get_wallet_rpc('wwatch') + self.nodes[3].loadwallet("wwatch") + wwatch = self.nodes[3].get_wallet_rpc("wwatch") w3 = self.nodes[3].get_wallet_rpc(self.default_wallet_name) result = wwatch.fundrawtransaction( rawTx, - {'includeWatching': True, 'changeAddress': w3.getrawchangeaddress(), - 'subtractFeeFromOutputs': [0]}) + { + "includeWatching": True, + "changeAddress": w3.getrawchangeaddress(), + "subtractFeeFromOutputs": [0], + }, + ) res_dec = self.nodes[0].decoderawtransaction(result["hex"]) assert_equal(len(res_dec["vin"]), 1) assert res_dec["vin"][0]["txid"] == self.watchonly_txid assert_greater_than(result["fee"], 0) assert_equal(result["changepos"], -1) - assert_equal(result["fee"] + res_dec["vout"][0]["value"], - self.watchonly_amount) + assert_equal(result["fee"] + res_dec["vout"][0]["value"], self.watchonly_amount) signedtx = wwatch.signrawtransactionwithwallet(result["hex"]) assert not signedtx["complete"] signedtx = self.nodes[0].signrawtransactionwithwallet(signedtx["hex"]) assert signedtx["complete"] self.nodes[0].sendrawtransaction(signedtx["hex"]) self.generate(self.nodes[0], 1) wwatch.unloadwallet() def test_option_feerate(self): self.log.info("Test fundrawtxn feeRate option") # Make sure there is exactly one input so coin selection can't skew the # result. assert_equal(len(self.nodes[3].listunspent(1)), 1) inputs = [] outputs = {self.nodes[3].getnewaddress(): 1000000} rawTx = self.nodes[3].createrawtransaction(inputs, outputs) # uses self.min_relay_tx_fee (set by settxfee) result = self.nodes[3].fundrawtransaction(rawTx) result2 = self.nodes[3].fundrawtransaction( - rawTx, {"feeRate": 2 * self.min_relay_tx_fee}) - result_fee_rate = result['fee'] * 1000 / \ - FromHex(CTransaction(), result['hex']).billable_size() + rawTx, {"feeRate": 2 * self.min_relay_tx_fee} + ) + result_fee_rate = ( + result["fee"] + * 1000 + / FromHex(CTransaction(), result["hex"]).billable_size() + ) assert_fee_amount( - result2['fee'], FromHex(CTransaction(), result2['hex']).billable_size(), 2 * result_fee_rate) + result2["fee"], + FromHex(CTransaction(), result2["hex"]).billable_size(), + 2 * result_fee_rate, + ) result3 = self.nodes[3].fundrawtransaction( - rawTx, {"feeRate": 10 * self.min_relay_tx_fee}) - assert_raises_rpc_error(-4, - "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)", - self.nodes[3].fundrawtransaction, - rawTx, - {"feeRate": 1000000}) + rawTx, {"feeRate": 10 * self.min_relay_tx_fee} + ) + assert_raises_rpc_error( + -4, + "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)", + self.nodes[3].fundrawtransaction, + rawTx, + {"feeRate": 1000000}, + ) # allow this transaction to be underfunded by 10 bytes. This is due # to the first transaction possibly being overfunded by up to .9 # satoshi due to fee ceilings being used. assert_fee_amount( - result3['fee'], FromHex(CTransaction(), result3['hex']).billable_size(), 10 * result_fee_rate, 10) + result3["fee"], + FromHex(CTransaction(), result3["hex"]).billable_size(), + 10 * result_fee_rate, + 10, + ) def test_address_reuse(self): """Test no address reuse occurs.""" self.log.info("Test fundrawtxn does not reuse addresses") rawTx = self.nodes[3].createrawtransaction( - inputs=[], outputs={self.nodes[3].getnewaddress(): 1000000}) + inputs=[], outputs={self.nodes[3].getnewaddress(): 1000000} + ) result3 = self.nodes[3].fundrawtransaction(rawTx) res_dec = self.nodes[0].decoderawtransaction(result3["hex"]) changeaddress = "" - for out in res_dec['vout']: - if out['value'] > 1000000.0: - changeaddress += out['scriptPubKey']['addresses'][0] + for out in res_dec["vout"]: + if out["value"] > 1000000.0: + changeaddress += out["scriptPubKey"]["addresses"][0] assert changeaddress != "" nextaddr = self.nodes[3].getnewaddress() # Now the change address key should be removed from the keypool. assert changeaddress != nextaddr def test_option_subtract_fee_from_outputs(self): self.log.info("Test fundrawtxn subtractFeeFromOutputs option") # Make sure there is exactly one input so coin selection can't skew the # result. assert_equal(len(self.nodes[3].listunspent(1)), 1) inputs = [] outputs = {self.nodes[2].getnewaddress(): 1000000} rawTx = self.nodes[3].createrawtransaction(inputs, outputs) # uses self.min_relay_tx_fee (set by settxfee) - result = [self.nodes[3].fundrawtransaction(rawTx), - # empty subtraction list - self.nodes[3].fundrawtransaction( - rawTx, {"subtractFeeFromOutputs": []}), - # uses self.min_relay_tx_fee (set by settxfee) - self.nodes[3].fundrawtransaction( - rawTx, {"subtractFeeFromOutputs": [0]}), - self.nodes[3].fundrawtransaction( - rawTx, {"feeRate": 2 * self.min_relay_tx_fee}), - self.nodes[3].fundrawtransaction( - rawTx, {"feeRate": 2 * self.min_relay_tx_fee, "subtractFeeFromOutputs": [0]}), - ] - - dec_tx = [self.nodes[3].decoderawtransaction(tx_['hex']) - for tx_ in result] - output = [d['vout'][1 - r['changepos']]['value'] - for d, r in zip(dec_tx, result)] - change = [d['vout'][r['changepos']]['value'] - for d, r in zip(dec_tx, result)] - - assert_equal(result[0]['fee'], result[1]['fee'], result[2]['fee']) - assert_equal(result[3]['fee'], result[4]['fee']) + result = [ + self.nodes[3].fundrawtransaction(rawTx), + # empty subtraction list + self.nodes[3].fundrawtransaction(rawTx, {"subtractFeeFromOutputs": []}), + # uses self.min_relay_tx_fee (set by settxfee) + self.nodes[3].fundrawtransaction(rawTx, {"subtractFeeFromOutputs": [0]}), + self.nodes[3].fundrawtransaction( + rawTx, {"feeRate": 2 * self.min_relay_tx_fee} + ), + self.nodes[3].fundrawtransaction( + rawTx, + {"feeRate": 2 * self.min_relay_tx_fee, "subtractFeeFromOutputs": [0]}, + ), + ] + + dec_tx = [self.nodes[3].decoderawtransaction(tx_["hex"]) for tx_ in result] + output = [ + d["vout"][1 - r["changepos"]]["value"] for d, r in zip(dec_tx, result) + ] + change = [d["vout"][r["changepos"]]["value"] for d, r in zip(dec_tx, result)] + + assert_equal(result[0]["fee"], result[1]["fee"], result[2]["fee"]) + assert_equal(result[3]["fee"], result[4]["fee"]) assert_equal(change[0], change[1]) assert_equal(output[0], output[1]) - assert_equal(output[0], output[2] + result[2]['fee']) - assert_equal(change[0] + result[0]['fee'], change[2]) - assert_equal(output[3], output[4] + result[4]['fee']) - assert_equal(change[3] + result[3]['fee'], change[4]) + assert_equal(output[0], output[2] + result[2]["fee"]) + assert_equal(change[0] + result[0]["fee"], change[2]) + assert_equal(output[3], output[4] + result[4]["fee"]) + assert_equal(change[3] + result[3]["fee"], change[4]) inputs = [] outputs = { - self.nodes[2].getnewaddress(): value for value in (1000000.0, 1100000.0, 1200000.0, 1300000.0)} + self.nodes[2].getnewaddress(): value + for value in (1000000.0, 1100000.0, 1200000.0, 1300000.0) + } rawTx = self.nodes[3].createrawtransaction(inputs, outputs) # Split the fee between outputs 0, 2, and 3, but not output 1 - result = [self.nodes[3].fundrawtransaction(rawTx), - self.nodes[3].fundrawtransaction(rawTx, {"subtractFeeFromOutputs": [0, 2, 3]})] - - dec_tx = [self.nodes[3].decoderawtransaction(result[0]['hex']), - self.nodes[3].decoderawtransaction(result[1]['hex'])] + result = [ + self.nodes[3].fundrawtransaction(rawTx), + self.nodes[3].fundrawtransaction( + rawTx, {"subtractFeeFromOutputs": [0, 2, 3]} + ), + ] + + dec_tx = [ + self.nodes[3].decoderawtransaction(result[0]["hex"]), + self.nodes[3].decoderawtransaction(result[1]["hex"]), + ] # Nested list of non-change output amounts for each transaction. - output = [[out['value'] for i, out in enumerate(d['vout']) if i != r['changepos']] - for d, r in zip(dec_tx, result)] + output = [ + [out["value"] for i, out in enumerate(d["vout"]) if i != r["changepos"]] + for d, r in zip(dec_tx, result) + ] # List of differences in output amounts between normal and subtractFee # transactions. share = [o0 - o1 for o0, o1 in zip(output[0], output[1])] # Output 1 is the same in both transactions. assert_equal(share[1], 0) # The other 3 outputs are smaller as a result of # subtractFeeFromOutputs. assert_greater_than(share[0], 0) assert_greater_than(share[2], 0) assert_greater_than(share[3], 0) # Outputs 2 and 3 take the same share of the fee. assert_equal(share[2], share[3]) # Output 0 takes at least as much share of the fee, and no more than 2 # satoshis more, than outputs 2 and 3. assert_greater_than_or_equal(share[0], share[2]) assert_greater_than_or_equal(share[2] + Decimal("2e-8"), share[0]) # The fee is the same in both transactions. - assert_equal(result[0]['fee'], result[1]['fee']) + assert_equal(result[0]["fee"], result[1]["fee"]) # The total subtracted from the outputs is equal to the fee. - assert_equal(share[0] + share[2] + share[3], result[0]['fee']) + assert_equal(share[0] + share[2] + share[3], result[0]["fee"]) def test_subtract_fee_with_presets(self): self.log.info( - "Test fundrawtxn subtract fee from outputs with preset inputs that are sufficient") + "Test fundrawtxn subtract fee from outputs with preset inputs that are" + " sufficient" + ) addr = self.nodes[0].getnewaddress() txid = self.nodes[0].sendtoaddress(addr, 10000000) vout = find_vout_for_address(self.nodes[0], txid, addr) - rawtx = self.nodes[0].createrawtransaction([{'txid': txid, 'vout': vout}], [ - {self.nodes[0].getnewaddress(): 5000000}]) + rawtx = self.nodes[0].createrawtransaction( + [{"txid": txid, "vout": vout}], [{self.nodes[0].getnewaddress(): 5000000}] + ) fundedtx = self.nodes[0].fundrawtransaction( - rawtx, {'subtractFeeFromOutputs': [0]}) - signedtx = self.nodes[0].signrawtransactionwithwallet(fundedtx['hex']) - self.nodes[0].sendrawtransaction(signedtx['hex']) + rawtx, {"subtractFeeFromOutputs": [0]} + ) + signedtx = self.nodes[0].signrawtransactionwithwallet(fundedtx["hex"]) + self.nodes[0].sendrawtransaction(signedtx["hex"]) -if __name__ == '__main__': +if __name__ == "__main__": RawTransactionsTest().main() diff --git a/test/functional/rpc_generate.py b/test/functional/rpc_generate.py index 97ed1cd0a..3f7f7f0b4 100755 --- a/test/functional/rpc_generate.py +++ b/test/functional/rpc_generate.py @@ -1,36 +1,35 @@ #!/usr/bin/env python3 # 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. """Test generate RPC.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error class RPCGenerateTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 def run_test(self): message = ( "generate\n\n" "has been replaced by the -generate cli option. Refer to -help " "for more information." ) - self.log.info( - "Test rpc generate raises with message to use cli option") + self.log.info("Test rpc generate raises with message to use cli option") assert_raises_rpc_error(-32601, message, self.nodes[0].rpc.generate) - self.log.info( - "Test rpc generate help prints message to use cli option") + self.log.info("Test rpc generate help prints message to use cli option") assert_equal(message, self.nodes[0].help("generate")) self.log.info( - "Test rpc generate is a hidden command not discoverable in general help") + "Test rpc generate is a hidden command not discoverable in general help" + ) assert message not in self.nodes[0].help() if __name__ == "__main__": RPCGenerateTest().main() diff --git a/test/functional/rpc_generateblock.py b/test/functional/rpc_generateblock.py index 5df9c0fc0..de5d2f82b 100755 --- a/test/functional/rpc_generateblock.py +++ b/test/functional/rpc_generateblock.py @@ -1,150 +1,161 @@ #!/usr/bin/env python3 # 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. -'''Test generateblock rpc. -''' +"""Test generateblock rpc. +""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error class GenerateBlockTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 def skip_test_if_missing_module(self): self.skip_if_no_wallet() def run_test(self): node = self.nodes[0] - self.log.info('Generate an empty block to address') + self.log.info("Generate an empty block to address") address = node.getnewaddress() - blockhash = self.generateblock( - node, output=address, transactions=[])['hash'] + blockhash = self.generateblock(node, output=address, transactions=[])["hash"] block = node.getblock(blockhash=blockhash, verbose=2) - assert_equal(len(block['tx']), 1) - assert_equal(block['tx'][0]['vout'][0] - ['scriptPubKey']['addresses'][0], address) + assert_equal(len(block["tx"]), 1) + assert_equal(block["tx"][0]["vout"][0]["scriptPubKey"]["addresses"][0], address) - self.log.info('Generate an empty block to a descriptor') - blockhash = self.generateblock(node, f"addr({address})", [])['hash'] + self.log.info("Generate an empty block to a descriptor") + blockhash = self.generateblock(node, f"addr({address})", [])["hash"] block = node.getblock(blockhash=blockhash, verbosity=2) - assert_equal(len(block['tx']), 1) - assert_equal(block['tx'][0]['vout'][0] - ['scriptPubKey']['addresses'][0], address) + assert_equal(len(block["tx"]), 1) + assert_equal(block["tx"][0]["vout"][0]["scriptPubKey"]["addresses"][0], address) self.log.info( - 'Generate an empty block to a combo descriptor with compressed pubkey') - combo_key = '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798' - combo_address = 'ecregtest:qp63uahgrxged4z5jswyt5dn5v3lzsem6c49crxznd' - blockhash = self.generateblock(node, f"combo({combo_key})", [])['hash'] + "Generate an empty block to a combo descriptor with compressed pubkey" + ) + combo_key = "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + combo_address = "ecregtest:qp63uahgrxged4z5jswyt5dn5v3lzsem6c49crxznd" + blockhash = self.generateblock(node, f"combo({combo_key})", [])["hash"] block = node.getblock(blockhash, 2) - assert_equal(len(block['tx']), 1) - assert_equal(block['tx'][0]['vout'][0]['scriptPubKey'] - ['addresses'][0], combo_address) + assert_equal(len(block["tx"]), 1) + assert_equal( + block["tx"][0]["vout"][0]["scriptPubKey"]["addresses"][0], combo_address + ) self.log.info( - 'Generate an empty block to a combo descriptor with uncompressed pubkey') - combo_key = '0408ef68c46d20596cc3f6ddf7c8794f71913add807f1dc55949fa805d764d191c0b7ce6894c126fce0babc6663042f3dde9b0cf76467ea315514e5a6731149c67' - combo_address = 'ecregtest:qqmagqc48ln8p7zk6ez2h64amcamr86qwqku5075py' - blockhash = self.generateblock(node, f"combo({combo_key})", [])['hash'] + "Generate an empty block to a combo descriptor with uncompressed pubkey" + ) + combo_key = "0408ef68c46d20596cc3f6ddf7c8794f71913add807f1dc55949fa805d764d191c0b7ce6894c126fce0babc6663042f3dde9b0cf76467ea315514e5a6731149c67" + combo_address = "ecregtest:qqmagqc48ln8p7zk6ez2h64amcamr86qwqku5075py" + blockhash = self.generateblock(node, f"combo({combo_key})", [])["hash"] block = node.getblock(blockhash, 2) - assert_equal(len(block['tx']), 1) - assert_equal(block['tx'][0]['vout'][0]['scriptPubKey'] - ['addresses'][0], combo_address) + assert_equal(len(block["tx"]), 1) + assert_equal( + block["tx"][0]["vout"][0]["scriptPubKey"]["addresses"][0], combo_address + ) # Generate 110 blocks to spend self.generatetoaddress(node, 110, address) # Generate some extra mempool transactions to verify they don't get # mined for _ in range(10): node.sendtoaddress(address, 1000) - self.log.info('Generate block with txid') + self.log.info("Generate block with txid") txid = node.sendtoaddress(address, 1000000) - blockhash = self.generateblock(node, address, [txid])['hash'] + blockhash = self.generateblock(node, address, [txid])["hash"] block = node.getblock(blockhash, 1) - assert_equal(len(block['tx']), 2) - assert_equal(block['tx'][1], txid) + assert_equal(len(block["tx"]), 2) + assert_equal(block["tx"][1], txid) - self.log.info('Generate block with raw tx') + self.log.info("Generate block with raw tx") utxos = node.listunspent(addresses=[address]) raw = node.createrawtransaction( - [{'txid': utxos[0]['txid'], 'vout':utxos[0]['vout']}], [{address: 1000000}]) - signed_raw = node.signrawtransactionwithwallet(raw)['hex'] - blockhash = self.generateblock(node, address, [signed_raw])['hash'] + [{"txid": utxos[0]["txid"], "vout": utxos[0]["vout"]}], [{address: 1000000}] + ) + signed_raw = node.signrawtransactionwithwallet(raw)["hex"] + blockhash = self.generateblock(node, address, [signed_raw])["hash"] block = node.getblock(blockhash, 1) - assert_equal(len(block['tx']), 2) - txid = block['tx'][1] - assert_equal(node.gettransaction(txid)['hex'], signed_raw) + assert_equal(len(block["tx"]), 2) + txid = block["tx"][1] + assert_equal(node.gettransaction(txid)["hex"], signed_raw) - self.log.info('Fail to generate block with out of order txs') + self.log.info("Fail to generate block with out of order txs") raw1 = node.createrawtransaction( - [{'txid': txid, 'vout': 0}], [{address: 999900}]) - signed_raw1 = node.signrawtransactionwithwallet(raw1)['hex'] + [{"txid": txid, "vout": 0}], [{address: 999900}] + ) + signed_raw1 = node.signrawtransactionwithwallet(raw1)["hex"] txid1 = node.sendrawtransaction(signed_raw1) raw2 = node.createrawtransaction( - [{'txid': txid1, 'vout': 0}], [{address: 999000}]) - signed_raw2 = node.signrawtransactionwithwallet(raw2)['hex'] + [{"txid": txid1, "vout": 0}], [{address: 999000}] + ) + signed_raw2 = node.signrawtransactionwithwallet(raw2)["hex"] txid2 = node.sendrawtransaction(signed_raw2) # Reversed CTOR txids = sorted([txid1, txid2], reverse=True) - assert_raises_rpc_error(-25, - 'TestBlockValidity failed: tx-ordering', - self.generateblock, - node, - address, - txids) - - self.log.info('Fail to generate block with txid not in mempool') - missing_txid = '0000000000000000000000000000000000000000000000000000000000000000' - assert_raises_rpc_error(-5, - f"Transaction {missing_txid} not in mempool.", - self.generateblock, - node, - address, - [missing_txid]) - - self.log.info('Fail to generate block with invalid raw tx') - invalid_raw_tx = '0000' - assert_raises_rpc_error(-22, - f"Transaction decode failed for {invalid_raw_tx}", - self.generateblock, - node, - address, - [invalid_raw_tx]) - - self.log.info('Fail to generate block with invalid address/descriptor') - assert_raises_rpc_error(-5, - 'Invalid address or descriptor', - self.generateblock, - node, - '1234', - []) - - self.log.info('Fail to generate block with a ranged descriptor') - ranged_descriptor = 'pkh(tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp/0/*)' + assert_raises_rpc_error( + -25, + "TestBlockValidity failed: tx-ordering", + self.generateblock, + node, + address, + txids, + ) + + self.log.info("Fail to generate block with txid not in mempool") + missing_txid = ( + "0000000000000000000000000000000000000000000000000000000000000000" + ) + assert_raises_rpc_error( + -5, + f"Transaction {missing_txid} not in mempool.", + self.generateblock, + node, + address, + [missing_txid], + ) + + self.log.info("Fail to generate block with invalid raw tx") + invalid_raw_tx = "0000" + assert_raises_rpc_error( + -22, + f"Transaction decode failed for {invalid_raw_tx}", + self.generateblock, + node, + address, + [invalid_raw_tx], + ) + + self.log.info("Fail to generate block with invalid address/descriptor") + assert_raises_rpc_error( + -5, "Invalid address or descriptor", self.generateblock, node, "1234", [] + ) + + self.log.info("Fail to generate block with a ranged descriptor") + ranged_descriptor = "pkh(tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp/0/*)" assert_raises_rpc_error( -8, - 'Ranged descriptor not accepted. Maybe pass through deriveaddresses first?', + "Ranged descriptor not accepted. Maybe pass through deriveaddresses first?", self.generateblock, node, ranged_descriptor, - []) + [], + ) - self.log.info( - 'Fail to generate block with a descriptor missing a private key') - child_descriptor = 'pkh(tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp/0\'/0)' - assert_raises_rpc_error(-5, - 'Cannot derive script without private keys', - self.generateblock, - node, - child_descriptor, - []) + self.log.info("Fail to generate block with a descriptor missing a private key") + child_descriptor = "pkh(tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp/0'/0)" + assert_raises_rpc_error( + -5, + "Cannot derive script without private keys", + self.generateblock, + node, + child_descriptor, + [], + ) -if __name__ == '__main__': +if __name__ == "__main__": GenerateBlockTest().main() diff --git a/test/functional/rpc_getblockfilter.py b/test/functional/rpc_getblockfilter.py index e14bbac29..0888657b1 100755 --- a/test/functional/rpc_getblockfilter.py +++ b/test/functional/rpc_getblockfilter.py @@ -1,76 +1,80 @@ #!/usr/bin/env python3 # Copyright (c) 2018 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 getblockfilter RPC.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, assert_is_hex_string, assert_raises_rpc_error, ) FILTER_TYPES = ["basic"] class GetBlockFilterTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 - self.extra_args = [["-blockfilterindex", - "-noparkdeepreorg"], ["-noparkdeepreorg"]] + self.extra_args = [ + ["-blockfilterindex", "-noparkdeepreorg"], + ["-noparkdeepreorg"], + ] def run_test(self): # Create two chains by disconnecting nodes 0 & 1, mining, then # reconnecting self.disconnect_nodes(0, 1) self.generate(self.nodes[0], 3, sync_fun=self.no_op) self.generate(self.nodes[1], 4, sync_fun=self.no_op) assert_equal(self.nodes[0].getblockcount(), 3) - chain0_hashes = [self.nodes[0].getblockhash( - block_height) for block_height in range(4)] + chain0_hashes = [ + self.nodes[0].getblockhash(block_height) for block_height in range(4) + ] # Reorg node 0 to a new chain self.connect_nodes(0, 1) self.sync_blocks() assert_equal(self.nodes[0].getblockcount(), 4) - chain1_hashes = [self.nodes[0].getblockhash( - block_height) for block_height in range(4)] + chain1_hashes = [ + self.nodes[0].getblockhash(block_height) for block_height in range(4) + ] # Test getblockfilter returns a filter for all blocks and filter types # on active chain for block_hash in chain1_hashes: for filter_type in FILTER_TYPES: result = self.nodes[0].getblockfilter(block_hash, filter_type) - assert_is_hex_string(result['filter']) + assert_is_hex_string(result["filter"]) # Test getblockfilter returns a filter for all blocks and filter types # on stale chain for block_hash in chain0_hashes: for filter_type in FILTER_TYPES: result = self.nodes[0].getblockfilter(block_hash, filter_type) - assert_is_hex_string(result['filter']) + assert_is_hex_string(result["filter"]) # Test getblockfilter with unknown block bad_block_hash = "0123456789abcdef" * 4 - assert_raises_rpc_error(-5, - "Block not found", - self.nodes[0].getblockfilter, - bad_block_hash, - "basic") + assert_raises_rpc_error( + -5, "Block not found", self.nodes[0].getblockfilter, bad_block_hash, "basic" + ) # Test getblockfilter with undefined filter type genesis_hash = self.nodes[0].getblockhash(0) - assert_raises_rpc_error(-5, - "Unknown filtertype", - self.nodes[0].getblockfilter, - genesis_hash, - "unknown") + assert_raises_rpc_error( + -5, + "Unknown filtertype", + self.nodes[0].getblockfilter, + genesis_hash, + "unknown", + ) -if __name__ == '__main__': +if __name__ == "__main__": GetBlockFilterTest().main() diff --git a/test/functional/rpc_getblockfrompeer.py b/test/functional/rpc_getblockfrompeer.py index 9b7590ba0..e20918ab5 100755 --- a/test/functional/rpc_getblockfrompeer.py +++ b/test/functional/rpc_getblockfrompeer.py @@ -1,94 +1,93 @@ #!/usr/bin/env python3 # 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. """Test the getblockfrompeer RPC.""" from test_framework.authproxy import JSONRPCException from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error class GetBlockFromPeerTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 self.extra_args = [[], ["-noparkdeepreorg"]] def setup_network(self): self.setup_nodes() # Don't connect the nodes initially def check_for_block(self, blockhash): try: self.nodes[0].getblock(blockhash) return True except JSONRPCException: return False def run_test(self): self.log.info("Mine 4 blocks on Node 0") self.generate(self.nodes[0], 4, sync_fun=self.no_op) assert_equal(self.nodes[0].getblockcount(), 204) self.log.info("Mine competing 3 blocks on Node 1") self.generate(self.nodes[1], 3, sync_fun=self.no_op) assert_equal(self.nodes[1].getblockcount(), 203) short_tip = self.nodes[1].getbestblockhash() self.log.info("Connect nodes to sync headers") self.connect_nodes(0, 1) self.sync_blocks() - self.log.info( - "Node 0 should only have the header for node 1's block 3") - x = next( - filter( - lambda x: x['hash'] == short_tip, - self.nodes[0].getchaintips())) - assert_equal(x['status'], "headers-only") - assert_raises_rpc_error(-1, - "Block not found on disk", - self.nodes[0].getblock, - short_tip) + self.log.info("Node 0 should only have the header for node 1's block 3") + x = next(filter(lambda x: x["hash"] == short_tip, self.nodes[0].getchaintips())) + assert_equal(x["status"], "headers-only") + assert_raises_rpc_error( + -1, "Block not found on disk", self.nodes[0].getblock, short_tip + ) self.log.info("Fetch block from node 1") peers = self.nodes[0].getpeerinfo() assert_equal(len(peers), 1) peer_0_peer_1_id = peers[0]["id"] self.log.info("Arguments must be sensible") - assert_raises_rpc_error(-8, - "hash must be of length 64 (not 4, for '1234')", - self.nodes[0].getblockfrompeer, - "1234", - 0) + assert_raises_rpc_error( + -8, + "hash must be of length 64 (not 4, for '1234')", + self.nodes[0].getblockfrompeer, + "1234", + 0, + ) self.log.info("We must already have the header") - assert_raises_rpc_error(-1, - "Block header missing", - self.nodes[0].getblockfrompeer, - "00" * 32, - 0) + assert_raises_rpc_error( + -1, "Block header missing", self.nodes[0].getblockfrompeer, "00" * 32, 0 + ) self.log.info("Non-existent peer generates error") - assert_raises_rpc_error(-1, - "Peer does not exist", - self.nodes[0].getblockfrompeer, - short_tip, - peer_0_peer_1_id + 1) + assert_raises_rpc_error( + -1, + "Peer does not exist", + self.nodes[0].getblockfrompeer, + short_tip, + peer_0_peer_1_id + 1, + ) self.log.info("Successful fetch") result = self.nodes[0].getblockfrompeer(short_tip, peer_0_peer_1_id) self.wait_until(lambda: self.check_for_block(short_tip), timeout=1) assert_equal(result, {}) self.log.info("Don't fetch blocks we already have") - assert_raises_rpc_error(-1, - "Block already downloaded", - self.nodes[0].getblockfrompeer, - short_tip, - peer_0_peer_1_id) + assert_raises_rpc_error( + -1, + "Block already downloaded", + self.nodes[0].getblockfrompeer, + short_tip, + peer_0_peer_1_id, + ) -if __name__ == '__main__': +if __name__ == "__main__": GetBlockFromPeerTest().main() diff --git a/test/functional/rpc_getblockstats.py b/test/functional/rpc_getblockstats.py index 33dcaf445..f80b142ff 100755 --- a/test/functional/rpc_getblockstats.py +++ b/test/functional/rpc_getblockstats.py @@ -1,200 +1,223 @@ #!/usr/bin/env python3 # Copyright (c) 2017-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 getblockstats rpc call # import decimal import json import os from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error TESTSDIR = os.path.dirname(os.path.realpath(__file__)) def EncodeDecimal(o): if isinstance(o, decimal.Decimal): # json.load will read a quoted float as a string and not convert it back # to decimal, so store the value as unquoted float instead. return float(o) raise TypeError(f"{repr(o)} is not JSON serializable") class GetblockstatsTest(BitcoinTestFramework): - start_height = 101 max_stat_pos = 2 def add_options(self, parser): - parser.add_argument('--gen-test-data', dest='gen_test_data', - default=False, action='store_true', - help='Generate test data') - parser.add_argument('--test-data', dest='test_data', - default='data/rpc_getblockstats.json', - action='store', metavar='FILE', - help='Test data file') + parser.add_argument( + "--gen-test-data", + dest="gen_test_data", + default=False, + action="store_true", + help="Generate test data", + ) + parser.add_argument( + "--test-data", + dest="test_data", + default="data/rpc_getblockstats.json", + action="store", + metavar="FILE", + help="Test data file", + ) def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True self.supports_cli = False def get_stats(self): - return [self.nodes[0].getblockstats( - hash_or_height=self.start_height + i) for i in range(self.max_stat_pos + 1)] + return [ + self.nodes[0].getblockstats(hash_or_height=self.start_height + i) + for i in range(self.max_stat_pos + 1) + ] def generate_test_data(self, filename): mocktime = 1525107225 self.nodes[0].setmocktime(mocktime) self.generate(self.nodes[0], 101) address = self.nodes[0].get_deterministic_priv_key().address self.nodes[0].sendtoaddress( - address=address, - amount=10000000, - subtractfeefromamount=True) + address=address, amount=10000000, subtractfeefromamount=True + ) self.generate(self.nodes[0], 1) self.nodes[0].sendtoaddress( - address=address, - amount=10000000, - subtractfeefromamount=True) + address=address, amount=10000000, subtractfeefromamount=True + ) self.nodes[0].sendtoaddress( - address=address, - amount=10000000, - subtractfeefromamount=False) + address=address, amount=10000000, subtractfeefromamount=False + ) self.nodes[0].settxfee(amount=3000) self.nodes[0].sendtoaddress( - address=address, - amount=1000000, - subtractfeefromamount=True) + address=address, amount=1000000, subtractfeefromamount=True + ) self.sync_all() self.generate(self.nodes[0], 1) self.expected_stats = self.get_stats() blocks = [] tip = self.nodes[0].getbestblockhash() blockhash = None height = 0 while tip != blockhash: blockhash = self.nodes[0].getblockhash(height) blocks.append(self.nodes[0].getblock(blockhash, 0)) height += 1 to_dump = { - 'blocks': blocks, - 'mocktime': int(mocktime), - 'stats': self.expected_stats, + "blocks": blocks, + "mocktime": int(mocktime), + "stats": self.expected_stats, } - with open(filename, 'w', encoding="utf8") as f: - json.dump(to_dump, f, sort_keys=True, - indent=2, default=EncodeDecimal) + with open(filename, "w", encoding="utf8") as f: + json.dump(to_dump, f, sort_keys=True, indent=2, default=EncodeDecimal) def load_test_data(self, filename): - with open(filename, 'r', encoding="utf8") as f: + with open(filename, "r", encoding="utf8") as f: d = json.load(f, parse_float=decimal.Decimal) - blocks = d['blocks'] - mocktime = d['mocktime'] - self.expected_stats = d['stats'] + blocks = d["blocks"] + mocktime = d["mocktime"] + self.expected_stats = d["stats"] self.log.info(self.expected_stats) # Set the timestamps from the file so that the nodes can get out of # Initial Block Download self.nodes[0].setmocktime(mocktime) self.sync_all() for i, b in enumerate(blocks): self.nodes[0].submitblock(b) def run_test(self): test_data = os.path.join(TESTSDIR, self.options.test_data) if self.options.gen_test_data: self.generate_test_data(test_data) else: self.load_test_data(test_data) self.sync_all() stats = self.get_stats() # Make sure all valid statistics are included but nothing else is expected_keys = self.expected_stats[0].keys() assert_equal(set(stats[0].keys()), set(expected_keys)) - assert_equal(stats[0]['height'], self.start_height) - assert_equal(stats[self.max_stat_pos]['height'], - self.start_height + self.max_stat_pos) + assert_equal(stats[0]["height"], self.start_height) + assert_equal( + stats[self.max_stat_pos]["height"], self.start_height + self.max_stat_pos + ) for i in range(self.max_stat_pos + 1): - self.log.info(f'Checking block {i}\n') + self.log.info(f"Checking block {i}\n") assert_equal(stats[i], self.expected_stats[i]) # Check selecting block by hash too - blockhash = self.expected_stats[i]['blockhash'] - stats_by_hash = self.nodes[0].getblockstats( - hash_or_height=blockhash) + blockhash = self.expected_stats[i]["blockhash"] + stats_by_hash = self.nodes[0].getblockstats(hash_or_height=blockhash) assert_equal(stats_by_hash, self.expected_stats[i]) # Make sure each stat can be queried on its own for stat in expected_keys: for i in range(self.max_stat_pos + 1): result = self.nodes[0].getblockstats( - hash_or_height=self.start_height + i, stats=[stat]) + hash_or_height=self.start_height + i, stats=[stat] + ) assert_equal(list(result.keys()), [stat]) if result[stat] != self.expected_stats[i][stat]: self.log.info( - f'result[{stat}] ({i}) failed, ' - f'{result[stat]!r} != {self.expected_stats[i][stat]!r}') + f"result[{stat}] ({i}) failed, " + f"{result[stat]!r} != {self.expected_stats[i][stat]!r}" + ) assert_equal(result[stat], self.expected_stats[i][stat]) # Make sure only the selected statistics are included (more than one) - some_stats = {'minfee', 'maxfee'} - stats = self.nodes[0].getblockstats( - hash_or_height=1, stats=list(some_stats)) + some_stats = {"minfee", "maxfee"} + stats = self.nodes[0].getblockstats(hash_or_height=1, stats=list(some_stats)) assert_equal(set(stats.keys()), some_stats) # Test invalid parameters raise the proper json exceptions tip = self.start_height + self.max_stat_pos assert_raises_rpc_error( - -8, f'Target block height {tip + 1} after current tip {tip}', - self.nodes[0].getblockstats, hash_or_height=tip + 1) + -8, + f"Target block height {tip + 1} after current tip {tip}", + self.nodes[0].getblockstats, + hash_or_height=tip + 1, + ) assert_raises_rpc_error( - -8, 'Target block height -1 is negative', - self.nodes[0].getblockstats, hash_or_height=-1) + -8, + "Target block height -1 is negative", + self.nodes[0].getblockstats, + hash_or_height=-1, + ) # Make sure not valid stats aren't allowed - inv_sel_stat = 'asdfghjkl' + inv_sel_stat = "asdfghjkl" inv_stats = [ [inv_sel_stat], - ['minfee', inv_sel_stat], - [inv_sel_stat, 'minfee'], - ['minfee', inv_sel_stat, 'maxfee'], + ["minfee", inv_sel_stat], + [inv_sel_stat, "minfee"], + ["minfee", inv_sel_stat, "maxfee"], ] for inv_stat in inv_stats: assert_raises_rpc_error( - -8, f'Invalid selected statistic {inv_sel_stat}', - self.nodes[0].getblockstats, hash_or_height=1, stats=inv_stat) + -8, + f"Invalid selected statistic {inv_sel_stat}", + self.nodes[0].getblockstats, + hash_or_height=1, + stats=inv_stat, + ) # Make sure we aren't always returning inv_sel_stat as the culprit stat # Mainchain's genesis block shouldn't be found on regtest - assert_raises_rpc_error(-5, 'Block not found', self.nodes[0].getblockstats, - hash_or_height='000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f') + assert_raises_rpc_error( + -5, + "Block not found", + self.nodes[0].getblockstats, + hash_or_height=( + "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" + ), + ) # Invalid number of args - assert_raises_rpc_error(-1, - 'getblockstats hash_or_height ( stats )', - self.nodes[0].getblockstats, - '00', - 1, - 2) - assert_raises_rpc_error(-1, - 'getblockstats hash_or_height ( stats )', - self.nodes[0].getblockstats) - - -if __name__ == '__main__': + assert_raises_rpc_error( + -1, + "getblockstats hash_or_height ( stats )", + self.nodes[0].getblockstats, + "00", + 1, + 2, + ) + assert_raises_rpc_error( + -1, "getblockstats hash_or_height ( stats )", self.nodes[0].getblockstats + ) + + +if __name__ == "__main__": GetblockstatsTest().main() diff --git a/test/functional/rpc_getchaintips.py b/test/functional/rpc_getchaintips.py index 64eb85135..29eb5ac68 100755 --- a/test/functional/rpc_getchaintips.py +++ b/test/functional/rpc_getchaintips.py @@ -1,66 +1,64 @@ #!/usr/bin/env python3 # 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 getchaintips RPC. - introduce a network split - work on chains of different lengths - join the network together again - verify that getchaintips now returns two chain tips. """ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal -class GetChainTipsTest (BitcoinTestFramework): +class GetChainTipsTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 4 self.extra_args = [["-noparkdeepreorg"], ["-noparkdeepreorg"], [], []] def run_test(self): tips = self.nodes[0].getchaintips() assert_equal(len(tips), 1) - assert_equal(tips[0]['branchlen'], 0) - assert_equal(tips[0]['height'], 200) - assert_equal(tips[0]['status'], 'active') + assert_equal(tips[0]["branchlen"], 0) + assert_equal(tips[0]["height"], 200) + assert_equal(tips[0]["status"], "active") # Split the network and build two chains of different lengths. self.split_network() - self.generate(self.nodes[0], 10, - sync_fun=lambda: self.sync_all(self.nodes[:2])) - self.generate(self.nodes[2], 20, - sync_fun=lambda: self.sync_all(self.nodes[2:])) + self.generate(self.nodes[0], 10, sync_fun=lambda: self.sync_all(self.nodes[:2])) + self.generate(self.nodes[2], 20, sync_fun=lambda: self.sync_all(self.nodes[2:])) tips = self.nodes[1].getchaintips() assert_equal(len(tips), 1) shortTip = tips[0] - assert_equal(shortTip['branchlen'], 0) - assert_equal(shortTip['height'], 210) - assert_equal(tips[0]['status'], 'active') + assert_equal(shortTip["branchlen"], 0) + assert_equal(shortTip["height"], 210) + assert_equal(tips[0]["status"], "active") tips = self.nodes[3].getchaintips() assert_equal(len(tips), 1) longTip = tips[0] - assert_equal(longTip['branchlen'], 0) - assert_equal(longTip['height'], 220) - assert_equal(tips[0]['status'], 'active') + assert_equal(longTip["branchlen"], 0) + assert_equal(longTip["height"], 220) + assert_equal(tips[0]["status"], "active") # Join the network halves and check that we now have two tips # (at least at the nodes that previously had the short chain). self.join_network() tips = self.nodes[0].getchaintips() assert_equal(len(tips), 2) assert_equal(tips[0], longTip) - assert_equal(tips[1]['branchlen'], 10) - assert_equal(tips[1]['status'], 'valid-fork') - tips[1]['branchlen'] = 0 - tips[1]['status'] = 'active' + assert_equal(tips[1]["branchlen"], 10) + assert_equal(tips[1]["status"], "valid-fork") + tips[1]["branchlen"] = 0 + tips[1]["status"] = "active" assert_equal(tips[1], shortTip) -if __name__ == '__main__': +if __name__ == "__main__": GetChainTipsTest().main() diff --git a/test/functional/rpc_getdescriptorinfo.py b/test/functional/rpc_getdescriptorinfo.py index 5d5a80432..3b998cf97 100755 --- a/test/functional/rpc_getdescriptorinfo.py +++ b/test/functional/rpc_getdescriptorinfo.py @@ -1,92 +1,102 @@ #!/usr/bin/env python3 # Copyright (c) 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 getdescriptorinfo RPC. """ from test_framework.descriptors import descsum_create from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error class DescriptorTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 self.extra_args = [["-disablewallet"]] self.wallet_names = [] def test_desc(self, desc, isrange, issolvable, hasprivatekeys): info = self.nodes[0].getdescriptorinfo(desc) - assert_equal( - info, self.nodes[0].getdescriptorinfo( - descsum_create(desc))) - assert_equal(info['descriptor'], descsum_create(desc)) - assert_equal(info['isrange'], isrange) - assert_equal(info['issolvable'], issolvable) - assert_equal(info['hasprivatekeys'], hasprivatekeys) + assert_equal(info, self.nodes[0].getdescriptorinfo(descsum_create(desc))) + assert_equal(info["descriptor"], descsum_create(desc)) + assert_equal(info["isrange"], isrange) + assert_equal(info["issolvable"], issolvable) + assert_equal(info["hasprivatekeys"], hasprivatekeys) def run_test(self): - assert_raises_rpc_error(-1, 'getdescriptorinfo', - self.nodes[0].getdescriptorinfo) - assert_raises_rpc_error(-3, 'Expected type string', - self.nodes[0].getdescriptorinfo, 1) - assert_raises_rpc_error(-5, - 'is not a valid descriptor function', - self.nodes[0].getdescriptorinfo, - '') + assert_raises_rpc_error( + -1, "getdescriptorinfo", self.nodes[0].getdescriptorinfo + ) + assert_raises_rpc_error( + -3, "Expected type string", self.nodes[0].getdescriptorinfo, 1 + ) + assert_raises_rpc_error( + -5, + "is not a valid descriptor function", + self.nodes[0].getdescriptorinfo, + "", + ) # P2PK output with the specified public key. self.test_desc( - 'pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)', + "pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)", isrange=False, issolvable=True, - hasprivatekeys=False) + hasprivatekeys=False, + ) # P2PKH output with the specified public key. self.test_desc( - 'pkh(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)', + "pkh(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)", isrange=False, issolvable=True, - hasprivatekeys=False) + hasprivatekeys=False, + ) # Any P2PK, P2PKH output with the specified public key. self.test_desc( - 'combo(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)', + "combo(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)", isrange=False, issolvable=True, - hasprivatekeys=False) + hasprivatekeys=False, + ) # A bare *1-of-2* multisig output with keys in the specified order. self.test_desc( - 'multi(1,022f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4,025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc)', + "multi(1,022f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4,025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc)", isrange=False, issolvable=True, - hasprivatekeys=False) + hasprivatekeys=False, + ) # A P2SH *2-of-2* multisig output with keys in the specified order. self.test_desc( - 'sh(multi(2,022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01,03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe))', + "sh(multi(2,022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01,03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe))", isrange=False, issolvable=True, - hasprivatekeys=False) + hasprivatekeys=False, + ) # A P2PK output with the public key of the specified xpub. self.test_desc( - 'pk(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B)', + "pk(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B)", isrange=False, issolvable=True, - hasprivatekeys=False) + hasprivatekeys=False, + ) # A P2PKH output with child key *1'/2* of the specified xpub. self.test_desc( "pkh(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1'/2)", isrange=False, issolvable=True, - hasprivatekeys=False) + hasprivatekeys=False, + ) # A set of P2PKH outputs, but additionally specifies that the specified # xpub is a child of a master with fingerprint `d34db33f`, and derived # using path `44'/0'/0'`. self.test_desc( "pkh([d34db33f/44'/0'/0']tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/*)", isrange=True, issolvable=True, - hasprivatekeys=False) + hasprivatekeys=False, + ) -if __name__ == '__main__': +if __name__ == "__main__": DescriptorTest().main() diff --git a/test/functional/rpc_help.py b/test/functional/rpc_help.py index ef488dffa..cf211ecf8 100755 --- a/test/functional/rpc_help.py +++ b/test/functional/rpc_help.py @@ -1,144 +1,161 @@ #!/usr/bin/env python3 # Copyright (c) 2018 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 help output.""" import os import re from collections import defaultdict from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error def parse_string(s): assert s[0] == '"' assert s[-1] == '"' return s[1:-1] def process_mapping(fname): """Find and parse conversion table in implementation file `fname`.""" cmds = [] in_rpcs = False with open(fname, "r", encoding="utf8") as f: for line in f: line = line.rstrip() if not in_rpcs: - if line == 'static const CRPCConvertParam vRPCConvertParams[] = {': + if line == "static const CRPCConvertParam vRPCConvertParams[] = {": in_rpcs = True else: - if line.startswith('};'): + if line.startswith("};"): in_rpcs = False - elif '{' in line and '"' in line: - m = re.search(r'{ *("[^"]*"), *([0-9]+) *, *("[^"]*") *},', - line) - assert m, f'No match to table expression: {line}' + elif "{" in line and '"' in line: + m = re.search(r'{ *("[^"]*"), *([0-9]+) *, *("[^"]*") *},', line) + assert m, f"No match to table expression: {line}" name = parse_string(m.group(1)) idx = int(m.group(2)) argname = parse_string(m.group(3)) cmds.append((name, idx, argname)) assert not in_rpcs and cmds return cmds class HelpRpcTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 self.supports_cli = False def run_test(self): self.test_client_conversion_table() self.test_categories() self.dump_help() if self.is_wallet_compiled(): self.wallet_help() def test_client_conversion_table(self): file_conversion_table = os.path.join( - self.config["environment"]["SRCDIR"], 'src', 'rpc', 'client.cpp') + self.config["environment"]["SRCDIR"], "src", "rpc", "client.cpp" + ) mapping_client = process_mapping(file_conversion_table) # Ignore echojson in client table - mapping_client = [m for m in mapping_client if m[0] != 'echojson'] + mapping_client = [m for m in mapping_client if m[0] != "echojson"] mapping_server = self.nodes[0].help("dump_all_command_conversions") # Filter all RPCs whether they need conversion - mapping_server_conversion = [tuple(m[:3]) - for m in mapping_server if not m[3]] + mapping_server_conversion = [tuple(m[:3]) for m in mapping_server if not m[3]] # Only check if all RPC methods have been compiled (i.e. wallet is # enabled) - if self.is_wallet_compiled() and sorted( - mapping_client) != sorted(mapping_server_conversion): + if self.is_wallet_compiled() and sorted(mapping_client) != sorted( + mapping_server_conversion + ): raise AssertionError( - f"RPC client conversion table ({file_conversion_table}) and " - f"RPC server named arguments mismatch!\n" - f"{set(mapping_client).symmetric_difference(mapping_server_conversion)}", + ( + f"RPC client conversion table ({file_conversion_table}) and " + "RPC server named arguments mismatch!\n" + f"{set(mapping_client).symmetric_difference(mapping_server_conversion)}" + ), ) # Check for conversion difference by argument name. # It is preferable for API consistency that arguments with the same name # have the same conversion, so bin by argument name. all_methods_by_argname = defaultdict(list) converts_by_argname = defaultdict(list) for m in mapping_server: all_methods_by_argname[m[2]].append(m[0]) converts_by_argname[m[2]].append(m[3]) for argname, convert in converts_by_argname.items(): if all(convert) != any(convert): # Only allow dummy to fail consistency check - assert argname == 'dummy', ( - f'WARNING: conversion mismatch for argument named ' - f'{argname} ({list(zip(all_methods_by_argname[argname], converts_by_argname[argname]))})' + assert argname == "dummy", ( + "WARNING: conversion mismatch for argument named " + f"{argname} ({list(zip(all_methods_by_argname[argname], converts_by_argname[argname]))})" ) def test_categories(self): node = self.nodes[0] # wrong argument count - assert_raises_rpc_error(-1, 'help', node.help, 'foo', 'bar') + assert_raises_rpc_error(-1, "help", node.help, "foo", "bar") # invalid argument - assert_raises_rpc_error(-1, - 'JSON value is not a string as expected', node.help, 0) + assert_raises_rpc_error( + -1, "JSON value is not a string as expected", node.help, 0 + ) # help of unknown command - assert_equal(node.help('foo'), 'help: unknown command: foo') + assert_equal(node.help("foo"), "help: unknown command: foo") # command titles - titles = [line[3:-3] - for line in node.help().splitlines() if line.startswith('==')] - components = ['Avalanche', 'Blockchain', 'Control', 'Generating', - 'Mining', 'Network', 'Rawtransactions', 'Util'] + titles = [ + line[3:-3] for line in node.help().splitlines() if line.startswith("==") + ] + components = [ + "Avalanche", + "Blockchain", + "Control", + "Generating", + "Mining", + "Network", + "Rawtransactions", + "Util", + ] if self.is_wallet_compiled(): - components.append('Wallet') + components.append("Wallet") if self.is_zmq_compiled(): - components.append('Zmq') + components.append("Zmq") assert_equal(titles, components) def dump_help(self): - dump_dir = os.path.join(self.options.tmpdir, 'rpc_help_dump') + dump_dir = os.path.join(self.options.tmpdir, "rpc_help_dump") os.mkdir(dump_dir) - calls = [line.split(' ', 1)[0] for line in self.nodes[0].help( - ).splitlines() if line and not line.startswith('==')] + calls = [ + line.split(" ", 1)[0] + for line in self.nodes[0].help().splitlines() + if line and not line.startswith("==") + ] for call in calls: - with open(os.path.join(dump_dir, call), 'w', encoding='utf-8') as f: + with open(os.path.join(dump_dir, call), "w", encoding="utf-8") as f: # Make sure the node can generate the help at runtime without # crashing f.write(self.nodes[0].help(call)) def wallet_help(self): - assert ('getnewaddress ( "label" "address_type" )' in - self.nodes[0].help('getnewaddress')) - self.restart_node(0, extra_args=['-nowallet=1']) - assert ('getnewaddress ( "label" "address_type" )' in - self.nodes[0].help('getnewaddress')) + assert 'getnewaddress ( "label" "address_type" )' in self.nodes[0].help( + "getnewaddress" + ) + self.restart_node(0, extra_args=["-nowallet=1"]) + assert 'getnewaddress ( "label" "address_type" )' in self.nodes[0].help( + "getnewaddress" + ) -if __name__ == '__main__': +if __name__ == "__main__": HelpRpcTest().main() diff --git a/test/functional/rpc_invalidateblock.py b/test/functional/rpc_invalidateblock.py index bbef14d0f..093aed130 100755 --- a/test/functional/rpc_invalidateblock.py +++ b/test/functional/rpc_invalidateblock.py @@ -1,91 +1,96 @@ #!/usr/bin/env python3 # 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 invalidateblock RPC.""" from test_framework.address import ADDRESS_ECREG_UNSPENDABLE_DESCRIPTOR from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal class InvalidateTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 self.extra_args = [["-noparkdeepreorg"], [], []] def setup_network(self): self.setup_nodes() def run_test(self): self.log.info( - "Make sure we repopulate setBlockIndexCandidates after InvalidateBlock:") + "Make sure we repopulate setBlockIndexCandidates after InvalidateBlock:" + ) self.log.info("Mine 4 blocks on Node 0") self.generate(self.nodes[0], 4, sync_fun=self.no_op) assert_equal(self.nodes[0].getblockcount(), 4) besthash_n0 = self.nodes[0].getbestblockhash() self.log.info("Mine competing 6 blocks on Node 1") self.generate(self.nodes[1], 6, sync_fun=self.no_op) assert_equal(self.nodes[1].getblockcount(), 6) self.log.info("Connect nodes to force a reorg") self.connect_nodes(0, 1) self.sync_blocks(self.nodes[0:2]) assert_equal(self.nodes[0].getblockcount(), 6) badhash = self.nodes[1].getblockhash(2) self.log.info( - "Invalidate block 2 on node 0 and verify we reorg to node 0's original chain") + "Invalidate block 2 on node 0 and verify we reorg to node 0's original" + " chain" + ) self.nodes[0].invalidateblock(badhash) assert_equal(self.nodes[0].getblockcount(), 4) assert_equal(self.nodes[0].getbestblockhash(), besthash_n0) self.log.info("\nMake sure we won't reorg to a lower work chain:") self.connect_nodes(1, 2) self.log.info("Sync node 2 to node 1 so both have 6 blocks") self.sync_blocks(self.nodes[1:3]) assert_equal(self.nodes[2].getblockcount(), 6) self.log.info("Invalidate block 5 on node 1 so its tip is now at 4") self.nodes[1].invalidateblock(self.nodes[1].getblockhash(5)) assert_equal(self.nodes[1].getblockcount(), 4) self.log.info("Invalidate block 3 on node 2, so its tip is now 2") self.nodes[2].invalidateblock(self.nodes[2].getblockhash(3)) assert_equal(self.nodes[2].getblockcount(), 2) self.log.info("..and then mine a block") self.generate(self.nodes[2], 1, sync_fun=self.no_op) self.log.info("Verify all nodes are at the right height") self.wait_until(lambda: self.nodes[2].getblockcount() == 3, timeout=5) self.wait_until(lambda: self.nodes[0].getblockcount() == 4, timeout=5) self.wait_until(lambda: self.nodes[1].getblockcount() == 4, timeout=5) self.log.info("Verify that we reconsider all ancestors as well") - blocks = self.generatetodescriptor(self.nodes[1], - 10, ADDRESS_ECREG_UNSPENDABLE_DESCRIPTOR, sync_fun=self.no_op) + blocks = self.generatetodescriptor( + self.nodes[1], 10, ADDRESS_ECREG_UNSPENDABLE_DESCRIPTOR, sync_fun=self.no_op + ) assert_equal(self.nodes[1].getbestblockhash(), blocks[-1]) # Invalidate the two blocks at the tip self.nodes[1].invalidateblock(blocks[-1]) self.nodes[1].invalidateblock(blocks[-2]) assert_equal(self.nodes[1].getbestblockhash(), blocks[-3]) # Reconsider only the previous tip self.nodes[1].reconsiderblock(blocks[-1]) # Should be back at the tip by now assert_equal(self.nodes[1].getbestblockhash(), blocks[-1]) self.log.info("Verify that we reconsider all descendants") - blocks = self.generatetodescriptor(self.nodes[1], - 10, ADDRESS_ECREG_UNSPENDABLE_DESCRIPTOR, sync_fun=self.no_op) + blocks = self.generatetodescriptor( + self.nodes[1], 10, ADDRESS_ECREG_UNSPENDABLE_DESCRIPTOR, sync_fun=self.no_op + ) assert_equal(self.nodes[1].getbestblockhash(), blocks[-1]) # Invalidate the two blocks at the tip self.nodes[1].invalidateblock(blocks[-2]) self.nodes[1].invalidateblock(blocks[-4]) assert_equal(self.nodes[1].getbestblockhash(), blocks[-5]) # Reconsider only the previous tip self.nodes[1].reconsiderblock(blocks[-4]) # Should be back at the tip by now assert_equal(self.nodes[1].getbestblockhash(), blocks[-1]) -if __name__ == '__main__': +if __name__ == "__main__": InvalidateTest().main() diff --git a/test/functional/rpc_misc.py b/test/functional/rpc_misc.py index fc648303a..0283d97a3 100755 --- a/test/functional/rpc_misc.py +++ b/test/functional/rpc_misc.py @@ -1,113 +1,119 @@ #!/usr/bin/env python3 # Copyright (c) 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 RPC misc output.""" import xml.etree.ElementTree as ET from test_framework.authproxy import JSONRPCException from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, assert_greater_than, assert_greater_than_or_equal, assert_raises_rpc_error, ) class RpcMiscTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 self.supports_cli = False def run_test(self): node = self.nodes[0] self.log.info("test CHECK_NONFATAL") assert_raises_rpc_error( -1, - 'Internal bug detected: "request.params[9].get_str() != "trigger_internal_bug""', - lambda: node.echo(arg9='trigger_internal_bug'), + ( + 'Internal bug detected: "request.params[9].get_str() !=' + ' "trigger_internal_bug""' + ), + lambda: node.echo(arg9="trigger_internal_bug"), ) self.log.info("test getmemoryinfo") - memory = node.getmemoryinfo()['locked'] - assert_greater_than(memory['used'], 0) - assert_greater_than(memory['free'], 0) - assert_greater_than(memory['total'], 0) + memory = node.getmemoryinfo()["locked"] + assert_greater_than(memory["used"], 0) + assert_greater_than(memory["free"], 0) + assert_greater_than(memory["total"], 0) # assert_greater_than_or_equal() for locked in case locking pages # failed at some point - assert_greater_than_or_equal(memory['locked'], 0) - assert_greater_than(memory['chunks_used'], 0) - assert_greater_than(memory['chunks_free'], 0) - assert_equal(memory['used'] + memory['free'], memory['total']) + assert_greater_than_or_equal(memory["locked"], 0) + assert_greater_than(memory["chunks_used"], 0) + assert_greater_than(memory["chunks_free"], 0) + assert_equal(memory["used"] + memory["free"], memory["total"]) self.log.info("test mallocinfo") try: mallocinfo = node.getmemoryinfo(mode="mallocinfo") self.log.info('getmemoryinfo(mode="mallocinfo") call succeeded') tree = ET.fromstring(mallocinfo) - assert_equal(tree.tag, 'malloc') + assert_equal(tree.tag, "malloc") except JSONRPCException: self.log.info('getmemoryinfo(mode="mallocinfo") not available') - assert_raises_rpc_error(-8, 'mallocinfo is only available when compiled with glibc 2.10+', - node.getmemoryinfo, mode="mallocinfo") + assert_raises_rpc_error( + -8, + "mallocinfo is only available when compiled with glibc 2.10+", + node.getmemoryinfo, + mode="mallocinfo", + ) - assert_raises_rpc_error(-8, "unknown mode foobar", - node.getmemoryinfo, mode="foobar") + assert_raises_rpc_error( + -8, "unknown mode foobar", node.getmemoryinfo, mode="foobar" + ) self.log.info("test logging rpc and help") # Test logging RPC returns the expected number of logging categories. # Use check if it is greater or equal because some logging categories # are behind a preprocessor directive. assert_greater_than_or_equal(len(node.logging()), 26) # Test toggling a logging category on/off/on with the logging RPC. - assert_equal(node.logging()['qt'], True) - node.logging(exclude=['qt']) - assert_equal(node.logging()['qt'], False) - node.logging(include=['qt']) - assert_equal(node.logging()['qt'], True) + assert_equal(node.logging()["qt"], True) + node.logging(exclude=["qt"]) + assert_equal(node.logging()["qt"], False) + node.logging(include=["qt"]) + assert_equal(node.logging()["qt"], True) # Test logging RPC returns the logging categories in alphabetical # order. sorted_logging_categories = sorted(node.logging()) assert_equal(list(node.logging()), sorted_logging_categories) # Test logging help returns the logging categories string in # alphabetical order. - categories = ', '.join(sorted_logging_categories) - logging_help = self.nodes[0].help('logging') + categories = ", ".join(sorted_logging_categories) + logging_help = self.nodes[0].help("logging") assert f"valid logging categories are: {categories}" in logging_help self.log.info("test getindexinfo") # Without any indices running the RPC returns an empty object assert_equal(node.getindexinfo(), {}) # Restart the node with indices and wait for them to sync - self.restart_node( - 0, ["-txindex", "-blockfilterindex", "-coinstatsindex"]) - self.wait_until( - lambda: all(i["synced"] for i in node.getindexinfo().values())) + self.restart_node(0, ["-txindex", "-blockfilterindex", "-coinstatsindex"]) + self.wait_until(lambda: all(i["synced"] for i in node.getindexinfo().values())) # Returns a list of all running indices by default values = {"synced": True, "best_block_height": 200} assert_equal( node.getindexinfo(), { "txindex": values, "basic block filter index": values, "coinstatsindex": values, - } + }, ) # Specifying an index by name returns only the status of that index for i in {"txindex", "basic block filter index", "coinstatsindex"}: assert_equal(node.getindexinfo(i), {i: values}) # Specifying an unknown index name returns an empty result assert_equal(node.getindexinfo("foo"), {}) -if __name__ == '__main__': +if __name__ == "__main__": RpcMiscTest().main() diff --git a/test/functional/rpc_named_arguments.py b/test/functional/rpc_named_arguments.py index 0bdbf6c7e..57df0811b 100755 --- a/test/functional/rpc_named_arguments.py +++ b/test/functional/rpc_named_arguments.py @@ -1,36 +1,38 @@ #!/usr/bin/env python3 # Copyright (c) 2016-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 using named arguments for RPCs.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error class NamedArgumentTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 self.supports_cli = False def run_test(self): node = self.nodes[0] - h = node.help(command='getblockchaininfo') - assert h.startswith('getblockchaininfo\n') + h = node.help(command="getblockchaininfo") + assert h.startswith("getblockchaininfo\n") - assert_raises_rpc_error(-8, 'Unknown named parameter', - node.help, random='getblockchaininfo') + assert_raises_rpc_error( + -8, "Unknown named parameter", node.help, random="getblockchaininfo" + ) h = node.getblockhash(height=0) node.getblock(blockhash=h) assert_equal(node.echo(), []) assert_equal(node.echo(arg0=0, arg9=9), [0] + [None] * 8 + [9]) assert_equal(node.echo(arg1=1), [None, 1]) assert_equal(node.echo(arg9=None), [None] * 10) - assert_equal(node.echo(arg0=0, arg3=3, arg9=9), - [0] + [None] * 2 + [3] + [None] * 5 + [9]) + assert_equal( + node.echo(arg0=0, arg3=3, arg9=9), [0] + [None] * 2 + [3] + [None] * 5 + [9] + ) -if __name__ == '__main__': +if __name__ == "__main__": NamedArgumentTest().main() diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py index 73dcb523a..a9d2b7de0 100755 --- a/test/functional/rpc_net.py +++ b/test/functional/rpc_net.py @@ -1,365 +1,391 @@ #!/usr/bin/env python3 # 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}") + 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.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) + 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) + 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'] + 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 + 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) + 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']): + 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) + 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) + self.wait_until( + lambda: self.nodes[0].getnetworkinfo()["connections"] == 0, timeout=3 + ) - with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: true\n']): + 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) + 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"]) + 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") - ) + 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') + 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) + 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') + 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') + 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') + 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') + 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) + 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) + 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']): + 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")) + 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_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[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') + 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") + 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']): + 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].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) + 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") + 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.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") + 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"]): + 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") + "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}) + 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") + "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"]): + 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__': +if __name__ == "__main__": NetTest().main() diff --git a/test/functional/rpc_packages.py b/test/functional/rpc_packages.py index 64525be59..c036fedb9 100755 --- a/test/functional/rpc_packages.py +++ b/test/functional/rpc_packages.py @@ -1,326 +1,396 @@ #!/usr/bin/env python3 # Copyright (c) 2021 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """RPCs that handle raw transaction packages.""" import random from decimal import Decimal from test_framework.address import ADDRESS_ECREG_P2SH_OP_TRUE, SCRIPTSIG_OP_TRUE from test_framework.messages import CTransaction, FromHex, ToHex from test_framework.test_framework import BitcoinTestFramework from test_framework.txtools import pad_tx from test_framework.util import assert_equal from test_framework.wallet import ( create_child_with_parents, create_raw_chain, make_chain, ) class RPCPackagesTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True def assert_testres_equal(self, package_hex, testres_expected): """Shuffle package_hex and assert that the testmempoolaccept result matches testres_expected. This should only be used to test packages where the order does not matter. The ordering of transactions in package_hex and testres_expected must match. """ shuffled_indices = list(range(len(package_hex))) random.shuffle(shuffled_indices) shuffled_package = [package_hex[i] for i in shuffled_indices] shuffled_testres = [testres_expected[i] for i in shuffled_indices] assert_equal( - shuffled_testres, - self.nodes[0].testmempoolaccept(shuffled_package)) + shuffled_testres, self.nodes[0].testmempoolaccept(shuffled_package) + ) def run_test(self): self.log.info("Generate blocks to create UTXOs") node = self.nodes[0] self.privkeys = [node.get_deterministic_priv_key().key] self.address = node.get_deterministic_priv_key().address self.coins = [] # The last 100 coinbase transactions are premature for b in self.generatetoaddress(node, 300, self.address)[:100]: coinbase = node.getblock(blockhash=b, verbosity=2)["tx"][0] - self.coins.append({ - "txid": coinbase["txid"], - "amount": coinbase["vout"][0]["value"], - "scriptPubKey": coinbase["vout"][0]["scriptPubKey"], - }) + self.coins.append( + { + "txid": coinbase["txid"], + "amount": coinbase["vout"][0]["value"], + "scriptPubKey": coinbase["vout"][0]["scriptPubKey"], + } + ) # Create some transactions that can be reused throughout the test. # Never submit these to mempool. self.independent_txns_hex = [] self.independent_txns_testres = [] for _ in range(3): coin = self.coins.pop() - rawtx = node.createrawtransaction([{"txid": coin["txid"], "vout": 0}], - {self.address: coin["amount"] - Decimal("100.00")}) + rawtx = node.createrawtransaction( + [{"txid": coin["txid"], "vout": 0}], + {self.address: coin["amount"] - Decimal("100.00")}, + ) signedtx = node.signrawtransactionwithkey( - hexstring=rawtx, privkeys=self.privkeys) + hexstring=rawtx, privkeys=self.privkeys + ) assert signedtx["complete"] testres = node.testmempoolaccept([signedtx["hex"]]) assert testres[0]["allowed"] self.independent_txns_hex.append(signedtx["hex"]) # testmempoolaccept returns a list of length one, avoid creating a # 2D list self.independent_txns_testres.append(testres[0]) self.independent_txns_testres_blank = [ - {"txid": res["txid"], } for res in self.independent_txns_testres] + { + "txid": res["txid"], + } + for res in self.independent_txns_testres + ] self.test_independent() self.test_chain() self.test_multiple_children() self.test_multiple_parents() self.test_conflicting() def test_independent(self): self.log.info("Test multiple independent transactions in a package") node = self.nodes[0] # For independent transactions, order doesn't matter. self.assert_testres_equal( - self.independent_txns_hex, self.independent_txns_testres) + self.independent_txns_hex, self.independent_txns_testres + ) self.log.info( - "Test an otherwise valid package with an extra garbage tx appended") + "Test an otherwise valid package with an extra garbage tx appended" + ) garbage_tx = node.createrawtransaction( - [{"txid": "00" * 32, "vout": 5}], {self.address: 1_000_000}) + [{"txid": "00" * 32, "vout": 5}], {self.address: 1_000_000} + ) tx = FromHex(CTransaction(), garbage_tx) pad_tx(tx) garbage_tx = ToHex(tx) # This particular test differs from Core, because we do not test the # missing inputs separately from the signature verification for a given # transaction. Both are done in validation as part of PreChecks. # See https://reviews.bitcoinabc.org/D8203 - testres_bad = node.testmempoolaccept( - self.independent_txns_hex + [garbage_tx]) + testres_bad = node.testmempoolaccept(self.independent_txns_hex + [garbage_tx]) assert_equal( testres_bad, - self.independent_txns_testres + [ - {"txid": tx.get_id(), "allowed": False, - "reject-reason": "missing-inputs"}]) + self.independent_txns_testres + + [ + { + "txid": tx.get_id(), + "allowed": False, + "reject-reason": "missing-inputs", + } + ], + ) self.log.info( - "Check testmempoolaccept tells us when some transactions completed validation successfully") + "Check testmempoolaccept tells us when some transactions completed" + " validation successfully" + ) coin = self.coins.pop() tx_bad_sig_hex = node.createrawtransaction( [{"txid": coin["txid"], "vout": 0}], - {self.address: coin["amount"] - Decimal("100.00")}) + {self.address: coin["amount"] - Decimal("100.00")}, + ) tx_bad_sig = FromHex(CTransaction(), tx_bad_sig_hex) pad_tx(tx_bad_sig) tx_bad_sig_hex = ToHex(tx_bad_sig) testres_bad_sig = node.testmempoolaccept( - self.independent_txns_hex + [tx_bad_sig_hex]) + self.independent_txns_hex + [tx_bad_sig_hex] + ) # By the time the signature for the last transaction is checked, all the # other transactions have been fully validated, which is why the node # returns full validation results for all transactions here but empty # results in other cases. - assert_equal(testres_bad_sig, self.independent_txns_testres + [{ - "txid": tx_bad_sig.get_id(), "allowed": False, - "reject-reason": "mandatory-script-verify-flag-failed (Operation not valid with the current stack size)" - }]) + assert_equal( + testres_bad_sig, + self.independent_txns_testres + + [ + { + "txid": tx_bad_sig.get_id(), + "allowed": False, + "reject-reason": ( + "mandatory-script-verify-flag-failed (Operation not valid with" + " the current stack size)" + ), + } + ], + ) self.log.info( - "Check testmempoolaccept reports txns in packages that exceed max feerate") + "Check testmempoolaccept reports txns in packages that exceed max feerate" + ) coin = self.coins.pop() tx_high_fee_raw = node.createrawtransaction( [{"txid": coin["txid"], "vout": 0}], - {self.address: coin["amount"] - Decimal("999_000")}) + {self.address: coin["amount"] - Decimal("999_000")}, + ) tx_high_fee_signed = node.signrawtransactionwithkey( - hexstring=tx_high_fee_raw, privkeys=self.privkeys) + hexstring=tx_high_fee_raw, privkeys=self.privkeys + ) assert tx_high_fee_signed["complete"] tx_high_fee = FromHex(CTransaction(), tx_high_fee_signed["hex"]) testres_high_fee = node.testmempoolaccept([tx_high_fee_signed["hex"]]) assert_equal( testres_high_fee, - [{"txid": tx_high_fee.get_id(), "allowed": False, - "reject-reason": "max-fee-exceeded"}]) - package_high_fee = [tx_high_fee_signed["hex"]] + \ - self.independent_txns_hex + [ + { + "txid": tx_high_fee.get_id(), + "allowed": False, + "reject-reason": "max-fee-exceeded", + } + ], + ) + package_high_fee = [tx_high_fee_signed["hex"]] + self.independent_txns_hex testres_package_high_fee = node.testmempoolaccept(package_high_fee) - assert_equal(testres_package_high_fee, - testres_high_fee + self.independent_txns_testres_blank) + assert_equal( + testres_package_high_fee, + testres_high_fee + self.independent_txns_testres_blank, + ) def test_chain(self): node = self.nodes[0] first_coin = self.coins.pop() (chain_hex, chain_txns) = create_raw_chain( - node, first_coin, self.address, self.privkeys) + node, first_coin, self.address, self.privkeys + ) self.log.info( - "Check that testmempoolaccept requires packages to be sorted by dependency") + "Check that testmempoolaccept requires packages to be sorted by dependency" + ) assert_equal( node.testmempoolaccept(rawtxs=chain_hex[::-1]), - [{"txid": tx.get_id(), "package-error": "package-not-sorted"} - for tx in chain_txns[::-1]]) + [ + {"txid": tx.get_id(), "package-error": "package-not-sorted"} + for tx in chain_txns[::-1] + ], + ) self.log.info("Testmempoolaccept a chain of 50 transactions") testres_multiple = node.testmempoolaccept(rawtxs=chain_hex) testres_single = [] # Test accept and then submit each one individually, which should be # identical to package test accept for rawtx in chain_hex: testres = node.testmempoolaccept([rawtx]) testres_single.append(testres[0]) # Submit the transaction now so its child should have no problem # validating node.sendrawtransaction(rawtx) assert_equal(testres_single, testres_multiple) # Clean up by clearing the mempool self.generate(node, 1) def test_multiple_children(self): node = self.nodes[0] self.log.info( - "Testmempoolaccept a package in which a transaction has two children within the package") + "Testmempoolaccept a package in which a transaction has two children within" + " the package" + ) first_coin = self.coins.pop() # Deduct reasonable fee and make 2 outputs value = (first_coin["amount"] - Decimal("200.00")) / 2 inputs = [{"txid": first_coin["txid"], "vout": 0}] outputs = [{self.address: value}, {ADDRESS_ECREG_P2SH_OP_TRUE: value}] rawtx = node.createrawtransaction(inputs, outputs) parent_signed = node.signrawtransactionwithkey( - hexstring=rawtx, privkeys=self.privkeys) + hexstring=rawtx, privkeys=self.privkeys + ) assert parent_signed["complete"] parent_tx = FromHex(CTransaction(), parent_signed["hex"]) parent_txid = parent_tx.get_id() assert node.testmempoolaccept([parent_signed["hex"]])[0]["allowed"] parent_locking_script_a = parent_tx.vout[0].scriptPubKey.hex() child_value = value - Decimal("100.00") # Child A (_, tx_child_a_hex, _, _) = make_chain( - node, self.address, self.privkeys, parent_txid, value, 0, - parent_locking_script_a) + node, + self.address, + self.privkeys, + parent_txid, + value, + 0, + parent_locking_script_a, + ) assert not node.testmempoolaccept([tx_child_a_hex])[0]["allowed"] # Child B rawtx_b = node.createrawtransaction( - [{"txid": parent_txid, "vout": 1}], {self.address: child_value}) + [{"txid": parent_txid, "vout": 1}], {self.address: child_value} + ) tx_child_b = FromHex(CTransaction(), rawtx_b) tx_child_b.vin[0].scriptSig = SCRIPTSIG_OP_TRUE pad_tx(tx_child_b) tx_child_b_hex = ToHex(tx_child_b) assert not node.testmempoolaccept([tx_child_b_hex])[0]["allowed"] self.log.info( - "Testmempoolaccept with entire package, should work with children in either order") + "Testmempoolaccept with entire package, should work with children in either" + " order" + ) testres_multiple_ab = node.testmempoolaccept( - rawtxs=[parent_signed["hex"], tx_child_a_hex, tx_child_b_hex]) + rawtxs=[parent_signed["hex"], tx_child_a_hex, tx_child_b_hex] + ) testres_multiple_ba = node.testmempoolaccept( - rawtxs=[parent_signed["hex"], tx_child_b_hex, tx_child_a_hex]) + rawtxs=[parent_signed["hex"], tx_child_b_hex, tx_child_a_hex] + ) - assert all(testres["allowed"] - for testres in testres_multiple_ab + testres_multiple_ba) + assert all( + testres["allowed"] for testres in testres_multiple_ab + testres_multiple_ba + ) testres_single = [] # Test accept and then submit each one individually, which should be # identical to package testaccept for rawtx in [parent_signed["hex"], tx_child_a_hex, tx_child_b_hex]: testres = node.testmempoolaccept([rawtx]) testres_single.append(testres[0]) # Submit the transaction now so its child should have no problem # validating node.sendrawtransaction(rawtx) assert_equal(testres_single, testres_multiple_ab) def test_multiple_parents(self): node = self.nodes[0] self.log.info( - "Testmempoolaccept a package in which a transaction has multiple parents within the package") + "Testmempoolaccept a package in which a transaction has multiple parents" + " within the package" + ) for num_parents in [2, 10, 49]: # Test a package with num_parents parents and 1 child transaction. package_hex = [] parents_tx = [] values = [] parent_locking_scripts = [] for _ in range(num_parents): parent_coin = self.coins.pop() value = parent_coin["amount"] (tx, txhex, value, parent_locking_script) = make_chain( - node, self.address, self.privkeys, parent_coin["txid"], - value) + node, self.address, self.privkeys, parent_coin["txid"], value + ) package_hex.append(txhex) parents_tx.append(tx) values.append(value) parent_locking_scripts.append(parent_locking_script) child_hex = create_child_with_parents( - node, self.address, self.privkeys, parents_tx, values, - parent_locking_scripts) + node, + self.address, + self.privkeys, + parents_tx, + values, + parent_locking_scripts, + ) # Package accept should work with the parents in any order # (as long as parents come before child) for _ in range(10): random.shuffle(package_hex) testres_multiple = node.testmempoolaccept( - rawtxs=package_hex + [child_hex]) - assert all(testres["allowed"] - for testres in testres_multiple) + rawtxs=package_hex + [child_hex] + ) + assert all(testres["allowed"] for testres in testres_multiple) testres_single = [] # Test accept and then submit each one individually, which should be # identical to package testaccept for rawtx in package_hex + [child_hex]: testres_single.append(node.testmempoolaccept([rawtx])[0]) # Submit the transaction now so its child should have no problem # validating node.sendrawtransaction(rawtx) assert_equal(testres_single, testres_multiple) def test_conflicting(self): node = self.nodes[0] prevtx = self.coins.pop() inputs = [{"txid": prevtx["txid"], "vout": 0}] - output1 = { - node.get_deterministic_priv_key().address: 50_000_000 - - 1250} + output1 = {node.get_deterministic_priv_key().address: 50_000_000 - 1250} output2 = {ADDRESS_ECREG_P2SH_OP_TRUE: 50_000_000 - 1250} # tx1 and tx2 share the same inputs rawtx1 = node.createrawtransaction(inputs, output1) rawtx2 = node.createrawtransaction(inputs, output2) signedtx1 = node.signrawtransactionwithkey( - hexstring=rawtx1, privkeys=self.privkeys) + hexstring=rawtx1, privkeys=self.privkeys + ) signedtx2 = node.signrawtransactionwithkey( - hexstring=rawtx2, privkeys=self.privkeys) + hexstring=rawtx2, privkeys=self.privkeys + ) tx1 = FromHex(CTransaction(), signedtx1["hex"]) tx2 = FromHex(CTransaction(), signedtx2["hex"]) assert signedtx1["complete"] assert signedtx2["complete"] # Ensure tx1 and tx2 are valid by themselves assert node.testmempoolaccept([signedtx1["hex"]])[0]["allowed"] assert node.testmempoolaccept([signedtx2["hex"]])[0]["allowed"] self.log.info("Test duplicate transactions in the same package") testres = node.testmempoolaccept([signedtx1["hex"], signedtx1["hex"]]) assert_equal( testres, [ {"txid": tx1.get_id(), "package-error": "conflict-in-package"}, - {"txid": tx1.get_id(), "package-error": "conflict-in-package"} - ]) + {"txid": tx1.get_id(), "package-error": "conflict-in-package"}, + ], + ) self.log.info("Test conflicting transactions in the same package") testres = node.testmempoolaccept([signedtx1["hex"], signedtx2["hex"]]) assert_equal( testres, [ {"txid": tx1.get_id(), "package-error": "conflict-in-package"}, - {"txid": tx2.get_id(), "package-error": "conflict-in-package"} - ]) + {"txid": tx2.get_id(), "package-error": "conflict-in-package"}, + ], + ) if __name__ == "__main__": RPCPackagesTest().main() diff --git a/test/functional/rpc_preciousblock.py b/test/functional/rpc_preciousblock.py index 9f4ad7480..ac1fc5a5d 100755 --- a/test/functional/rpc_preciousblock.py +++ b/test/functional/rpc_preciousblock.py @@ -1,121 +1,121 @@ #!/usr/bin/env python3 # Copyright (c) 2015-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 preciousblock RPC.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal def unidirectional_node_sync_via_rpc(node_src, node_dest): blocks_to_copy = [] blockhash = node_src.getbestblockhash() while True: try: assert len(node_dest.getblock(blockhash, False)) > 0 break except Exception: blocks_to_copy.append(blockhash) - blockhash = node_src.getblockheader( - blockhash, True)['previousblockhash'] + blockhash = node_src.getblockheader(blockhash, True)["previousblockhash"] blocks_to_copy.reverse() for blockhash in blocks_to_copy: blockdata = node_src.getblock(blockhash, False) - assert node_dest.submitblock(blockdata) in (None, 'inconclusive') + assert node_dest.submitblock(blockdata) in (None, "inconclusive") def node_sync_via_rpc(nodes): for node_src in nodes: for node_dest in nodes: if node_src is node_dest: continue unidirectional_node_sync_via_rpc(node_src, node_dest) class PreciousTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 - self.extra_args = [["-noparkdeepreorg"], - ["-noparkdeepreorg"], ["-noparkdeepreorg"]] + self.extra_args = [ + ["-noparkdeepreorg"], + ["-noparkdeepreorg"], + ["-noparkdeepreorg"], + ] self.supports_cli = False def setup_network(self): self.setup_nodes() def run_test(self): - self.log.info( - "Ensure submitblock can in principle reorg to a competing chain") + self.log.info("Ensure submitblock can in principle reorg to a competing chain") self.generate(self.nodes[0], 1, sync_fun=self.no_op) assert_equal(self.nodes[0].getblockcount(), 1) hashZ = self.generate(self.nodes[1], 2, sync_fun=self.no_op)[-1] assert_equal(self.nodes[1].getblockcount(), 2) node_sync_via_rpc(self.nodes[0:3]) assert_equal(self.nodes[0].getbestblockhash(), hashZ) self.log.info("Mine blocks A-B-C on Node 0") hashC = self.generate(self.nodes[0], 3, sync_fun=self.no_op)[-1] assert_equal(self.nodes[0].getblockcount(), 5) self.log.info("Mine competing blocks E-F-G on Node 1") hashG = self.generate(self.nodes[1], 3, sync_fun=self.no_op)[-1] assert_equal(self.nodes[1].getblockcount(), 5) assert hashC != hashG self.log.info("Connect nodes and check no reorg occurs") # Submit competing blocks via RPC so any reorg should occur before we # proceed (no way to wait on inaction for p2p sync) node_sync_via_rpc(self.nodes[0:2]) self.connect_nodes(0, 1) assert_equal(self.nodes[0].getbestblockhash(), hashC) assert_equal(self.nodes[1].getbestblockhash(), hashG) self.log.info("Make Node0 prefer block G") self.nodes[0].preciousblock(hashG) assert_equal(self.nodes[0].getbestblockhash(), hashG) self.log.info("Make Node0 prefer block C again") self.nodes[0].preciousblock(hashC) assert_equal(self.nodes[0].getbestblockhash(), hashC) self.log.info("Make Node1 prefer block C") self.nodes[1].preciousblock(hashC) # wait because node 1 may not have downloaded hashC self.sync_blocks(self.nodes[0:2]) assert_equal(self.nodes[1].getbestblockhash(), hashC) self.log.info("Make Node1 prefer block G again") self.nodes[1].preciousblock(hashG) assert_equal(self.nodes[1].getbestblockhash(), hashG) self.log.info("Make Node0 prefer block G again") self.nodes[0].preciousblock(hashG) assert_equal(self.nodes[0].getbestblockhash(), hashG) self.log.info("Make Node1 prefer block C again") self.nodes[1].preciousblock(hashC) assert_equal(self.nodes[1].getbestblockhash(), hashC) - self.log.info( - "Mine another block (E-F-G-)H on Node 0 and reorg Node 1") + self.log.info("Mine another block (E-F-G-)H on Node 0 and reorg Node 1") self.generate(self.nodes[0], 1, sync_fun=self.no_op) assert_equal(self.nodes[0].getblockcount(), 6) self.sync_blocks(self.nodes[0:2]) hashH = self.nodes[0].getbestblockhash() assert_equal(self.nodes[1].getbestblockhash(), hashH) self.log.info("Node1 should not be able to prefer block C anymore") self.nodes[1].preciousblock(hashC) assert_equal(self.nodes[1].getbestblockhash(), hashH) self.log.info("Mine competing blocks I-J-K-L on Node 2") self.generate(self.nodes[2], 4, sync_fun=self.no_op) assert_equal(self.nodes[2].getblockcount(), 6) hashL = self.nodes[2].getbestblockhash() self.log.info("Connect nodes and check no reorg occurs") node_sync_via_rpc(self.nodes[1:3]) self.connect_nodes(1, 2) self.connect_nodes(0, 2) assert_equal(self.nodes[0].getbestblockhash(), hashH) assert_equal(self.nodes[1].getbestblockhash(), hashH) assert_equal(self.nodes[2].getbestblockhash(), hashL) self.log.info("Make Node1 prefer block L") self.nodes[1].preciousblock(hashL) assert_equal(self.nodes[1].getbestblockhash(), hashL) self.log.info("Make Node2 prefer block H") self.nodes[2].preciousblock(hashH) assert_equal(self.nodes[2].getbestblockhash(), hashH) -if __name__ == '__main__': +if __name__ == "__main__": PreciousTest().main() diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py index 3b21019e9..5d964c3e3 100755 --- a/test/functional/rpc_psbt.py +++ b/test/functional/rpc_psbt.py @@ -1,463 +1,525 @@ #!/usr/bin/env python3 # Copyright (c) 2018 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 Partially Signed Transaction RPCs. """ import json import os from decimal import Decimal from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_approx, assert_equal, assert_raises_rpc_error, find_output, ) # Create one-input, one-output, no-fee transaction: class PSBTTest(BitcoinTestFramework): - def set_test_params(self): self.num_nodes = 3 self.supports_cli = False def skip_test_if_missing_module(self): self.skip_if_no_wallet() def run_test(self): # Create and fund a raw tx for sending 10 BTC psbtx1 = self.nodes[0].walletcreatefundedpsbt( - [], {self.nodes[2].getnewaddress(): 10000000})['psbt'] + [], {self.nodes[2].getnewaddress(): 10000000} + )["psbt"] # If inputs are specified, do not automatically add more: utxo1 = self.nodes[0].listunspent()[0] - assert_raises_rpc_error(-4, - "Insufficient funds", - self.nodes[0].walletcreatefundedpsbt, - [{"txid": utxo1['txid'], - "vout": utxo1['vout']}], - {self.nodes[2].getnewaddress(): 90000000}) + assert_raises_rpc_error( + -4, + "Insufficient funds", + self.nodes[0].walletcreatefundedpsbt, + [{"txid": utxo1["txid"], "vout": utxo1["vout"]}], + {self.nodes[2].getnewaddress(): 90000000}, + ) - psbtx1 = self.nodes[0].walletcreatefundedpsbt([{"txid": utxo1['txid'], "vout": utxo1['vout']}], { - self.nodes[2].getnewaddress(): 90000000}, 0, {"add_inputs": True})['psbt'] - assert_equal(len(self.nodes[0].decodepsbt(psbtx1)['tx']['vin']), 2) + psbtx1 = self.nodes[0].walletcreatefundedpsbt( + [{"txid": utxo1["txid"], "vout": utxo1["vout"]}], + {self.nodes[2].getnewaddress(): 90000000}, + 0, + {"add_inputs": True}, + )["psbt"] + assert_equal(len(self.nodes[0].decodepsbt(psbtx1)["tx"]["vin"]), 2) # Inputs argument can be null - self.nodes[0].walletcreatefundedpsbt( - None, {self.nodes[2].getnewaddress(): 10}) + self.nodes[0].walletcreatefundedpsbt(None, {self.nodes[2].getnewaddress(): 10}) # Node 1 should not be able to add anything to it but still return the # psbtx same as before - psbtx = self.nodes[1].walletprocesspsbt(psbtx1)['psbt'] + psbtx = self.nodes[1].walletprocesspsbt(psbtx1)["psbt"] assert_equal(psbtx1, psbtx) # Sign the transaction and send - signed_tx = self.nodes[0].walletprocesspsbt(psbtx)['psbt'] - final_tx = self.nodes[0].finalizepsbt(signed_tx)['hex'] + signed_tx = self.nodes[0].walletprocesspsbt(psbtx)["psbt"] + final_tx = self.nodes[0].finalizepsbt(signed_tx)["hex"] self.nodes[0].sendrawtransaction(final_tx) # Manually selected inputs can be locked: assert_equal(len(self.nodes[0].listlockunspent()), 0) utxo1 = self.nodes[0].listunspent()[0] psbtx1 = self.nodes[0].walletcreatefundedpsbt( - [{"txid": utxo1['txid'], "vout": utxo1['vout']}], - {self.nodes[2].getnewaddress(): 1_000_000}, 0, - {"lockUnspents": True})["psbt"] + [{"txid": utxo1["txid"], "vout": utxo1["vout"]}], + {self.nodes[2].getnewaddress(): 1_000_000}, + 0, + {"lockUnspents": True}, + )["psbt"] assert_equal(len(self.nodes[0].listlockunspent()), 1) # Locks are ignored for manually selected inputs self.nodes[0].walletcreatefundedpsbt( - [{"txid": utxo1['txid'], "vout": utxo1['vout']}], - {self.nodes[2].getnewaddress(): 1_000_000}, 0) + [{"txid": utxo1["txid"], "vout": utxo1["vout"]}], + {self.nodes[2].getnewaddress(): 1_000_000}, + 0, + ) # Create p2sh, p2pkh addresses - pubkey0 = self.nodes[0].getaddressinfo( - self.nodes[0].getnewaddress())['pubkey'] - pubkey1 = self.nodes[1].getaddressinfo( - self.nodes[1].getnewaddress())['pubkey'] - pubkey2 = self.nodes[2].getaddressinfo( - self.nodes[2].getnewaddress())['pubkey'] - p2sh = self.nodes[1].addmultisigaddress( - 2, [pubkey0, pubkey1, pubkey2], "")['address'] + pubkey0 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())["pubkey"] + pubkey1 = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress())["pubkey"] + pubkey2 = self.nodes[2].getaddressinfo(self.nodes[2].getnewaddress())["pubkey"] + p2sh = self.nodes[1].addmultisigaddress(2, [pubkey0, pubkey1, pubkey2], "")[ + "address" + ] p2pkh = self.nodes[1].getnewaddress("") # fund those addresses rawtx = self.nodes[0].createrawtransaction( - [], {p2sh: 10000000, p2pkh: 10000000}) + [], {p2sh: 10000000, p2pkh: 10000000} + ) rawtx = self.nodes[0].fundrawtransaction(rawtx, {"changePosition": 0}) - signed_tx = self.nodes[0].signrawtransactionwithwallet(rawtx['hex'])[ - 'hex'] + signed_tx = self.nodes[0].signrawtransactionwithwallet(rawtx["hex"])["hex"] txid = self.nodes[0].sendrawtransaction(signed_tx) self.generate(self.nodes[0], 6) # Find the output pos p2sh_pos = -1 p2pkh_pos = -1 decoded = self.nodes[0].decoderawtransaction(signed_tx) - for out in decoded['vout']: - if out['scriptPubKey']['addresses'][0] == p2sh: - p2sh_pos = out['n'] - elif out['scriptPubKey']['addresses'][0] == p2pkh: - p2pkh_pos = out['n'] + for out in decoded["vout"]: + if out["scriptPubKey"]["addresses"][0] == p2sh: + p2sh_pos = out["n"] + elif out["scriptPubKey"]["addresses"][0] == p2pkh: + p2pkh_pos = out["n"] # spend single key from node 1 - rawtx = self.nodes[1].walletcreatefundedpsbt([{"txid": txid, "vout": p2pkh_pos}], { - self.nodes[1].getnewaddress(): 9990000})['psbt'] + rawtx = self.nodes[1].walletcreatefundedpsbt( + [{"txid": txid, "vout": p2pkh_pos}], + {self.nodes[1].getnewaddress(): 9990000}, + )["psbt"] walletprocesspsbt_out = self.nodes[1].walletprocesspsbt(rawtx) # Make sure it has UTXOs - decoded = self.nodes[1].decodepsbt(walletprocesspsbt_out['psbt']) - assert 'utxo' in decoded['inputs'][0] - assert_equal(walletprocesspsbt_out['complete'], True) + decoded = self.nodes[1].decodepsbt(walletprocesspsbt_out["psbt"]) + assert "utxo" in decoded["inputs"][0] + assert_equal(walletprocesspsbt_out["complete"], True) self.nodes[1].sendrawtransaction( - self.nodes[1].finalizepsbt(walletprocesspsbt_out['psbt'])['hex']) + self.nodes[1].finalizepsbt(walletprocesspsbt_out["psbt"])["hex"] + ) - inputs = [{"txid": txid, "vout": p2sh_pos}, - {"txid": txid, "vout": p2pkh_pos}] + inputs = [{"txid": txid, "vout": p2sh_pos}, {"txid": txid, "vout": p2pkh_pos}] output = {self.nodes[1].getnewaddress(): 29_990_000} self.log.info( "Test walletcreatefundedpsbt feeRate of 100,000 XEC/kB produces a " - "total fee at or slightly below -maxtxfee") + "total fee at or slightly below -maxtxfee" + ) res = self.nodes[1].walletcreatefundedpsbt( - inputs, output, 0, {"feeRate": 100_000, "add_inputs": True}) + inputs, output, 0, {"feeRate": 100_000, "add_inputs": True} + ) assert_approx(res["fee"], 65000, 5000) self.log.info( "Test walletcreatefundedpsbt feeRate of 10,000,000 XEC/kB produces " - "a total fee well above -maxtxfee and raises RPC error") + "a total fee well above -maxtxfee and raises RPC error" + ) # previously this was silently capped at -maxtxfee for bool_add, output_ in ( - (True, output), - (False, {self.nodes[1].getnewaddress(): 1_000_000})): + (True, output), + (False, {self.nodes[1].getnewaddress(): 1_000_000}), + ): assert_raises_rpc_error( -4, "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)", self.nodes[1].walletcreatefundedpsbt, - inputs, output_, 0, - {"feeRate": 10_000_000, "add_inputs": bool_add}) + inputs, + output_, + 0, + {"feeRate": 10_000_000, "add_inputs": bool_add}, + ) self.log.info("Test various PSBT operations") # partially sign multisig things with node 1 - psbtx = self.nodes[1].walletcreatefundedpsbt([{"txid": txid, "vout": p2sh_pos}], { - self.nodes[1].getnewaddress(): 9990000})['psbt'] + psbtx = self.nodes[1].walletcreatefundedpsbt( + [{"txid": txid, "vout": p2sh_pos}], {self.nodes[1].getnewaddress(): 9990000} + )["psbt"] walletprocesspsbt_out = self.nodes[1].walletprocesspsbt(psbtx) - psbtx = walletprocesspsbt_out['psbt'] - assert_equal(walletprocesspsbt_out['complete'], False) + psbtx = walletprocesspsbt_out["psbt"] + assert_equal(walletprocesspsbt_out["complete"], False) # partially sign with node 2. This should be complete and sendable walletprocesspsbt_out = self.nodes[2].walletprocesspsbt(psbtx) - assert_equal(walletprocesspsbt_out['complete'], True) + assert_equal(walletprocesspsbt_out["complete"], True) self.nodes[2].sendrawtransaction( - self.nodes[2].finalizepsbt(walletprocesspsbt_out['psbt'])['hex']) + self.nodes[2].finalizepsbt(walletprocesspsbt_out["psbt"])["hex"] + ) # check that walletprocesspsbt fails to decode a non-psbt - rawtx = self.nodes[1].createrawtransaction([{"txid": txid, "vout": p2pkh_pos}], { - self.nodes[1].getnewaddress(): 9990000}) - assert_raises_rpc_error(-22, "TX decode failed", - self.nodes[1].walletprocesspsbt, rawtx) + rawtx = self.nodes[1].createrawtransaction( + [{"txid": txid, "vout": p2pkh_pos}], + {self.nodes[1].getnewaddress(): 9990000}, + ) + assert_raises_rpc_error( + -22, "TX decode failed", self.nodes[1].walletprocesspsbt, rawtx + ) # Convert a non-psbt to psbt and make sure we can decode it rawtx = self.nodes[0].createrawtransaction( - [], {self.nodes[1].getnewaddress(): 10000000}) + [], {self.nodes[1].getnewaddress(): 10000000} + ) rawtx = self.nodes[0].fundrawtransaction(rawtx) - new_psbt = self.nodes[0].converttopsbt(rawtx['hex']) + new_psbt = self.nodes[0].converttopsbt(rawtx["hex"]) self.nodes[0].decodepsbt(new_psbt) # Make sure that a non-psbt with signatures cannot be converted # Error is "Inputs must not have scriptSigs" - signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx['hex']) - assert_raises_rpc_error(-22, - "", - self.nodes[0].converttopsbt, - signedtx['hex']) - assert_raises_rpc_error(-22, - "", - self.nodes[0].converttopsbt, - signedtx['hex'], - False) + signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx["hex"]) + assert_raises_rpc_error(-22, "", self.nodes[0].converttopsbt, signedtx["hex"]) + assert_raises_rpc_error( + -22, "", self.nodes[0].converttopsbt, signedtx["hex"], False + ) # Unless we allow it to convert and strip signatures - self.nodes[0].converttopsbt(signedtx['hex'], True) + self.nodes[0].converttopsbt(signedtx["hex"], True) # Explicilty allow converting non-empty txs - new_psbt = self.nodes[0].converttopsbt(rawtx['hex']) + new_psbt = self.nodes[0].converttopsbt(rawtx["hex"]) self.nodes[0].decodepsbt(new_psbt) # Create outputs to nodes 1 and 2 node1_addr = self.nodes[1].getnewaddress() node2_addr = self.nodes[2].getnewaddress() txid1 = self.nodes[0].sendtoaddress(node1_addr, 13000000) txid2 = self.nodes[0].sendtoaddress(node2_addr, 13000000) blockhash = self.generate(self.nodes[0], 6)[0] - vout1 = find_output( - self.nodes[1], - txid1, - 13000000, - blockhash=blockhash) - vout2 = find_output( - self.nodes[2], - txid2, - 13000000, - blockhash=blockhash) + vout1 = find_output(self.nodes[1], txid1, 13000000, blockhash=blockhash) + vout2 = find_output(self.nodes[2], txid2, 13000000, blockhash=blockhash) # Create a psbt spending outputs from nodes 1 and 2 - psbt_orig = self.nodes[0].createpsbt([{"txid": txid1, "vout": vout1}, { - "txid": txid2, "vout": vout2}], {self.nodes[0].getnewaddress(): 25999000}) + psbt_orig = self.nodes[0].createpsbt( + [{"txid": txid1, "vout": vout1}, {"txid": txid2, "vout": vout2}], + {self.nodes[0].getnewaddress(): 25999000}, + ) # Update psbts, should only have data for one input and not the other - psbt1 = self.nodes[1].walletprocesspsbt( - psbt_orig, False, "ALL|FORKID")['psbt'] + psbt1 = self.nodes[1].walletprocesspsbt(psbt_orig, False, "ALL|FORKID")["psbt"] psbt1_decoded = self.nodes[0].decodepsbt(psbt1) - assert psbt1_decoded['inputs'][0] and not psbt1_decoded['inputs'][1] + assert psbt1_decoded["inputs"][0] and not psbt1_decoded["inputs"][1] # Check that BIP32 path was added - assert "bip32_derivs" in psbt1_decoded['inputs'][0] - psbt2 = self.nodes[2].walletprocesspsbt( - psbt_orig, False, "ALL|FORKID", False)['psbt'] + assert "bip32_derivs" in psbt1_decoded["inputs"][0] + psbt2 = self.nodes[2].walletprocesspsbt(psbt_orig, False, "ALL|FORKID", False)[ + "psbt" + ] psbt2_decoded = self.nodes[0].decodepsbt(psbt2) - assert not psbt2_decoded['inputs'][0] and psbt2_decoded['inputs'][1] + assert not psbt2_decoded["inputs"][0] and psbt2_decoded["inputs"][1] # Check that BIP32 paths were not added - assert "bip32_derivs" not in psbt2_decoded['inputs'][1] + assert "bip32_derivs" not in psbt2_decoded["inputs"][1] # Sign PSBTs (workaround issue #18039) - psbt1 = self.nodes[1].walletprocesspsbt(psbt_orig)['psbt'] - psbt2 = self.nodes[2].walletprocesspsbt(psbt_orig)['psbt'] + psbt1 = self.nodes[1].walletprocesspsbt(psbt_orig)["psbt"] + psbt2 = self.nodes[2].walletprocesspsbt(psbt_orig)["psbt"] # Combine, finalize, and send the psbts combined = self.nodes[0].combinepsbt([psbt1, psbt2]) - finalized = self.nodes[0].finalizepsbt(combined)['hex'] + finalized = self.nodes[0].finalizepsbt(combined)["hex"] self.nodes[0].sendrawtransaction(finalized) self.generate(self.nodes[0], 6) block_height = self.nodes[0].getblockcount() unspent = self.nodes[0].listunspent()[0] # Make sure change address wallet does not have P2SH innerscript access to results in success # when attempting BnB coin selection self.nodes[0].walletcreatefundedpsbt( [], - [{self.nodes[2].getnewaddress():unspent["amount"] + 1000000}], + [{self.nodes[2].getnewaddress(): unspent["amount"] + 1000000}], block_height + 2, {"changeAddress": self.nodes[1].getnewaddress()}, - False) + False, + ) # Regression test for 14473 (mishandling of already-signed # transaction): - psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid": unspent["txid"], "vout":unspent["vout"]}], [ - {self.nodes[2].getnewaddress():unspent["amount"] + 1000000}], 0, {"add_inputs": True}) + psbtx_info = self.nodes[0].walletcreatefundedpsbt( + [{"txid": unspent["txid"], "vout": unspent["vout"]}], + [{self.nodes[2].getnewaddress(): unspent["amount"] + 1000000}], + 0, + {"add_inputs": True}, + ) complete_psbt = self.nodes[0].walletprocesspsbt(psbtx_info["psbt"]) - double_processed_psbt = self.nodes[0].walletprocesspsbt( - complete_psbt["psbt"]) + double_processed_psbt = self.nodes[0].walletprocesspsbt(complete_psbt["psbt"]) assert_equal(complete_psbt, double_processed_psbt) # We don't care about the decode result, but decoding must succeed. self.nodes[0].decodepsbt(double_processed_psbt["psbt"]) # BIP 174 Test Vectors # Check that unknown values are just passed through unknown_psbt = "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PAAA=" - unknown_out = self.nodes[0].walletprocesspsbt(unknown_psbt)['psbt'] + unknown_out = self.nodes[0].walletprocesspsbt(unknown_psbt)["psbt"] assert_equal(unknown_psbt, unknown_out) # Open the data file - with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data/rpc_psbt.json'), encoding='utf-8') as f: + with open( + os.path.join( + os.path.dirname(os.path.realpath(__file__)), "data/rpc_psbt.json" + ), + encoding="utf-8", + ) as f: d = json.load(f) - invalids = d['invalid'] - valids = d['valid'] - creators = d['creator'] - signers = d['signer'] - combiners = d['combiner'] - finalizers = d['finalizer'] - extractors = d['extractor'] + invalids = d["invalid"] + valids = d["valid"] + creators = d["creator"] + signers = d["signer"] + combiners = d["combiner"] + finalizers = d["finalizer"] + extractors = d["extractor"] # Invalid PSBTs for invalid in invalids: - assert_raises_rpc_error(-22, "TX decode failed", - self.nodes[0].decodepsbt, invalid) + assert_raises_rpc_error( + -22, "TX decode failed", self.nodes[0].decodepsbt, invalid + ) # Valid PSBTs for valid in valids: self.nodes[0].decodepsbt(valid) # Creator Tests for creator in creators: - created_tx = self.nodes[0].createpsbt( - creator['inputs'], creator['outputs']) - assert_equal(created_tx, creator['result']) + created_tx = self.nodes[0].createpsbt(creator["inputs"], creator["outputs"]) + assert_equal(created_tx, creator["result"]) # Signer tests for i, signer in enumerate(signers): self.nodes[2].createwallet(f"wallet{i}") wrpc = self.nodes[2].get_wallet_rpc(f"wallet{i}") - for key in signer['privkeys']: + for key in signer["privkeys"]: wrpc.importprivkey(key) - signed_tx = wrpc.walletprocesspsbt(signer['psbt'])['psbt'] - assert_equal(signed_tx, signer['result']) + signed_tx = wrpc.walletprocesspsbt(signer["psbt"])["psbt"] + assert_equal(signed_tx, signer["result"]) # Combiner test for combiner in combiners: - combined = self.nodes[2].combinepsbt(combiner['combine']) - assert_equal(combined, combiner['result']) + combined = self.nodes[2].combinepsbt(combiner["combine"]) + assert_equal(combined, combiner["result"]) # Empty combiner test - assert_raises_rpc_error(-8, - "Parameter 'txs' cannot be empty", - self.nodes[0].combinepsbt, - []) + assert_raises_rpc_error( + -8, "Parameter 'txs' cannot be empty", self.nodes[0].combinepsbt, [] + ) # Finalizer test for finalizer in finalizers: - finalized = self.nodes[2].finalizepsbt( - finalizer['finalize'], False)['psbt'] - assert_equal(finalized, finalizer['result']) + finalized = self.nodes[2].finalizepsbt(finalizer["finalize"], False)["psbt"] + assert_equal(finalized, finalizer["result"]) # Extractor test for extractor in extractors: - extracted = self.nodes[2].finalizepsbt( - extractor['extract'], True)['hex'] - assert_equal(extracted, extractor['result']) + extracted = self.nodes[2].finalizepsbt(extractor["extract"], True)["hex"] + assert_equal(extracted, extractor["result"]) # Test decoding error: invalid base64 - assert_raises_rpc_error(-22, - "TX decode failed invalid base64", - self.nodes[0].decodepsbt, - ";definitely not base64;") + assert_raises_rpc_error( + -22, + "TX decode failed invalid base64", + self.nodes[0].decodepsbt, + ";definitely not base64;", + ) # Test that psbts with p2pkh outputs are created properly p2pkh = self.nodes[0].getnewaddress() psbt = self.nodes[1].walletcreatefundedpsbt( - [], [{p2pkh: 1000000}], 0, {"includeWatching": True}, True) - self.nodes[0].decodepsbt(psbt['psbt']) + [], [{p2pkh: 1000000}], 0, {"includeWatching": True}, True + ) + self.nodes[0].decodepsbt(psbt["psbt"]) # Send to all types of addresses addr1 = self.nodes[1].getnewaddress("") # originally bech32 txid1 = self.nodes[0].sendtoaddress(addr1, 11000000) vout1 = find_output(self.nodes[0], txid1, 11000000) addr2 = self.nodes[1].getnewaddress("") # originally legacy txid2 = self.nodes[0].sendtoaddress(addr2, 11000000) vout2 = find_output(self.nodes[0], txid2, 11000000) addr3 = self.nodes[1].getnewaddress("") # originally p2sh-segwit txid3 = self.nodes[0].sendtoaddress(addr3, 11000000) vout3 = find_output(self.nodes[0], txid3, 11000000) self.sync_all() def test_psbt_input_keys(psbt_input, keys): """Check that the psbt input has only the expected keys.""" assert_equal(set(keys), set(psbt_input.keys())) # Create a PSBT. None of the inputs are filled initially - psbt = self.nodes[1].createpsbt([{"txid": txid1, "vout": vout1}, {"txid": txid2, "vout": vout2}, { - "txid": txid3, "vout": vout3}], {self.nodes[0].getnewaddress(): 32999000}) + psbt = self.nodes[1].createpsbt( + [ + {"txid": txid1, "vout": vout1}, + {"txid": txid2, "vout": vout2}, + {"txid": txid3, "vout": vout3}, + ], + {self.nodes[0].getnewaddress(): 32999000}, + ) decoded = self.nodes[1].decodepsbt(psbt) - test_psbt_input_keys(decoded['inputs'][0], []) - test_psbt_input_keys(decoded['inputs'][1], []) - test_psbt_input_keys(decoded['inputs'][2], []) + test_psbt_input_keys(decoded["inputs"][0], []) + test_psbt_input_keys(decoded["inputs"][1], []) + test_psbt_input_keys(decoded["inputs"][2], []) # Update a PSBT with UTXOs from the node updated = self.nodes[1].utxoupdatepsbt(psbt) decoded = self.nodes[1].decodepsbt(updated) - test_psbt_input_keys(decoded['inputs'][1], []) - test_psbt_input_keys(decoded['inputs'][2], []) + test_psbt_input_keys(decoded["inputs"][1], []) + test_psbt_input_keys(decoded["inputs"][2], []) # Try again, now while providing descriptors - descs = [self.nodes[1].getaddressinfo(addr)['desc'] for addr in [ - addr1, addr2, addr3]] + descs = [ + self.nodes[1].getaddressinfo(addr)["desc"] for addr in [addr1, addr2, addr3] + ] updated = self.nodes[1].utxoupdatepsbt(psbt=psbt, descriptors=descs) decoded = self.nodes[1].decodepsbt(updated) - test_psbt_input_keys(decoded['inputs'][1], []) + test_psbt_input_keys(decoded["inputs"][1], []) # Two PSBTs with a common input should not be joinable - psbt1 = self.nodes[1].createpsbt([{"txid": txid1, "vout": vout1}], { - self.nodes[0].getnewaddress(): Decimal('10999000')}) - assert_raises_rpc_error(-8, "exists in multiple PSBTs", - self.nodes[1].joinpsbts, [psbt1, updated]) + psbt1 = self.nodes[1].createpsbt( + [{"txid": txid1, "vout": vout1}], + {self.nodes[0].getnewaddress(): Decimal("10999000")}, + ) + assert_raises_rpc_error( + -8, "exists in multiple PSBTs", self.nodes[1].joinpsbts, [psbt1, updated] + ) # Join two distinct PSBTs addr4 = self.nodes[1].getnewaddress("") txid4 = self.nodes[0].sendtoaddress(addr4, 5000000) vout4 = find_output(self.nodes[0], txid4, 5000000) self.generate(self.nodes[0], 6) - psbt2 = self.nodes[1].createpsbt([{"txid": txid4, "vout": vout4}], { - self.nodes[0].getnewaddress(): Decimal('4999000')}) - psbt2 = self.nodes[1].walletprocesspsbt(psbt2)['psbt'] + psbt2 = self.nodes[1].createpsbt( + [{"txid": txid4, "vout": vout4}], + {self.nodes[0].getnewaddress(): Decimal("4999000")}, + ) + psbt2 = self.nodes[1].walletprocesspsbt(psbt2)["psbt"] psbt2_decoded = self.nodes[0].decodepsbt(psbt2) - assert "final_scriptSig" in psbt2_decoded['inputs'][0] + assert "final_scriptSig" in psbt2_decoded["inputs"][0] joined = self.nodes[0].joinpsbts([psbt, psbt2]) joined_decoded = self.nodes[0].decodepsbt(joined) - assert len(joined_decoded['inputs']) == 4 and len( - joined_decoded['outputs']) == 2 and "final_scriptSig" not in joined_decoded['inputs'][3] + assert ( + len(joined_decoded["inputs"]) == 4 + and len(joined_decoded["outputs"]) == 2 + and "final_scriptSig" not in joined_decoded["inputs"][3] + ) # Fail when trying to join less than two PSBTs - assert_raises_rpc_error(-8, - "At least two PSBTs are required to join PSBTs.", self.nodes[1].joinpsbts, []) - assert_raises_rpc_error(-8, - "At least two PSBTs are required to join PSBTs.", self.nodes[1].joinpsbts, [psbt2]) + assert_raises_rpc_error( + -8, + "At least two PSBTs are required to join PSBTs.", + self.nodes[1].joinpsbts, + [], + ) + assert_raises_rpc_error( + -8, + "At least two PSBTs are required to join PSBTs.", + self.nodes[1].joinpsbts, + [psbt2], + ) # Check that joining shuffles the inputs and outputs # 10 attempts should be enough to get a shuffled join shuffled = False for _ in range(10): shuffled_joined = self.nodes[0].joinpsbts([psbt, psbt2]) shuffled |= joined != shuffled_joined if shuffled: break assert shuffled # Newly created PSBT needs UTXOs and updating addr = self.nodes[1].getnewaddress("") txid = self.nodes[0].sendtoaddress(addr, 7000000) blockhash = self.generate(self.nodes[0], 6)[0] vout = find_output(self.nodes[0], txid, 7000000, blockhash=blockhash) - psbt = self.nodes[1].createpsbt([{"txid": txid, "vout": vout}], { - self.nodes[0].getnewaddress(""): Decimal('6999000')}) + psbt = self.nodes[1].createpsbt( + [{"txid": txid, "vout": vout}], + {self.nodes[0].getnewaddress(""): Decimal("6999000")}, + ) analyzed = self.nodes[0].analyzepsbt(psbt) - assert not analyzed['inputs'][0]['has_utxo'] and not analyzed['inputs'][0][ - 'is_final'] and analyzed['inputs'][0]['next'] == 'updater' and analyzed['next'] == 'updater' + assert ( + not analyzed["inputs"][0]["has_utxo"] + and not analyzed["inputs"][0]["is_final"] + and analyzed["inputs"][0]["next"] == "updater" + and analyzed["next"] == "updater" + ) # After update with wallet, only needs signing - updated = self.nodes[1].walletprocesspsbt( - psbt, False, 'ALL|FORKID', True)['psbt'] + updated = self.nodes[1].walletprocesspsbt(psbt, False, "ALL|FORKID", True)[ + "psbt" + ] analyzed = self.nodes[0].analyzepsbt(updated) - assert analyzed['inputs'][0]['has_utxo'] and not analyzed['inputs'][0][ - 'is_final'] and analyzed['inputs'][0]['next'] == 'signer' and analyzed['next'] == 'signer' + assert ( + analyzed["inputs"][0]["has_utxo"] + and not analyzed["inputs"][0]["is_final"] + and analyzed["inputs"][0]["next"] == "signer" + and analyzed["next"] == "signer" + ) # Check fee and size things - assert analyzed['fee'] == Decimal( - '1000') and analyzed['estimated_vsize'] == 191 and analyzed['estimated_feerate'] == Decimal('5235.60') + assert ( + analyzed["fee"] == Decimal("1000") + and analyzed["estimated_vsize"] == 191 + and analyzed["estimated_feerate"] == Decimal("5235.60") + ) # After signing and finalizing, needs extracting - signed = self.nodes[1].walletprocesspsbt(updated)['psbt'] + signed = self.nodes[1].walletprocesspsbt(updated)["psbt"] analyzed = self.nodes[0].analyzepsbt(signed) - assert analyzed['inputs'][0]['has_utxo'] and analyzed['inputs'][0]['is_final'] and analyzed['next'] == 'extractor' + assert ( + analyzed["inputs"][0]["has_utxo"] + and analyzed["inputs"][0]["is_final"] + and analyzed["next"] == "extractor" + ) self.log.info( - "PSBT spending unspendable outputs should have error message and Creator as next") + "PSBT spending unspendable outputs should have error message and Creator as" + " next" + ) analysis = self.nodes[0].analyzepsbt( - 'cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWAEHYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFv8/wADXYP/7//////8JxOh0LR2HAI8AAAAAAAEAIADC6wsAAAAAF2oUt/X69ELjeX2nTof+fZ10l+OyAokDAQcJAwEHEAABAACAAAEAIADC6wsAAAAAF2oUt/X69ELjeX2nTof+fZ10l+OyAokDAQcJAwEHENkMak8AAAAA') - assert_equal(analysis['next'], 'creator') + "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWAEHYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFv8/wADXYP/7//////8JxOh0LR2HAI8AAAAAAAEAIADC6wsAAAAAF2oUt/X69ELjeX2nTof+fZ10l+OyAokDAQcJAwEHEAABAACAAAEAIADC6wsAAAAAF2oUt/X69ELjeX2nTof+fZ10l+OyAokDAQcJAwEHENkMak8AAAAA" + ) + assert_equal(analysis["next"], "creator") assert_equal( - analysis['error'], - 'PSBT is not valid. Input 0 spends unspendable output') + analysis["error"], "PSBT is not valid. Input 0 spends unspendable output" + ) self.log.info( - "PSBT with invalid values should have error message and Creator as next") + "PSBT with invalid values should have error message and Creator as next" + ) analysis = self.nodes[0].analyzepsbt( - 'cHNidP8BAHECAAAAAfA00BFgAm6tp86RowwH6BMImQNL5zXUcTT97XoLGz0BAAAAAAD/////AgD5ApUAAAAAFgAUKNw0x8HRctAgmvoevm4u1SbN7XL87QKVAAAAABYAFPck4gF7iL4NL4wtfRAKgQbghiTUAAAAAAABAB8AgIFq49AHABYAFJUDtxf2PHo641HEOBOAIvFMNTr2AAAA') - assert_equal(analysis['next'], 'creator') - assert_equal( - analysis['error'], - 'PSBT is not valid. Input 0 has invalid value') + "cHNidP8BAHECAAAAAfA00BFgAm6tp86RowwH6BMImQNL5zXUcTT97XoLGz0BAAAAAAD/////AgD5ApUAAAAAFgAUKNw0x8HRctAgmvoevm4u1SbN7XL87QKVAAAAABYAFPck4gF7iL4NL4wtfRAKgQbghiTUAAAAAAABAB8AgIFq49AHABYAFJUDtxf2PHo641HEOBOAIvFMNTr2AAAA" + ) + assert_equal(analysis["next"], "creator") + assert_equal(analysis["error"], "PSBT is not valid. Input 0 has invalid value") analysis = self.nodes[0].analyzepsbt( - 'cHNidP8BAHECAAAAAfA00BFgAm6tp86RowwH6BMImQNL5zXUcTT97XoLGz0BAAAAAAD/////AgCAgWrj0AcAFgAUKNw0x8HRctAgmvoevm4u1SbN7XL87QKVAAAAABYAFPck4gF7iL4NL4wtfRAKgQbghiTUAAAAAAABAB8A8gUqAQAAABYAFJUDtxf2PHo641HEOBOAIvFMNTr2AAAA') - assert_equal(analysis['next'], 'creator') - assert_equal( - analysis['error'], - 'PSBT is not valid. Output amount invalid') + "cHNidP8BAHECAAAAAfA00BFgAm6tp86RowwH6BMImQNL5zXUcTT97XoLGz0BAAAAAAD/////AgCAgWrj0AcAFgAUKNw0x8HRctAgmvoevm4u1SbN7XL87QKVAAAAABYAFPck4gF7iL4NL4wtfRAKgQbghiTUAAAAAAABAB8A8gUqAQAAABYAFJUDtxf2PHo641HEOBOAIvFMNTr2AAAA" + ) + assert_equal(analysis["next"], "creator") + assert_equal(analysis["error"], "PSBT is not valid. Output amount invalid") -if __name__ == '__main__': +if __name__ == "__main__": PSBTTest().main() diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py index 332f192b1..0cd39ba18 100755 --- a/test/functional/rpc_rawtransaction.py +++ b/test/functional/rpc_rawtransaction.py @@ -1,726 +1,967 @@ #!/usr/bin/env python3 # 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.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') + "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']) + 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') + "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, []) + 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') + 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}], {}) + 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') + 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']]) + 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) + 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') + "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})))) + 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}]), + 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)]))))) + 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}]), + 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')]))))) + 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'}]), + 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})') + self.log.info(f"sendrawtransaction with missing prevtx info ({addr_type})") # Test `signrawtransactionwithwallet` invalid `prevtxs` - inputs = [{'txid': txid, 'vout': 3, 'sequence': 1000}] + 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( + -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 - } - ]) + 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') + self.log.info("sendrawtransaction with missing input") # won't exists inputs = [ - {'txid': "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout': 1}] + { + "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']) + 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) + 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) + 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 + 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) + 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( + -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')", + ( + "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, - "ZZZ0000000000000000000000000000000000000000000000000000000000000") - assert_raises_rpc_error(-5, "Block hash not found", self.nodes[0].getrawtransaction, - tx, True, "0000000000000000000000000000000000000000000000000000000000000000") + "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) + 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"]) + 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']]) + 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]) + 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'] + 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')) + 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'] + 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']) + 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')) + 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'], - }] + 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) + rawTx, inputs + ) # node1 only has one key, can't comp. sign the tx - assert_equal(rawTxPartialSigned['complete'], False) + assert_equal(rawTxPartialSigned["complete"], False) - rawTxSigned = self.nodes[2].signrawtransactionwithwallet(rawTx, - inputs) + 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']) + 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 + 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()) + 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'] + 2, [addr1Obj["pubkey"], addr2Obj["pubkey"]] + )["address"] mSigObj = self.nodes[2].addmultisigaddress( - 2, [addr1Obj['pubkey'], addr2Obj['pubkey']])['address'] + 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']) + 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')) + 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']}] + 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) + rawTx2, inputs + ) self.log.debug(rawTxPartialSigned1) # node1 only has one key, can't comp. sign the tx - assert_equal(rawTxPartialSigned1['complete'], False) + assert_equal(rawTxPartialSigned1["complete"], False) rawTxPartialSigned2 = self.nodes[2].signrawtransactionwithwallet( - rawTx2, inputs) + rawTx2, inputs + ) self.log.debug(rawTxPartialSigned2) # node2 only has one key, can't comp. sign the tx - assert_equal(rawTxPartialSigned2['complete'], False) + assert_equal(rawTxPartialSigned2["complete"], False) rawTxComb = self.nodes[2].combinerawtransaction( - [rawTxPartialSigned1['hex'], rawTxPartialSigned2['hex']]) + [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')) + 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}) + [{"txid": txid, "vout": vout}], {self.nodes[1].getnewaddress(): 9_999_000} + ) rawTxSigned = self.nodes[1].signrawtransactionwithwallet(rawTx) - txId = self.nodes[1].sendrawtransaction(rawTxSigned['hex']) + 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']) + 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']) + 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']) + 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']) + 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']) + 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") + 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, []) + 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, {}) + -1, "not a boolean", self.nodes[0].getrawtransaction, txId, {} + ) inputs = [ - {'txid': "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'sequence': 1000}] + { + "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) + -8, + "Invalid parameter, missing vout key", + self.nodes[0].createrawtransaction, + inputs, + outputs, + ) - inputs[0]['vout'] = "1" + inputs[0]["vout"] = "1" assert_raises_rpc_error( - -8, 'Invalid parameter, vout must be a number', - self.nodes[0].createrawtransaction, inputs, outputs) + -8, + "Invalid parameter, vout must be a number", + self.nodes[0].createrawtransaction, + inputs, + outputs, + ) - inputs[0]['vout'] = -1 + inputs[0]["vout"] = -1 assert_raises_rpc_error( - -8, 'Invalid parameter, vout cannot be negative', - self.nodes[0].createrawtransaction, inputs, outputs) + -8, + "Invalid parameter, vout cannot be negative", + self.nodes[0].createrawtransaction, + inputs, + outputs, + ) - inputs[0]['vout'] = 1 + inputs[0]["vout"] = 1 rawtx = self.nodes[0].createrawtransaction(inputs, outputs) decrawtx = self.nodes[0].decoderawtransaction(rawtx) - assert_equal(decrawtx['vin'][0]['sequence'], 1000) + assert_equal(decrawtx["vin"][0]["sequence"], 1000) # 9. invalid parameters - sequence number out of range - inputs[0]['sequence'] = -1 + inputs[0]["sequence"] = -1 assert_raises_rpc_error( - -8, 'Invalid parameter, sequence number is out of range', - self.nodes[0].createrawtransaction, inputs, outputs) + -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 + inputs[0]["sequence"] = 4294967296 assert_raises_rpc_error( - -8, 'Invalid parameter, sequence number is out of range', - self.nodes[0].createrawtransaction, inputs, outputs) + -8, + "Invalid parameter, sequence number is out of range", + self.nodes[0].createrawtransaction, + inputs, + outputs, + ) - inputs[0]['sequence'] = 4294967294 + inputs[0]["sequence"] = 4294967294 rawtx = self.nodes[0].createrawtransaction(inputs, outputs) decrawtx = self.nodes[0].decoderawtransaction(rawtx) - assert_equal(decrawtx['vin'][0]['sequence'], 4294967294) + 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) + assert_equal(decrawtx["version"], 0x80000000) # Test the maximum transaction version number that fits in a signed # 32-bit integer. tx = CTransaction() - tx.nVersion = 0x7fffffff + tx.nVersion = 0x7FFFFFFF rawtx = ToHex(tx) decrawtx = self.nodes[0].decoderawtransaction(rawtx) - assert_equal(decrawtx['version'], 0x7fffffff) + assert_equal(decrawtx["version"], 0x7FFFFFFF) - self.log.info('sendrawtransaction/testmempoolaccept with maxfeerate') + self.log.info("sendrawtransaction/testmempoolaccept with maxfeerate") # Test a transaction with a small fee. - txId = self.nodes[0].sendtoaddress( - self.nodes[2].getnewaddress(), 1000000) + 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')) + vout = next(o for o in rawTx["vout"] if o["value"] == Decimal("1000000.00")) self.sync_all() - inputs = [{"txid": txId, "vout": vout['n']}] + 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) + 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') + 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) + 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']) + 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) + 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')) + vout = next(o for o in rawTx["vout"] if o["value"] == Decimal("1000000.00")) self.sync_all() - inputs = [{"txid": txId, "vout": vout['n']}] + 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) + 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') + 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']) + 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) + rawtxs=[rawTxSigned["hex"]], maxfeerate="200000.00" + )[0] + assert_equal(testres["allowed"], True) self.nodes[2].sendrawtransaction( - hexstring=rawTxSigned['hex'], - maxfeerate='200000.00') + hexstring=rawTxSigned["hex"], maxfeerate="200000.00" + ) self.log.info( - 'sendrawtransaction/testmempoolaccept with tx that is already in the chain') + "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') + 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']) + -27, + "Transaction already in block chain", + node.sendrawtransaction, + rawTxSigned["hex"], + ) ########################################## # Decoding weird scripts in transactions # ########################################## - self.log.info('Decode correctly-formatted but weird 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')) + 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]))) + 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") + 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__': +if __name__ == "__main__": RawTransactionsTest().main() diff --git a/test/functional/rpc_scantxoutset.py b/test/functional/rpc_scantxoutset.py index f894ce80c..90cc816ee 100755 --- a/test/functional/rpc_scantxoutset.py +++ b/test/functional/rpc_scantxoutset.py @@ -1,229 +1,466 @@ #!/usr/bin/env python3 # Copyright (c) 2018 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 scantxoutset rpc call.""" from decimal import Decimal from test_framework.messages import XEC from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error from test_framework.wallet import MiniWallet, address_to_scriptpubkey, getnewdestination def descriptors(out): - return sorted(u['desc'] for u in out['unspents']) + return sorted(u["desc"] for u in out["unspents"]) class ScantxoutsetTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 def sendtodestination(self, destination, amount): # interpret strings as addresses, assume scriptPubKey otherwise if isinstance(destination, str): destination = address_to_scriptpubkey(destination) self.wallet.send_to( - from_node=self.nodes[0], - scriptPubKey=destination, - amount=int( - XEC * amount)) + from_node=self.nodes[0], scriptPubKey=destination, amount=int(XEC * amount) + ) def run_test(self): self.wallet = MiniWallet(self.nodes[0]) self.wallet.rescan_utxos() self.log.info("Create UTXOs...") pubk, spk, addr = getnewdestination() self.sendtodestination(spk, 2000) # send to child keys of tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK # (m/0'/0'/0') - self.sendtodestination( - "mkHV1C6JLheLoUSSZYk7x3FH5tnx9bu7yc", 8000) + self.sendtodestination("mkHV1C6JLheLoUSSZYk7x3FH5tnx9bu7yc", 8000) # (m/0'/0'/1') - self.sendtodestination( - "mipUSRmJAj2KrjSvsPQtnP8ynUon7FhpCR", 16000) + self.sendtodestination("mipUSRmJAj2KrjSvsPQtnP8ynUon7FhpCR", 16000) # (m/0'/0'/1500') - self.sendtodestination( - "n37dAGe6Mq1HGM9t4b6rFEEsDGq7Fcgfqg", 32000) + self.sendtodestination("n37dAGe6Mq1HGM9t4b6rFEEsDGq7Fcgfqg", 32000) # (m/0'/0'/0) - self.sendtodestination( - "mqS9Rpg8nNLAzxFExsgFLCnzHBsoQ3PRM6", 64000) + self.sendtodestination("mqS9Rpg8nNLAzxFExsgFLCnzHBsoQ3PRM6", 64000) # (m/0'/0'/1) - self.sendtodestination( - "mnTg5gVWr3rbhHaKjJv7EEEc76ZqHgSj4S", 128000) + self.sendtodestination("mnTg5gVWr3rbhHaKjJv7EEEc76ZqHgSj4S", 128000) # (m/0'/0'/1500) - self.sendtodestination( - "mketCd6B9U9Uee1iCsppDJJBHfvi6U6ukC", 256000) + self.sendtodestination("mketCd6B9U9Uee1iCsppDJJBHfvi6U6ukC", 256000) # (m/1/1/0') - self.sendtodestination( - "mj8zFzrbBcdaWXowCQ1oPZ4qioBVzLzAp7", 512000) + self.sendtodestination("mj8zFzrbBcdaWXowCQ1oPZ4qioBVzLzAp7", 512000) # (m/1/1/1') - self.sendtodestination( - "mfnKpKQEftniaoE1iXuMMePQU3PUpcNisA", 1024000) + self.sendtodestination("mfnKpKQEftniaoE1iXuMMePQU3PUpcNisA", 1024000) # (m/1/1/1500') - self.sendtodestination( - "mou6cB1kaP1nNJM1sryW6YRwnd4shTbXYQ", 2048000) + self.sendtodestination("mou6cB1kaP1nNJM1sryW6YRwnd4shTbXYQ", 2048000) # (m/1/1/0) - self.sendtodestination( - "mtfUoUax9L4tzXARpw1oTGxWyoogp52KhJ", 4096000) + self.sendtodestination("mtfUoUax9L4tzXARpw1oTGxWyoogp52KhJ", 4096000) # (m/1/1/1) - self.sendtodestination( - "mxp7w7j8S1Aq6L8StS2PqVvtt4HGxXEvdy", 8192000) + self.sendtodestination("mxp7w7j8S1Aq6L8StS2PqVvtt4HGxXEvdy", 8192000) # (m/1/1/1500) - self.sendtodestination( - "mpQ8rokAhp1TAtJQR6F6TaUmjAWkAWYYBq", 16384000) + self.sendtodestination("mpQ8rokAhp1TAtJQR6F6TaUmjAWkAWYYBq", 16384000) self.generate(self.nodes[0], 1) scan = self.nodes[0].scantxoutset("start", []) info = self.nodes[0].gettxoutsetinfo() - assert_equal(scan['success'], True) - assert_equal(scan['height'], info['height']) - assert_equal(scan['txouts'], info['txouts']) - assert_equal(scan['bestblock'], info['bestblock']) + assert_equal(scan["success"], True) + assert_equal(scan["height"], info["height"]) + assert_equal(scan["txouts"], info["txouts"]) + assert_equal(scan["bestblock"], info["bestblock"]) self.log.info("Test if we have found the non HD unspent outputs.") - assert_equal(self.nodes[0].scantxoutset( - "start", [f"pkh({pubk.hex()})"])['total_amount'], Decimal("2000")) - assert_equal(self.nodes[0].scantxoutset( - "start", [f"combo({pubk.hex()})"])['total_amount'], Decimal("2000")) - assert_equal(self.nodes[0].scantxoutset( - "start", [f"addr({addr})"])['total_amount'], Decimal("2000")) - assert_equal(self.nodes[0].scantxoutset( - "start", [f"addr({addr})"])['total_amount'], Decimal("2000")) + assert_equal( + self.nodes[0].scantxoutset("start", [f"pkh({pubk.hex()})"])["total_amount"], + Decimal("2000"), + ) + assert_equal( + self.nodes[0].scantxoutset("start", [f"combo({pubk.hex()})"])[ + "total_amount" + ], + Decimal("2000"), + ) + assert_equal( + self.nodes[0].scantxoutset("start", [f"addr({addr})"])["total_amount"], + Decimal("2000"), + ) + assert_equal( + self.nodes[0].scantxoutset("start", [f"addr({addr})"])["total_amount"], + Decimal("2000"), + ) self.log.info("Test range validation.") - assert_raises_rpc_error(-8, - "End of range is too high", - self.nodes[0].scantxoutset, - "start", - [{"desc": "desc", - "range": -1}]) - assert_raises_rpc_error(-8, - "Range should be greater or equal than 0", - self.nodes[0].scantxoutset, - "start", - [{"desc": "desc", - "range": [-1, - 10]}]) - assert_raises_rpc_error(-8, - "End of range is too high", - self.nodes[0].scantxoutset, - "start", - [{"desc": "desc", - "range": [(2 << 31 + 1) - 1000000, - (2 << 31 + 1)]}]) - assert_raises_rpc_error(-8, - "Range specified as [begin,end] must not have begin after end", - self.nodes[0].scantxoutset, - "start", - [{"desc": "desc", - "range": [2, - 1]}]) - assert_raises_rpc_error(-8, - "Range is too large", - self.nodes[0].scantxoutset, - "start", - [{"desc": "desc", - "range": [0, - 1000001]}]) + assert_raises_rpc_error( + -8, + "End of range is too high", + self.nodes[0].scantxoutset, + "start", + [{"desc": "desc", "range": -1}], + ) + assert_raises_rpc_error( + -8, + "Range should be greater or equal than 0", + self.nodes[0].scantxoutset, + "start", + [{"desc": "desc", "range": [-1, 10]}], + ) + assert_raises_rpc_error( + -8, + "End of range is too high", + self.nodes[0].scantxoutset, + "start", + [{"desc": "desc", "range": [(2 << 31 + 1) - 1000000, (2 << 31 + 1)]}], + ) + assert_raises_rpc_error( + -8, + "Range specified as [begin,end] must not have begin after end", + self.nodes[0].scantxoutset, + "start", + [{"desc": "desc", "range": [2, 1]}], + ) + assert_raises_rpc_error( + -8, + "Range is too large", + self.nodes[0].scantxoutset, + "start", + [{"desc": "desc", "range": [0, 1000001]}], + ) self.log.info("Test extended key derivation.") # Run various scans, and verify that the sum of the amounts of the matches corresponds to the expected subset. # Note that all amounts in the UTXO set are powers of 2 multiplied by # 0.001 BTC, so each amounts uniquely identifies a subset. - assert_equal(self.nodes[0].scantxoutset("start", [ - "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0'/0h/0h)"])['total_amount'], Decimal("8000")) - assert_equal(self.nodes[0].scantxoutset("start", [ - "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0'/0'/1h)"])['total_amount'], Decimal("16000")) - assert_equal(self.nodes[0].scantxoutset("start", [ - "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0h/0'/1500')"])['total_amount'], Decimal("32000")) - assert_equal(self.nodes[0].scantxoutset("start", [ - "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0h/0h/0)"])['total_amount'], Decimal("64000")) - assert_equal(self.nodes[0].scantxoutset("start", [ - "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0'/0h/1)"])['total_amount'], Decimal("128000")) - assert_equal(self.nodes[0].scantxoutset("start", [ - "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0h/0'/1500)"])['total_amount'], Decimal("256000")) - assert_equal(self.nodes[0].scantxoutset("start", [ - {"desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0'/0h/*h)", "range": 1499}])['total_amount'], Decimal("24000")) - assert_equal(self.nodes[0].scantxoutset("start", [ - {"desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0'/0'/*h)", "range": 1500}])['total_amount'], Decimal("56000")) - assert_equal(self.nodes[0].scantxoutset("start", [ - {"desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0h/0'/*)", "range": 1499}])['total_amount'], Decimal("192000")) - assert_equal(self.nodes[0].scantxoutset("start", [ - {"desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0'/0h/*)", "range": 1500}])['total_amount'], Decimal("448000")) - assert_equal(self.nodes[0].scantxoutset("start", [ - "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/0')"])['total_amount'], Decimal("0512000")) - assert_equal(self.nodes[0].scantxoutset("start", [ - "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/1')"])['total_amount'], Decimal("1024000")) - assert_equal(self.nodes[0].scantxoutset("start", [ - "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/1500h)"])['total_amount'], Decimal("2048000")) - assert_equal(self.nodes[0].scantxoutset("start", [ - "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/0)"])['total_amount'], Decimal("4096000")) - assert_equal(self.nodes[0].scantxoutset("start", [ - "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/1)"])['total_amount'], Decimal("8192000")) - assert_equal(self.nodes[0].scantxoutset("start", [ - "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/1500)"])['total_amount'], Decimal("16384000")) - assert_equal(self.nodes[0].scantxoutset("start", [ - "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/0)"])['total_amount'], Decimal("4096000")) - assert_equal(self.nodes[0].scantxoutset("start", [ - "combo([abcdef88/1/2'/3/4h]tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/1)"])['total_amount'], Decimal("8192000")) - assert_equal(self.nodes[0].scantxoutset("start", [ - "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/1500)"])['total_amount'], Decimal("16384000")) - assert_equal(self.nodes[0].scantxoutset("start", [ - {"desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*')", "range": 1499}])['total_amount'], Decimal("1536000")) - assert_equal(self.nodes[0].scantxoutset("start", [ - {"desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*')", "range": 1500}])['total_amount'], Decimal("3584000")) - assert_equal(self.nodes[0].scantxoutset("start", [ - {"desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)", "range": 1499}])['total_amount'], Decimal("12288000")) - assert_equal(self.nodes[0].scantxoutset("start", [ - {"desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)", "range": 1500}])['total_amount'], Decimal("28672000")) - assert_equal(self.nodes[0].scantxoutset("start", [ - {"desc": "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/*)", "range": 1499}])['total_amount'], Decimal("12288000")) - assert_equal(self.nodes[0].scantxoutset("start", [ - {"desc": "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/*)", "range": 1500}])['total_amount'], Decimal("28672000")) + assert_equal( + self.nodes[0].scantxoutset( + "start", + [ + "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0'/0h/0h)" + ], + )["total_amount"], + Decimal("8000"), + ) + assert_equal( + self.nodes[0].scantxoutset( + "start", + [ + "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0'/0'/1h)" + ], + )["total_amount"], + Decimal("16000"), + ) + assert_equal( + self.nodes[0].scantxoutset( + "start", + [ + "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0h/0'/1500')" + ], + )["total_amount"], + Decimal("32000"), + ) + assert_equal( + self.nodes[0].scantxoutset( + "start", + [ + "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0h/0h/0)" + ], + )["total_amount"], + Decimal("64000"), + ) + assert_equal( + self.nodes[0].scantxoutset( + "start", + [ + "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0'/0h/1)" + ], + )["total_amount"], + Decimal("128000"), + ) + assert_equal( + self.nodes[0].scantxoutset( + "start", + [ + "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0h/0'/1500)" + ], + )["total_amount"], + Decimal("256000"), + ) + assert_equal( + self.nodes[0].scantxoutset( + "start", + [ + { + "desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0'/0h/*h)", + "range": 1499, + } + ], + )["total_amount"], + Decimal("24000"), + ) + assert_equal( + self.nodes[0].scantxoutset( + "start", + [ + { + "desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0'/0'/*h)", + "range": 1500, + } + ], + )["total_amount"], + Decimal("56000"), + ) + assert_equal( + self.nodes[0].scantxoutset( + "start", + [ + { + "desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0h/0'/*)", + "range": 1499, + } + ], + )["total_amount"], + Decimal("192000"), + ) + assert_equal( + self.nodes[0].scantxoutset( + "start", + [ + { + "desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0'/0h/*)", + "range": 1500, + } + ], + )["total_amount"], + Decimal("448000"), + ) + assert_equal( + self.nodes[0].scantxoutset( + "start", + [ + "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/0')" + ], + )["total_amount"], + Decimal("0512000"), + ) + assert_equal( + self.nodes[0].scantxoutset( + "start", + [ + "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/1')" + ], + )["total_amount"], + Decimal("1024000"), + ) + assert_equal( + self.nodes[0].scantxoutset( + "start", + [ + "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/1500h)" + ], + )["total_amount"], + Decimal("2048000"), + ) + assert_equal( + self.nodes[0].scantxoutset( + "start", + [ + "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/0)" + ], + )["total_amount"], + Decimal("4096000"), + ) + assert_equal( + self.nodes[0].scantxoutset( + "start", + [ + "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/1)" + ], + )["total_amount"], + Decimal("8192000"), + ) + assert_equal( + self.nodes[0].scantxoutset( + "start", + [ + "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/1500)" + ], + )["total_amount"], + Decimal("16384000"), + ) + assert_equal( + self.nodes[0].scantxoutset( + "start", + [ + "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/0)" + ], + )["total_amount"], + Decimal("4096000"), + ) + assert_equal( + self.nodes[0].scantxoutset( + "start", + [ + "combo([abcdef88/1/2'/3/4h]tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/1)" + ], + )["total_amount"], + Decimal("8192000"), + ) + assert_equal( + self.nodes[0].scantxoutset( + "start", + [ + "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/1500)" + ], + )["total_amount"], + Decimal("16384000"), + ) + assert_equal( + self.nodes[0].scantxoutset( + "start", + [ + { + "desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*')", + "range": 1499, + } + ], + )["total_amount"], + Decimal("1536000"), + ) + assert_equal( + self.nodes[0].scantxoutset( + "start", + [ + { + "desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*')", + "range": 1500, + } + ], + )["total_amount"], + Decimal("3584000"), + ) + assert_equal( + self.nodes[0].scantxoutset( + "start", + [ + { + "desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)", + "range": 1499, + } + ], + )["total_amount"], + Decimal("12288000"), + ) + assert_equal( + self.nodes[0].scantxoutset( + "start", + [ + { + "desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/*)", + "range": 1500, + } + ], + )["total_amount"], + Decimal("28672000"), + ) assert_equal( self.nodes[0].scantxoutset( "start", [ { "desc": "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/*)", - "range": [ - 1500, - 1500]}])['total_amount'], - Decimal("16384000")) + "range": 1499, + } + ], + )["total_amount"], + Decimal("12288000"), + ) + assert_equal( + self.nodes[0].scantxoutset( + "start", + [ + { + "desc": "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/*)", + "range": 1500, + } + ], + )["total_amount"], + Decimal("28672000"), + ) + assert_equal( + self.nodes[0].scantxoutset( + "start", + [ + { + "desc": "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/*)", + "range": [1500, 1500], + } + ], + )["total_amount"], + Decimal("16384000"), + ) # Test the reported descriptors for a few matches - assert_equal(descriptors(self.nodes[0].scantxoutset("start", - [{"desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0h/0'/*)", - "range": 1499}])), - ["pkh([0c5f9a1e/0'/0'/0]026dbd8b2315f296d36e6b6920b1579ca75569464875c7ebe869b536a7d9503c8c)#dzxw429x", - "pkh([0c5f9a1e/0'/0'/1]033e6f25d76c00bedb3a8993c7d5739ee806397f0529b1b31dda31ef890f19a60c)#43rvceed"]) assert_equal( descriptors( self.nodes[0].scantxoutset( "start", - ["combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/0)"])), - ["pkh([0c5f9a1e/1/1/0]03e1c5b6e650966971d7e71ef2674f80222752740fc1dfd63bbbd220d2da9bd0fb)#cxmct4w8"]) - assert_equal(descriptors(self.nodes[0].scantxoutset("start", - [{"desc": "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/*)", - "range": 1500}])), - ['pkh([0c5f9a1e/1/1/0]03e1c5b6e650966971d7e71ef2674f80222752740fc1dfd63bbbd220d2da9bd0fb)#cxmct4w8', - 'pkh([0c5f9a1e/1/1/1500]03832901c250025da2aebae2bfb38d5c703a57ab66ad477f9c578bfbcd78abca6f)#vchwd07g', - 'pkh([0c5f9a1e/1/1/1]030d820fc9e8211c4169be8530efbc632775d8286167afd178caaf1089b77daba7)#z2t3ypsa']) + [ + { + "desc": "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0h/0'/*)", + "range": 1499, + } + ], + ) + ), + [ + "pkh([0c5f9a1e/0'/0'/0]026dbd8b2315f296d36e6b6920b1579ca75569464875c7ebe869b536a7d9503c8c)#dzxw429x", + "pkh([0c5f9a1e/0'/0'/1]033e6f25d76c00bedb3a8993c7d5739ee806397f0529b1b31dda31ef890f19a60c)#43rvceed", + ], + ) + assert_equal( + descriptors( + self.nodes[0].scantxoutset( + "start", + [ + "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/0)" + ], + ) + ), + [ + "pkh([0c5f9a1e/1/1/0]03e1c5b6e650966971d7e71ef2674f80222752740fc1dfd63bbbd220d2da9bd0fb)#cxmct4w8" + ], + ) + assert_equal( + descriptors( + self.nodes[0].scantxoutset( + "start", + [ + { + "desc": "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/*)", + "range": 1500, + } + ], + ) + ), + [ + "pkh([0c5f9a1e/1/1/0]03e1c5b6e650966971d7e71ef2674f80222752740fc1dfd63bbbd220d2da9bd0fb)#cxmct4w8", + "pkh([0c5f9a1e/1/1/1500]03832901c250025da2aebae2bfb38d5c703a57ab66ad477f9c578bfbcd78abca6f)#vchwd07g", + "pkh([0c5f9a1e/1/1/1]030d820fc9e8211c4169be8530efbc632775d8286167afd178caaf1089b77daba7)#z2t3ypsa", + ], + ) # Check that status and abort don't need second arg assert_equal(self.nodes[0].scantxoutset("status"), None) assert_equal(self.nodes[0].scantxoutset("abort"), False) # Check that second arg is needed for start - assert_raises_rpc_error(-1, - "scanobjects argument is required for the start action", - self.nodes[0].scantxoutset, - "start") + assert_raises_rpc_error( + -1, + "scanobjects argument is required for the start action", + self.nodes[0].scantxoutset, + "start", + ) -if __name__ == '__main__': +if __name__ == "__main__": ScantxoutsetTest().main() diff --git a/test/functional/rpc_setban.py b/test/functional/rpc_setban.py index 46ab9e310..b1c6213e2 100755 --- a/test/functional/rpc_setban.py +++ b/test/functional/rpc_setban.py @@ -1,49 +1,51 @@ #!/usr/bin/env python3 # Copyright (c) 2015-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 setban rpc call.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import p2p_port class SetBanTests(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True self.extra_args = [[], []] def run_test(self): # Node 0 connects to Node 1, check that the noban permission is not # granted self.connect_nodes(0, 1) peerinfo = self.nodes[1].getpeerinfo()[0] - assert 'noban' not in peerinfo['permissions'] + assert "noban" not in peerinfo["permissions"] # Node 0 get banned by Node 1 self.nodes[1].setban("127.0.0.1", "add") # Node 0 should not be able to reconnect - with self.nodes[1].assert_debug_log(expected_msgs=['dropped (banned)\n'], timeout=5): + with self.nodes[1].assert_debug_log( + expected_msgs=["dropped (banned)\n"], timeout=5 + ): self.restart_node(1, []) self.nodes[0].addnode(f"127.0.0.1:{str(p2p_port(1))}", "onetry") # However, node 0 should be able to reconnect if it has noban # permission - self.restart_node(1, ['-whitelist=127.0.0.1']) + self.restart_node(1, ["-whitelist=127.0.0.1"]) self.connect_nodes(0, 1) peerinfo = self.nodes[1].getpeerinfo()[0] - assert 'noban' in peerinfo['permissions'] + assert "noban" in peerinfo["permissions"] # If we remove the ban, Node 0 should be able to reconnect even without # noban permission self.nodes[1].setban("127.0.0.1", "remove") self.restart_node(1, []) self.connect_nodes(0, 1) peerinfo = self.nodes[1].getpeerinfo()[0] - assert 'noban' not in peerinfo['permissions'] + assert "noban" not in peerinfo["permissions"] -if __name__ == '__main__': +if __name__ == "__main__": SetBanTests().main() diff --git a/test/functional/rpc_signmessage.py b/test/functional/rpc_signmessage.py index 70f88090e..fba525170 100755 --- a/test/functional/rpc_signmessage.py +++ b/test/functional/rpc_signmessage.py @@ -1,45 +1,43 @@ #!/usr/bin/env python3 # Copyright (c) 2016-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 RPC commands for signing and verifying messages.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal class SignMessagesTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 def skip_test_if_missing_module(self): self.skip_if_no_wallet() def run_test(self): - message = 'This is just a test message' + message = "This is just a test message" - self.log.info('test signing with priv_key') - priv_key = 'cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N' - address = 'mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB' - expected_signature = 'H/+xpIcWFrFLpyHC44kZSSuUqfv6T9CHzCIw/tsetxO0UYjIHvnvA891Hm7Ho3tTMRNYS2xf+lozdASttZc8L+8=' + self.log.info("test signing with priv_key") + priv_key = "cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N" + address = "mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB" + expected_signature = "H/+xpIcWFrFLpyHC44kZSSuUqfv6T9CHzCIw/tsetxO0UYjIHvnvA891Hm7Ho3tTMRNYS2xf+lozdASttZc8L+8=" signature = self.nodes[0].signmessagewithprivkey(priv_key, message) assert_equal(expected_signature, signature) assert self.nodes[0].verifymessage(address, signature, message) - self.log.info('test signing with an address with wallet') + self.log.info("test signing with an address with wallet") address = self.nodes[0].getnewaddress() signature = self.nodes[0].signmessage(address, message) assert self.nodes[0].verifymessage(address, signature, message) - self.log.info('test verifying with another address should not work') + self.log.info("test verifying with another address should not work") other_address = self.nodes[0].getnewaddress() other_signature = self.nodes[0].signmessage(other_address, message) - assert not self.nodes[0].verifymessage( - other_address, signature, message) - assert not self.nodes[0].verifymessage( - address, other_signature, message) + assert not self.nodes[0].verifymessage(other_address, signature, message) + assert not self.nodes[0].verifymessage(address, other_signature, message) -if __name__ == '__main__': +if __name__ == "__main__": SignMessagesTest().main() diff --git a/test/functional/rpc_signrawtransaction.py b/test/functional/rpc_signrawtransaction.py index 8ad904942..1c7072420 100755 --- a/test/functional/rpc_signrawtransaction.py +++ b/test/functional/rpc_signrawtransaction.py @@ -1,277 +1,348 @@ #!/usr/bin/env python3 # Copyright (c) 2015-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 transaction signing using the signrawtransaction* RPCs.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error -RPC_WALLET_NOT_SPECIFIED = "Wallet file not specified (must request wallet " + \ - "RPC through /wallet/ uri-path)." +RPC_WALLET_NOT_SPECIFIED = ( + "Wallet file not specified (must request wallet " + + "RPC through /wallet/ uri-path)." +) class SignRawTransactionsTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 def skip_test_if_missing_module(self): self.skip_if_no_wallet() def successful_signing_test(self): """Creates and signs a valid raw transaction with one input. Expected results: 1) The transaction has a complete set of signatures 2) No script verification error occurred""" self.log.info("Test valid raw transaction with one input") - privKeys = ['cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N', - 'cVKpPfVKSJxKqVpE9awvXNWuLHCa5j5tiE7K6zbUSptFpTEtiFrA'] + privKeys = [ + "cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N", + "cVKpPfVKSJxKqVpE9awvXNWuLHCa5j5tiE7K6zbUSptFpTEtiFrA", + ] inputs = [ # Valid pay-to-pubkey scripts - {'txid': '9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71', - 'vout': 0, 'amount': 3141590, - 'scriptPubKey': '76a91460baa0f494b38ce3c940dea67f3804dc52d1fb9488ac'}, - {'txid': '83a4f6a6b73660e13ee6cb3c6063fa3759c50c9b7521d0536022961898f4fb02', - 'vout': 0, 'amount': '123456000', - 'scriptPubKey': '76a914669b857c03a5ed269d5d85a1ffac9ed5d663072788ac'}, + { + "txid": ( + "9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71" + ), + "vout": 0, + "amount": 3141590, + "scriptPubKey": "76a91460baa0f494b38ce3c940dea67f3804dc52d1fb9488ac", + }, + { + "txid": ( + "83a4f6a6b73660e13ee6cb3c6063fa3759c50c9b7521d0536022961898f4fb02" + ), + "vout": 0, + "amount": "123456000", + "scriptPubKey": "76a914669b857c03a5ed269d5d85a1ffac9ed5d663072788ac", + }, ] - outputs = {'mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB': 100000} + outputs = {"mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB": 100000} rawTx = self.nodes[0].createrawtransaction(inputs, outputs) - rawTxSigned = self.nodes[0].signrawtransactionwithkey( - rawTx, privKeys, inputs) + rawTxSigned = self.nodes[0].signrawtransactionwithkey(rawTx, privKeys, inputs) # 1) The transaction has a complete set of signatures - assert rawTxSigned['complete'] + assert rawTxSigned["complete"] # 2) No script verification error occurred - assert 'errors' not in rawTxSigned + assert "errors" not in rawTxSigned def test_with_lock_outputs(self): self.log.info( - "Test correct error reporting when trying to sign a locked output") + "Test correct error reporting when trying to sign a locked output" + ) self.nodes[0].encryptwallet("password") - rawTx = '020000000156b958f78e3f24e0b2f4e4db1255426b0902027cb37e3ddadb52e37c3557dddb0000000000ffffffff01c0a6b929010000001600149a2ee8c77140a053f36018ac8124a6ececc1668a00000000' + rawTx = "020000000156b958f78e3f24e0b2f4e4db1255426b0902027cb37e3ddadb52e37c3557dddb0000000000ffffffff01c0a6b929010000001600149a2ee8c77140a053f36018ac8124a6ececc1668a00000000" - assert_raises_rpc_error(-13, - "Please enter the wallet passphrase with walletpassphrase first", - self.nodes[0].signrawtransactionwithwallet, - rawTx) + assert_raises_rpc_error( + -13, + "Please enter the wallet passphrase with walletpassphrase first", + self.nodes[0].signrawtransactionwithwallet, + rawTx, + ) def script_verification_error_test(self): """Creates and signs a raw transaction with valid (vin 0), invalid (vin 1) and one missing (vin 2) input script. Expected results: 3) The transaction has no complete set of signatures 4) Two script verification errors occurred 5) Script verification errors have certain properties ("txid", "vout", "scriptSig", "sequence", "error") - 6) The verification errors refer to the invalid (vin 1) and missing input (vin 2)""" + 6) The verification errors refer to the invalid (vin 1) and missing input (vin 2) + """ self.log.info("Test script verification errors") - privKeys = ['cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N'] + privKeys = ["cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N"] inputs = [ # Valid pay-to-pubkey script - {'txid': '9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71', - 'vout': 0, 'amount': 0}, + { + "txid": ( + "9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71" + ), + "vout": 0, + "amount": 0, + }, # Invalid script - {'txid': '5b8673686910442c644b1f4993d8f7753c7c8fcb5c87ee40d56eaeef25204547', - 'vout': 7, 'amount': '1.1'}, + { + "txid": ( + "5b8673686910442c644b1f4993d8f7753c7c8fcb5c87ee40d56eaeef25204547" + ), + "vout": 7, + "amount": "1.1", + }, # Missing scriptPubKey - {'txid': '9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71', - 'vout': 1, 'amount': 2.0}, + { + "txid": ( + "9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71" + ), + "vout": 1, + "amount": 2.0, + }, ] scripts = [ # Valid pay-to-pubkey script - {'txid': '9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71', - 'vout': 0, 'amount': 0, - 'scriptPubKey': '76a91460baa0f494b38ce3c940dea67f3804dc52d1fb9488ac'}, + { + "txid": ( + "9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71" + ), + "vout": 0, + "amount": 0, + "scriptPubKey": "76a91460baa0f494b38ce3c940dea67f3804dc52d1fb9488ac", + }, # Invalid script - {'txid': '5b8673686910442c644b1f4993d8f7753c7c8fcb5c87ee40d56eaeef25204547', - 'vout': 7, 'amount': '1.1', - 'scriptPubKey': 'badbadbadbad'} + { + "txid": ( + "5b8673686910442c644b1f4993d8f7753c7c8fcb5c87ee40d56eaeef25204547" + ), + "vout": 7, + "amount": "1.1", + "scriptPubKey": "badbadbadbad", + }, ] - outputs = {'mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB': 0.1} + outputs = {"mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB": 0.1} rawTx = self.nodes[0].createrawtransaction(inputs, outputs) # Make sure decoderawtransaction is at least marginally sane decodedRawTx = self.nodes[0].decoderawtransaction(rawTx) for i, inp in enumerate(inputs): assert_equal(decodedRawTx["vin"][i]["txid"], inp["txid"]) assert_equal(decodedRawTx["vin"][i]["vout"], inp["vout"]) # Make sure decoderawtransaction throws if there is extra data - assert_raises_rpc_error(-22, "TX decode failed", - self.nodes[0].decoderawtransaction, f"{rawTx}00") + assert_raises_rpc_error( + -22, "TX decode failed", self.nodes[0].decoderawtransaction, f"{rawTx}00" + ) - rawTxSigned = self.nodes[0].signrawtransactionwithkey( - rawTx, privKeys, scripts) + rawTxSigned = self.nodes[0].signrawtransactionwithkey(rawTx, privKeys, scripts) # 3) The transaction has no complete set of signatures - assert not rawTxSigned['complete'] + assert not rawTxSigned["complete"] # 4) Two script verification errors occurred - assert 'errors' in rawTxSigned - assert_equal(len(rawTxSigned['errors']), 2) + assert "errors" in rawTxSigned + assert_equal(len(rawTxSigned["errors"]), 2) # 5) Script verification errors have certain properties - assert 'txid' in rawTxSigned['errors'][0] - assert 'vout' in rawTxSigned['errors'][0] - assert 'scriptSig' in rawTxSigned['errors'][0] - assert 'sequence' in rawTxSigned['errors'][0] - assert 'error' in rawTxSigned['errors'][0] + assert "txid" in rawTxSigned["errors"][0] + assert "vout" in rawTxSigned["errors"][0] + assert "scriptSig" in rawTxSigned["errors"][0] + assert "sequence" in rawTxSigned["errors"][0] + assert "error" in rawTxSigned["errors"][0] # 6) The verification errors refer to the invalid (vin 1) and missing # input (vin 2) - assert_equal(rawTxSigned['errors'][0]['txid'], inputs[1]['txid']) - assert_equal(rawTxSigned['errors'][0]['vout'], inputs[1]['vout']) - assert_equal(rawTxSigned['errors'][1]['txid'], inputs[2]['txid']) - assert_equal(rawTxSigned['errors'][1]['vout'], inputs[2]['vout']) + assert_equal(rawTxSigned["errors"][0]["txid"], inputs[1]["txid"]) + assert_equal(rawTxSigned["errors"][0]["vout"], inputs[1]["vout"]) + assert_equal(rawTxSigned["errors"][1]["txid"], inputs[2]["txid"]) + assert_equal(rawTxSigned["errors"][1]["vout"], inputs[2]["vout"]) def test_fully_signed_tx(self): self.log.info("Test signing a fully signed transaction does nothing") self.nodes[0].walletpassphrase("password", 9999) self.generate(self.nodes[0], 101) rawtx = self.nodes[0].createrawtransaction( - [], [{self.nodes[0].getnewaddress(): 10}]) + [], [{self.nodes[0].getnewaddress(): 10}] + ) fundedtx = self.nodes[0].fundrawtransaction(rawtx) signedtx = self.nodes[0].signrawtransactionwithwallet(fundedtx["hex"]) assert_equal(signedtx["complete"], True) signedtx2 = self.nodes[0].signrawtransactionwithwallet(signedtx["hex"]) assert_equal(signedtx2["complete"], True) assert_equal(signedtx["hex"], signedtx2["hex"]) self.nodes[0].walletlock() def test_sighashes(self): """Creates and signs a raw transaction with various sighashes. Expected result: 1) The transaction is complete if the sighash is valid and has FORKID. 2) The RPC throws an error if the sighash does not contain FORKID. 3) The RPC throws an error if the sighash is invalid.""" - privKeys = ['cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N'] + privKeys = ["cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N"] inputs = [ # Valid pay-to-pubkey script - {'txid': '9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71', - 'vout': 0, 'amount': 3141590, - 'scriptPubKey': '76a91460baa0f494b38ce3c940dea67f3804dc52d1fb9488ac'} + { + "txid": ( + "9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71" + ), + "vout": 0, + "amount": 3141590, + "scriptPubKey": "76a91460baa0f494b38ce3c940dea67f3804dc52d1fb9488ac", + } ] - outputs = {'mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB': 100000} + outputs = {"mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB": 100000} rawTx = self.nodes[0].createrawtransaction(inputs, outputs) valid_sighashes = [ "ALL|FORKID", "NONE|FORKID", "SINGLE|FORKID", "ALL|FORKID|ANYONECANPAY", "NONE|FORKID|ANYONECANPAY", "SINGLE|FORKID|ANYONECANPAY", ] no_forkid_sighashes = [ "ALL", "NONE", "SINGLE", "ALL|ANYONECANPAY", "NONE|ANYONECANPAY", "SINGLE|ANYONECANPAY", ] invalid_sighashes = [ "", "ALL|SINGLE|FORKID", str(0), str(0x20), ] # 1) If the sighash is valid with FORKID, the signature is complete for sighash in valid_sighashes: rawTxSigned = self.nodes[0].signrawtransactionwithkey( - rawTx, privKeys, inputs, sighash) - assert 'complete' in rawTxSigned - assert_equal(rawTxSigned['complete'], True) - assert 'errors' not in rawTxSigned + rawTx, privKeys, inputs, sighash + ) + assert "complete" in rawTxSigned + assert_equal(rawTxSigned["complete"], True) + assert "errors" not in rawTxSigned # 2) If FORKID is missing in the sighash, the RPC throws an error for sighash in no_forkid_sighashes: - assert_raises_rpc_error(-8, "Signature must use SIGHASH_FORKID", - self.nodes[0].signrawtransactionwithkey, - rawTx, privKeys, inputs, sighash) + assert_raises_rpc_error( + -8, + "Signature must use SIGHASH_FORKID", + self.nodes[0].signrawtransactionwithkey, + rawTx, + privKeys, + inputs, + sighash, + ) # 3) If the sighash is invalid the RPC throws an error for sighash in invalid_sighashes: - assert_raises_rpc_error(-1, f"{sighash} is not a valid sighash parameter.", - self.nodes[0].signrawtransactionwithkey, - rawTx, privKeys, inputs, sighash) + assert_raises_rpc_error( + -1, + f"{sighash} is not a valid sighash parameter.", + self.nodes[0].signrawtransactionwithkey, + rawTx, + privKeys, + inputs, + sighash, + ) def multiwallet_signing_test(self): """Creates and signs a raw transaction with a multiwallet node. Expected results: 1) The transaction is not signed if no wallet is specified 2) The transaction is signed if the correct wallet URI is given""" inputs = [ # Valid pay-to-pubkey scripts - {'txid': '9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71', - 'vout': 0, 'amount': 3141590, - 'scriptPubKey': '76a91460baa0f494b38ce3c940dea67f3804dc52d1fb9488ac'}, + { + "txid": ( + "9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71" + ), + "vout": 0, + "amount": 3141590, + "scriptPubKey": "76a91460baa0f494b38ce3c940dea67f3804dc52d1fb9488ac", + }, ] - outputs = {'mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB': 100000} + outputs = {"mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB": 100000} multiwallet_node = self.nodes[0] rawTx = multiwallet_node.createrawtransaction(inputs, outputs) # The multiwallet node cannot sign the transaction if no wallet is # specified - assert_raises_rpc_error(-19, RPC_WALLET_NOT_SPECIFIED, - multiwallet_node.signrawtransactionwithwallet, - rawTx) + assert_raises_rpc_error( + -19, + RPC_WALLET_NOT_SPECIFIED, + multiwallet_node.signrawtransactionwithwallet, + rawTx, + ) # The multiwallet node can sign the transaction using w1 - w1 = multiwallet_node.get_wallet_rpc('w1') - self.generatetoaddress(multiwallet_node, - nblocks=101, - address=w1.getnewaddress(label='coinbase')) + w1 = multiwallet_node.get_wallet_rpc("w1") + self.generatetoaddress( + multiwallet_node, nblocks=101, address=w1.getnewaddress(label="coinbase") + ) utxo = w1.listunspent()[0] - inputs = [{ - 'txid': utxo['txid'], - 'vout': utxo['vout'], - }] + inputs = [ + { + "txid": utxo["txid"], + "vout": utxo["vout"], + } + ] rawTx_w1 = w1.createrawtransaction(inputs, outputs) rawTxSigned_w1 = w1.signrawtransactionwithwallet(rawTx_w1) - assert rawTxSigned_w1['complete'] - assert 'errors' not in rawTxSigned_w1 + assert rawTxSigned_w1["complete"] + assert "errors" not in rawTxSigned_w1 def run_test(self): self.successful_signing_test() self.script_verification_error_test() self.test_sighashes() self.test_with_lock_outputs() self.test_fully_signed_tx() # The multiwalet require the node to use different flags, so we run it # last. self.restart_node(0, ["-wallet=w1", "-wallet=w2"]) self.multiwallet_signing_test() -if __name__ == '__main__': +if __name__ == "__main__": SignRawTransactionsTest().main() diff --git a/test/functional/rpc_txoutproof.py b/test/functional/rpc_txoutproof.py index 9d61c744e..bd997eaf5 100755 --- a/test/functional/rpc_txoutproof.py +++ b/test/functional/rpc_txoutproof.py @@ -1,146 +1,210 @@ #!/usr/bin/env python3 # 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) - txid1 = miniwallet.send_self_transfer(from_node=self.nodes[0])['txid'] - txid2 = miniwallet.send_self_transfer(from_node=self.nodes[0])['txid'] + 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]) + 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) + 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'] + 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')", + ( + "txid must be of length 64 (not 32, for" + " '00000000000000000000000000000000')" + ), self.nodes[0].gettxoutproof, ["00000000000000000000000000000000"], - blockhash) + blockhash, + ) assert_raises_rpc_error( -8, - "txid must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", + ( + "txid must be hexadecimal string (not" + " 'ZZZ0000000000000000000000000000000000000000000000000000000000000')" + ), self.nodes[0].gettxoutproof, ["ZZZ0000000000000000000000000000000000000000000000000000000000000"], - blockhash) + blockhash, + ) # Invalid blockhashes assert_raises_rpc_error( -8, - "blockhash must be of length 64 (not 32, for '00000000000000000000000000000000')", + ( + "blockhash must be of length 64 (not 32, for" + " '00000000000000000000000000000000')" + ), self.nodes[0].gettxoutproof, [txid_spent], - "00000000000000000000000000000000") + "00000000000000000000000000000000", + ) assert_raises_rpc_error( -8, - "blockhash must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", + ( + "blockhash must be hexadecimal string (not" + " 'ZZZ0000000000000000000000000000000000000000000000000000000000000')" + ), self.nodes[0].gettxoutproof, [txid_spent], - "ZZZ0000000000000000000000000000000000000000000000000000000000000") + "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]) + 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]) + 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") + 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]) + 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)) + 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]) + 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]) + 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, []) + 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]) + 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__': +if __name__ == "__main__": MerkleBlockTest().main() diff --git a/test/functional/rpc_uptime.py b/test/functional/rpc_uptime.py index e86f91b1d..9c04fc112 100755 --- a/test/functional/rpc_uptime.py +++ b/test/functional/rpc_uptime.py @@ -1,30 +1,30 @@ #!/usr/bin/env python3 # Copyright (c) 2017-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 RPC call related to the uptime command. Test corresponds to code in rpc/server.cpp. """ import time from test_framework.test_framework import BitcoinTestFramework class UptimeTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True def run_test(self): self._test_uptime() def _test_uptime(self): wait_time = 10 self.nodes[0].setmocktime(int(time.time() + wait_time)) assert self.nodes[0].uptime() >= wait_time -if __name__ == '__main__': +if __name__ == "__main__": UptimeTest().main() diff --git a/test/functional/rpc_users.py b/test/functional/rpc_users.py index a13df920a..d835e4c1a 100755 --- a/test/functional/rpc_users.py +++ b/test/functional/rpc_users.py @@ -1,124 +1,132 @@ #!/usr/bin/env python3 # Copyright (c) 2015-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 multiple RPC users.""" import configparser import http.client import os import string import subprocess import sys import urllib.parse from random import SystemRandom from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, get_datadir_path, str_to_b64str def call_with_auth(node, user, password): url = urllib.parse.urlparse(node.url) - headers = { - "Authorization": - f"Basic {str_to_b64str(f'{user}:{password}')}"} + headers = {"Authorization": f"Basic {str_to_b64str(f'{user}:{password}')}"} conn = http.client.HTTPConnection(url.hostname, url.port) conn.connect() - conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) + conn.request("POST", "/", '{"method": "getbestblockhash"}', headers) resp = conn.getresponse() conn.close() return resp class HTTPBasicsTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 self.supports_cli = False def setup_chain(self): super().setup_chain() # Append rpcauth to bitcoin.conf before initialization self.rtpassword = "cA773lm788buwYe4g4WT+05pKyNruVKjQ25x3n0DQcM=" rpcauth = "rpcauth=rt:93648e835a54c573682c2eb19f882535$7681e9c5b74bdd85e78166031d2058e1069b3ed7ed967c93fc63abba06f31144" self.rpcuser = "rpcuser💻" self.rpcpassword = "rpcpassword🔑" config = configparser.ConfigParser() - config.read_file(open(self.options.configfile, encoding='utf-8')) - gen_rpcauth = config['environment']['RPCAUTH'] + config.read_file(open(self.options.configfile, encoding="utf-8")) + gen_rpcauth = config["environment"]["RPCAUTH"] # Generate RPCAUTH with specified password self.rt2password = "8/F3uMDw4KSEbw96U3CA1C4X05dkHDN2BPFjTgZW4KI=" p = subprocess.Popen( - [sys.executable, gen_rpcauth, 'rt2', self.rt2password], - stdout=subprocess.PIPE, universal_newlines=True) + [sys.executable, gen_rpcauth, "rt2", self.rt2password], + stdout=subprocess.PIPE, + universal_newlines=True, + ) lines = p.stdout.read().splitlines() rpcauth2 = lines[1] # Generate RPCAUTH without specifying password - self.user = ''.join( - SystemRandom().choice( - string.ascii_letters + string.digits) for _ in range(10)) + self.user = "".join( + SystemRandom().choice(string.ascii_letters + string.digits) + for _ in range(10) + ) p = subprocess.Popen( [sys.executable, gen_rpcauth, self.user], - stdout=subprocess.PIPE, universal_newlines=True) + stdout=subprocess.PIPE, + universal_newlines=True, + ) lines = p.stdout.read().splitlines() rpcauth3 = lines[1] self.password = lines[3] - with open(os.path.join(get_datadir_path(self.options.tmpdir, 0), "bitcoin.conf"), 'a', encoding='utf8') as f: + with open( + os.path.join(get_datadir_path(self.options.tmpdir, 0), "bitcoin.conf"), + "a", + encoding="utf8", + ) as f: f.write(f"{rpcauth}\n") f.write(f"{rpcauth2}\n") f.write(f"{rpcauth3}\n") - with open(os.path.join(get_datadir_path(self.options.tmpdir, 1), "bitcoin.conf"), 'a', encoding='utf8') as f: + with open( + os.path.join(get_datadir_path(self.options.tmpdir, 1), "bitcoin.conf"), + "a", + encoding="utf8", + ) as f: f.write(f"rpcuser={self.rpcuser}\n") f.write(f"rpcpassword={self.rpcpassword}\n") def test_auth(self, node, user, password): - self.log.info('Correct...') + self.log.info("Correct...") assert_equal(200, call_with_auth(node, user, password).status) - self.log.info('Wrong...') - assert_equal( - 401, call_with_auth(node, user, f"{password}wrong").status) + self.log.info("Wrong...") + assert_equal(401, call_with_auth(node, user, f"{password}wrong").status) - self.log.info('Wrong...') - assert_equal( - 401, call_with_auth(node, f"{user}wrong", password).status) + self.log.info("Wrong...") + assert_equal(401, call_with_auth(node, f"{user}wrong", password).status) - self.log.info('Wrong...') + self.log.info("Wrong...") assert_equal( - 401, call_with_auth(node, f"{user}wrong", - f"{password}wrong").status) + 401, call_with_auth(node, f"{user}wrong", f"{password}wrong").status + ) def run_test(self): - self.log.info('Check correctness of the rpcauth config option') + self.log.info("Check correctness of the rpcauth config option") url = urllib.parse.urlparse(self.nodes[0].url) self.test_auth(self.nodes[0], url.username, url.password) - self.test_auth(self.nodes[0], 'rt', self.rtpassword) - self.test_auth(self.nodes[0], 'rt2', self.rt2password) + self.test_auth(self.nodes[0], "rt", self.rtpassword) + self.test_auth(self.nodes[0], "rt2", self.rt2password) self.test_auth(self.nodes[0], self.user, self.password) - self.log.info( - 'Check correctness of the rpcuser/rpcpassword config options') + self.log.info("Check correctness of the rpcuser/rpcpassword config options") url = urllib.parse.urlparse(self.nodes[1].url) self.test_auth(self.nodes[1], self.rpcuser, self.rpcpassword) self.log.info( - 'Check that failure to write cookie file will abort the node gracefully') + "Check that failure to write cookie file will abort the node gracefully" + ) self.stop_node(0) cookie_file = os.path.join( - get_datadir_path(self.options.tmpdir, 0), - self.chain, - '.cookie.tmp') + get_datadir_path(self.options.tmpdir, 0), self.chain, ".cookie.tmp" + ) os.mkdir(cookie_file) - init_error = 'Error: Unable to start HTTP server. See debug log for details.' + init_error = "Error: Unable to start HTTP server. See debug log for details." self.nodes[0].assert_start_raises_init_error(expected_msg=init_error) -if __name__ == '__main__': +if __name__ == "__main__": HTTPBasicsTest().main() diff --git a/test/functional/rpc_whitelist.py b/test/functional/rpc_whitelist.py index 8db9919cf..5b1f1aea5 100755 --- a/test/functional/rpc_whitelist.py +++ b/test/functional/rpc_whitelist.py @@ -1,163 +1,158 @@ #!/usr/bin/env python3 # Copyright (c) 2017-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. """ A test for RPC users with restricted permissions """ import http.client import os import urllib.parse from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, get_datadir_path, str_to_b64str def rpccall(node, user, method): url = urllib.parse.urlparse(node.url) - headers = { - "Authorization": f"Basic {str_to_b64str(f'{user[0]}:{user[3]}')}"} + headers = {"Authorization": f"Basic {str_to_b64str(f'{user[0]}:{user[3]}')}"} conn = http.client.HTTPConnection(url.hostname, url.port) conn.connect() - conn.request('POST', '/', f"{{\"method\": \"{method}\"}}", headers) + conn.request("POST", "/", f'{{"method": "{method}"}}', headers) resp = conn.getresponse() conn.close() return resp class RPCWhitelistTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 self.supports_cli = False def setup_chain(self): super().setup_chain() # 0 => Username # 1 => Password (Hashed) # 2 => Permissions # 3 => Password Plaintext self.users = [ - ["user1", - "50358aa884c841648e0700b073c32b2e$b73e95fff0748cc0b517859d2ca47d9bac1aa78231f3e48fa9222b612bd2083e", - "getbestblockhash,getblockcount,", - "12345"], - ["user2", - "8650ba41296f62092377a38547f361de$4620db7ba063ef4e2f7249853e9f3c5c3592a9619a759e3e6f1c63f2e22f1d21", - "getblockcount", - "54321"] + [ + "user1", + "50358aa884c841648e0700b073c32b2e$b73e95fff0748cc0b517859d2ca47d9bac1aa78231f3e48fa9222b612bd2083e", + "getbestblockhash,getblockcount,", + "12345", + ], + [ + "user2", + "8650ba41296f62092377a38547f361de$4620db7ba063ef4e2f7249853e9f3c5c3592a9619a759e3e6f1c63f2e22f1d21", + "getblockcount", + "54321", + ], ] # For exceptions self.strange_users = [ # Test empty - ["strangedude", - "62d67dffec03836edd698314f1b2be62$c2fb4be29bb0e3646298661123cf2d8629640979cabc268ef05ea613ab54068d", - ":", - "s7R4nG3R7H1nGZ"], - ["strangedude2", - "575c012c7fe4b1e83b9d809412da3ef7$09f448d0acfc19924dd62ecb96004d3c2d4b91f471030dfe43c6ea64a8f658c1", - "", - "s7R4nG3R7H1nGZ"], + [ + "strangedude", + "62d67dffec03836edd698314f1b2be62$c2fb4be29bb0e3646298661123cf2d8629640979cabc268ef05ea613ab54068d", + ":", + "s7R4nG3R7H1nGZ", + ], + [ + "strangedude2", + "575c012c7fe4b1e83b9d809412da3ef7$09f448d0acfc19924dd62ecb96004d3c2d4b91f471030dfe43c6ea64a8f658c1", + "", + "s7R4nG3R7H1nGZ", + ], # Test trailing comma - ["strangedude3", - "23189c561b5975a56f4cf94030495d61$3a2f6aac26351e2257428550a553c4c1979594e36675bbd3db692442387728c0", - ":getblockcount,", - "s7R4nG3R7H1nGZ"], + [ + "strangedude3", + "23189c561b5975a56f4cf94030495d61$3a2f6aac26351e2257428550a553c4c1979594e36675bbd3db692442387728c0", + ":getblockcount,", + "s7R4nG3R7H1nGZ", + ], # Test overwrite - ["strangedude4", - "990c895760a70df83949e8278665e19a$8f0906f20431ff24cb9e7f5b5041e4943bdf2a5c02a19ef4960dcf45e72cde1c", - ":getblockcount, getbestblockhash", - "s7R4nG3R7H1nGZ"], - ["strangedude4", - "990c895760a70df83949e8278665e19a$8f0906f20431ff24cb9e7f5b5041e4943bdf2a5c02a19ef4960dcf45e72cde1c", - ":getblockcount", - "s7R4nG3R7H1nGZ"], + [ + "strangedude4", + "990c895760a70df83949e8278665e19a$8f0906f20431ff24cb9e7f5b5041e4943bdf2a5c02a19ef4960dcf45e72cde1c", + ":getblockcount, getbestblockhash", + "s7R4nG3R7H1nGZ", + ], + [ + "strangedude4", + "990c895760a70df83949e8278665e19a$8f0906f20431ff24cb9e7f5b5041e4943bdf2a5c02a19ef4960dcf45e72cde1c", + ":getblockcount", + "s7R4nG3R7H1nGZ", + ], # Testing the same permission twice - ["strangedude5", - "d12c6e962d47a454f962eb41225e6ec8$2dd39635b155536d3c1a2e95d05feff87d5ba55f2d5ff975e6e997a836b717c9", - ":getblockcount,getblockcount", - "s7R4nG3R7H1nGZ"] + [ + "strangedude5", + "d12c6e962d47a454f962eb41225e6ec8$2dd39635b155536d3c1a2e95d05feff87d5ba55f2d5ff975e6e997a836b717c9", + ":getblockcount,getblockcount", + "s7R4nG3R7H1nGZ", + ], ] # These commands shouldn't be allowed for any user to test failures self.never_allowed = ["getnetworkinfo"] - with open(os.path.join(get_datadir_path(self.options.tmpdir, 0), "bitcoin.conf"), 'a', encoding='utf8') as f: + with open( + os.path.join(get_datadir_path(self.options.tmpdir, 0), "bitcoin.conf"), + "a", + encoding="utf8", + ) as f: f.write("\nrpcwhitelistdefault=0\n") for user in self.users: f.write(f"rpcauth={user[0]}:{user[1]}\n") f.write(f"rpcwhitelist={user[0]}:{user[2]}\n") # Special cases for strangedude in self.strange_users: - f.write( - f"rpcauth={strangedude[0]}:{strangedude[1]}\n") - f.write( - f"rpcwhitelist={strangedude[0]}{strangedude[2]}\n") + f.write(f"rpcauth={strangedude[0]}:{strangedude[1]}\n") + f.write(f"rpcwhitelist={strangedude[0]}{strangedude[2]}\n") def run_test(self): for user in self.users: permissions = user[2].replace(" ", "").split(",") # Pop all empty items i = 0 while i < len(permissions): - if permissions[i] == '': + if permissions[i] == "": permissions.pop(i) i += 1 for permission in permissions: self.log.info( - f"[{user[0]}]: Testing a permitted permission ({permission})") - assert_equal( - 200, - rpccall( - self.nodes[0], - user, - permission).status) + f"[{user[0]}]: Testing a permitted permission ({permission})" + ) + assert_equal(200, rpccall(self.nodes[0], user, permission).status) for permission in self.never_allowed: self.log.info( - f"[{user[0]}]: Testing a non permitted permission ({permission})") - assert_equal( - 403, - rpccall( - self.nodes[0], - user, - permission).status) + f"[{user[0]}]: Testing a non permitted permission ({permission})" + ) + assert_equal(403, rpccall(self.nodes[0], user, permission).status) # Now test the strange users for permission in self.never_allowed: self.log.info("Strange test 1") assert_equal( - 403, - rpccall( - self.nodes[0], - self.strange_users[0], - permission).status) + 403, rpccall(self.nodes[0], self.strange_users[0], permission).status + ) for permission in self.never_allowed: self.log.info("Strange test 2") assert_equal( - 403, - rpccall( - self.nodes[0], - self.strange_users[1], - permission).status) + 403, rpccall(self.nodes[0], self.strange_users[1], permission).status + ) self.log.info("Strange test 3") assert_equal( - 200, - rpccall( - self.nodes[0], - self.strange_users[2], - "getblockcount").status) + 200, rpccall(self.nodes[0], self.strange_users[2], "getblockcount").status + ) self.log.info("Strange test 4") assert_equal( 403, - rpccall( - self.nodes[0], - self.strange_users[3], - "getbestblockhash").status) + rpccall(self.nodes[0], self.strange_users[3], "getbestblockhash").status, + ) self.log.info("Strange test 5") assert_equal( - 200, - rpccall( - self.nodes[0], - self.strange_users[4], - "getblockcount").status) + 200, rpccall(self.nodes[0], self.strange_users[4], "getblockcount").status + ) if __name__ == "__main__": RPCWhitelistTest().main()