aboutsummaryrefslogtreecommitdiff
path: root/test/functional
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional')
-rw-r--r--test/functional/README.md12
-rwxr-xr-xtest/functional/abandonconflict.py2
-rwxr-xr-xtest/functional/address_types.py199
-rwxr-xr-xtest/functional/assumevalid.py68
-rwxr-xr-xtest/functional/bip65-cltv-p2p.py43
-rwxr-xr-xtest/functional/bip68-112-113-p2p.py8
-rwxr-xr-xtest/functional/bip68-sequence.py5
-rwxr-xr-xtest/functional/bip9-softforks.py10
-rwxr-xr-xtest/functional/bipdersig-p2p.py44
-rwxr-xr-xtest/functional/bitcoin_cli.py5
-rwxr-xr-xtest/functional/blockchain.py4
-rwxr-xr-xtest/functional/bumpfee.py5
-rwxr-xr-xtest/functional/conf_args.py49
-rwxr-xr-xtest/functional/create_cache.py3
-rwxr-xr-xtest/functional/decodescript.py2
-rwxr-xr-xtest/functional/disablewallet.py2
-rwxr-xr-xtest/functional/disconnect_ban.py2
-rwxr-xr-xtest/functional/example_test.py61
-rwxr-xr-xtest/functional/feature_logging.py59
-rwxr-xr-xtest/functional/forknotify.py59
-rwxr-xr-xtest/functional/fundrawtransaction.py2
-rwxr-xr-xtest/functional/getblocktemplate_longpoll.py2
-rwxr-xr-xtest/functional/getchaintips.py2
-rwxr-xr-xtest/functional/httpbasics.py2
-rwxr-xr-xtest/functional/import-rescan.py4
-rwxr-xr-xtest/functional/importmulti.py15
-rwxr-xr-xtest/functional/importprunedfunds.py2
-rwxr-xr-xtest/functional/invalidateblock.py2
-rwxr-xr-xtest/functional/invalidblockrequest.py5
-rwxr-xr-xtest/functional/invalidtxrequest.py4
-rwxr-xr-xtest/functional/keypool-topup.py4
-rwxr-xr-xtest/functional/keypool.py2
-rwxr-xr-xtest/functional/listsinceblock.py35
-rwxr-xr-xtest/functional/listtransactions.py2
-rwxr-xr-xtest/functional/maxuploadtarget.py84
-rwxr-xr-xtest/functional/mempool_limit.py14
-rwxr-xr-xtest/functional/mempool_packages.py2
-rwxr-xr-xtest/functional/mempool_persist.py8
-rwxr-xr-xtest/functional/mempool_reorg.py2
-rwxr-xr-xtest/functional/mempool_resurrect_test.py2
-rwxr-xr-xtest/functional/mempool_spendcoinbase.py2
-rwxr-xr-xtest/functional/merkle_blocks.py2
-rwxr-xr-xtest/functional/minchainwork.py10
-rwxr-xr-xtest/functional/mining.py2
-rwxr-xr-xtest/functional/multi_rpc.py2
-rwxr-xr-xtest/functional/multiwallet.py67
-rwxr-xr-xtest/functional/node_network_limited.py57
-rwxr-xr-xtest/functional/notifications.py86
-rwxr-xr-xtest/functional/nulldummy.py10
-rwxr-xr-xtest/functional/p2p-acceptblock.py308
-rwxr-xr-xtest/functional/p2p-compactblocks.py43
-rwxr-xr-xtest/functional/p2p-feefilter.py34
-rwxr-xr-xtest/functional/p2p-fingerprint.py152
-rwxr-xr-xtest/functional/p2p-fullblocktest.py14
-rwxr-xr-xtest/functional/p2p-leaktests.py114
-rwxr-xr-xtest/functional/p2p-mempool.py16
-rwxr-xr-xtest/functional/p2p-segwit.py321
-rwxr-xr-xtest/functional/p2p-timeouts.py51
-rwxr-xr-xtest/functional/p2p-versionbits-warning.py20
-rwxr-xr-xtest/functional/preciousblock.py2
-rwxr-xr-xtest/functional/prioritise_transaction.py21
-rwxr-xr-xtest/functional/proxy_test.py2
-rwxr-xr-xtest/functional/pruning.py2
-rwxr-xr-xtest/functional/rawtransactions.py108
-rwxr-xr-xtest/functional/receivedby.py126
-rwxr-xr-xtest/functional/reindex.py2
-rwxr-xr-xtest/functional/replace-by-fee.py15
-rwxr-xr-xtest/functional/rest.py2
-rwxr-xr-xtest/functional/rpcbind_test.py2
-rwxr-xr-xtest/functional/rpcnamedargs.py2
-rwxr-xr-xtest/functional/segwit.py49
-rwxr-xr-xtest/functional/sendheaders.py315
-rwxr-xr-xtest/functional/signmessages.py3
-rwxr-xr-xtest/functional/signrawtransactions.py2
-rwxr-xr-xtest/functional/smartfees.py2
-rw-r--r--test/functional/test_framework/address.py2
-rw-r--r--test/functional/test_framework/authproxy.py74
-rw-r--r--test/functional/test_framework/blockstore.py10
-rw-r--r--test/functional/test_framework/blocktools.py2
-rwxr-xr-xtest/functional/test_framework/comptool.py149
-rw-r--r--test/functional/test_framework/coverage.py30
-rw-r--r--test/functional/test_framework/key.py2
-rw-r--r--test/functional/test_framework/messages.py1308
-rwxr-xr-xtest/functional/test_framework/mininode.py1991
-rw-r--r--test/functional/test_framework/netutil.py2
-rw-r--r--test/functional/test_framework/script.py6
-rw-r--r--test/functional/test_framework/siphash.py2
-rw-r--r--test/functional/test_framework/socks5.py10
-rwxr-xr-xtest/functional/test_framework/test_framework.py43
-rwxr-xr-xtest/functional/test_framework/test_node.py113
-rw-r--r--test/functional/test_framework/util.py2
-rwxr-xr-xtest/functional/test_runner.py50
-rwxr-xr-xtest/functional/txn_clone.py17
-rwxr-xr-xtest/functional/txn_doublespend.py2
-rwxr-xr-xtest/functional/uacomment.py35
-rwxr-xr-xtest/functional/wallet-accounts.py139
-rwxr-xr-xtest/functional/wallet-dump.py48
-rwxr-xr-xtest/functional/wallet-encryption.py2
-rwxr-xr-xtest/functional/wallet-hd.py25
-rwxr-xr-xtest/functional/wallet.py25
-rwxr-xr-xtest/functional/walletbackup.py24
-rwxr-xr-xtest/functional/zapwallettxes.py2
-rwxr-xr-xtest/functional/zmq_test.py179
103 files changed, 4063 insertions, 3034 deletions
diff --git a/test/functional/README.md b/test/functional/README.md
index 8d2cd02345..d6ce490ab3 100644
--- a/test/functional/README.md
+++ b/test/functional/README.md
@@ -17,7 +17,7 @@ don't have test cases for.
#### Style guidelines
-- Where possible, try to adhere to [PEP-8 guidelines]([https://www.python.org/dev/peps/pep-0008/)
+- Where possible, try to adhere to [PEP-8 guidelines](https://www.python.org/dev/peps/pep-0008/)
- Use a python linter like flake8 before submitting PRs to catch common style
nits (eg trailing whitespace, unused imports, etc)
- Avoid wildcard imports where possible
@@ -77,12 +77,12 @@ wrappers for them, `msg_block`, `msg_tx`, etc).
with the bitcoind(s) being tested (using python's asyncore package); the other
implements the test logic.
-- `NodeConn` is the class used to connect to a bitcoind. If you implement
-a callback class that derives from `NodeConnCB` and pass that to the
-`NodeConn` object, your code will receive the appropriate callbacks when
-events of interest arrive.
+- `P2PConnection` is the class used to connect to a bitcoind. `P2PInterface`
+contains the higher level logic for processing P2P payloads and connecting to
+the Bitcoin Core node application logic. For custom behaviour, subclass the
+P2PInterface object and override the callback methods.
-- Call `NetworkThread.start()` after all `NodeConn` objects are created to
+- Call `network_thread_start()` after all `P2PInterface` objects are created to
start the networking thread. (Continue with the test logic in your existing
thread.)
diff --git a/test/functional/abandonconflict.py b/test/functional/abandonconflict.py
index e8dbc86469..14964438af 100755
--- a/test/functional/abandonconflict.py
+++ b/test/functional/abandonconflict.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 the abandontransaction RPC.
diff --git a/test/functional/address_types.py b/test/functional/address_types.py
new file mode 100755
index 0000000000..cb119c04b0
--- /dev/null
+++ b/test/functional/address_types.py
@@ -0,0 +1,199 @@
+#!/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 that the wallet can send and receive using all combinations of address types.
+
+There are 4 nodes-under-test:
+ - node0 uses legacy addresses
+ - node1 uses p2sh/segwit addresses
+ - node2 uses p2sh/segwit addresses and bech32 addresses for change
+ - node3 uses bech32 addresses
+
+node4 exists to generate new blocks.
+
+The script is a series of tests, iterating over the 4 nodes. In each iteration
+of the test, one node sends:
+ - 10/101th of its balance to itself (using getrawchangeaddress for single key addresses)
+ - 20/101th to the next node
+ - 30/101th to the node after that
+ - 40/101th to the remaining node
+ - 1/101th remains as fee+change
+
+Iterate over each node for single key addresses, and then over each node for
+multisig addresses. In a second iteration, the same is done, but with explicit address_type
+parameters passed to getnewaddress and getrawchangeaddress. Node0 and node3 send to p2sh,
+node 1 sends to bech32, and node2 sends to legacy. As every node sends coins after receiving,
+this also verifies that spending coins sent to all these address types works."""
+
+from decimal import Decimal
+import itertools
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal, assert_greater_than, connect_nodes_bi, sync_blocks, sync_mempools
+
+class AddressTypeTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 5
+ self.extra_args = [["-addresstype=legacy"], ["-addresstype=p2sh-segwit"], ["-addresstype=p2sh-segwit", "-changetype=bech32"], ["-addresstype=bech32"], []]
+
+ def setup_network(self):
+ self.setup_nodes()
+
+ # Fully mesh-connect nodes for faster mempool sync
+ for i, j in itertools.product(range(self.num_nodes), repeat=2):
+ if i > j:
+ connect_nodes_bi(self.nodes, i, j)
+ self.sync_all()
+
+ def get_balances(self, confirmed=True):
+ """Return a list of confirmed or unconfirmed balances."""
+ if confirmed:
+ return [self.nodes[i].getbalance() for i in range(4)]
+ else:
+ return [self.nodes[i].getunconfirmedbalance() for i in range(4)]
+
+ def test_address(self, node, address, multisig, typ):
+ """Run sanity checks on an address."""
+ info = self.nodes[node].validateaddress(address)
+ assert(info['isvalid'])
+ if not multisig and typ == 'legacy':
+ # P2PKH
+ assert(not info['isscript'])
+ assert(not info['iswitness'])
+ assert('pubkey' in info)
+ elif not multisig and typ == 'p2sh-segwit':
+ # P2SH-P2WPKH
+ assert(info['isscript'])
+ assert(not info['iswitness'])
+ assert_equal(info['script'], 'witness_v0_keyhash')
+ assert('pubkey' in info)
+ elif not multisig and typ == 'bech32':
+ # P2WPKH
+ assert(not info['isscript'])
+ assert(info['iswitness'])
+ assert_equal(info['witness_version'], 0)
+ assert_equal(len(info['witness_program']), 40)
+ assert('pubkey' in info)
+ elif typ == 'legacy':
+ # P2SH-multisig
+ assert(info['isscript'])
+ assert_equal(info['script'], 'multisig')
+ assert(not info['iswitness'])
+ assert('pubkeys' in info)
+ elif typ == 'p2sh-segwit':
+ # P2SH-P2WSH-multisig
+ assert(info['isscript'])
+ assert_equal(info['script'], 'witness_v0_scripthash')
+ assert(not info['iswitness'])
+ assert(info['embedded']['isscript'])
+ assert_equal(info['embedded']['script'], 'multisig')
+ assert(info['embedded']['iswitness'])
+ assert_equal(info['embedded']['witness_version'], 0)
+ assert_equal(len(info['embedded']['witness_program']), 64)
+ assert('pubkeys' in info['embedded'])
+ elif typ == 'bech32':
+ # P2WSH-multisig
+ assert(info['isscript'])
+ assert_equal(info['script'], 'multisig')
+ assert(info['iswitness'])
+ assert_equal(info['witness_version'], 0)
+ assert_equal(len(info['witness_program']), 64)
+ assert('pubkeys' in info)
+ else:
+ # Unknown type
+ assert(False)
+
+ def run_test(self):
+ # Mine 101 blocks on node4 to bring nodes out of IBD and make sure that
+ # no coinbases are maturing for the nodes-under-test during the test
+ self.nodes[4].generate(101)
+ sync_blocks(self.nodes)
+
+ uncompressed_1 = "0496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858ee"
+ uncompressed_2 = "047211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073dee6c89064984f03385237d92167c13e236446b417ab79a0fcae412ae3316b77"
+ compressed_1 = "0296b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52"
+ compressed_2 = "037211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073"
+
+ # addmultisigaddress with at least 1 uncompressed key should return a legacy address.
+ for node in range(4):
+ self.test_address(node, self.nodes[node].addmultisigaddress(2, [uncompressed_1, uncompressed_2]), True, 'legacy')
+ self.test_address(node, self.nodes[node].addmultisigaddress(2, [compressed_1, uncompressed_2]), True, 'legacy')
+ self.test_address(node, self.nodes[node].addmultisigaddress(2, [uncompressed_1, compressed_2]), True, 'legacy')
+ # addmultisigaddress with all compressed keys should return the appropriate address type (even when the keys are not ours).
+ self.test_address(0, self.nodes[0].addmultisigaddress(2, [compressed_1, compressed_2]), True, 'legacy')
+ self.test_address(1, self.nodes[1].addmultisigaddress(2, [compressed_1, compressed_2]), True, 'p2sh-segwit')
+ self.test_address(2, self.nodes[2].addmultisigaddress(2, [compressed_1, compressed_2]), True, 'p2sh-segwit')
+ self.test_address(3, self.nodes[3].addmultisigaddress(2, [compressed_1, compressed_2]), True, 'bech32')
+
+ for explicit_type, multisig, from_node in itertools.product([False, True], [False, True], range(4)):
+ address_type = None
+ if explicit_type and not multisig:
+ if from_node == 1:
+ address_type = 'bech32'
+ elif from_node == 0 or from_node == 3:
+ address_type = 'p2sh-segwit'
+ else:
+ address_type = 'legacy'
+ self.log.info("Sending from node {} ({}) with{} multisig using {}".format(from_node, self.extra_args[from_node], "" if multisig else "out", "default" if address_type is None else address_type))
+ old_balances = self.get_balances()
+ self.log.debug("Old balances are {}".format(old_balances))
+ to_send = (old_balances[from_node] / 101).quantize(Decimal("0.00000001"))
+ sends = {}
+
+ self.log.debug("Prepare sends")
+ for n, to_node in enumerate(range(from_node, from_node + 4)):
+ to_node %= 4
+ change = False
+ if not multisig:
+ if from_node == to_node:
+ # When sending non-multisig to self, use getrawchangeaddress
+ address = self.nodes[to_node].getrawchangeaddress(address_type=address_type)
+ change = True
+ else:
+ address = self.nodes[to_node].getnewaddress(address_type=address_type)
+ else:
+ addr1 = self.nodes[to_node].getnewaddress()
+ addr2 = self.nodes[to_node].getnewaddress()
+ address = self.nodes[to_node].addmultisigaddress(2, [addr1, addr2])
+
+ # Do some sanity checking on the created address
+ if address_type is not None:
+ typ = address_type
+ elif to_node == 0:
+ typ = 'legacy'
+ elif to_node == 1 or (to_node == 2 and not change):
+ typ = 'p2sh-segwit'
+ else:
+ typ = 'bech32'
+ self.test_address(to_node, address, multisig, typ)
+
+ # Output entry
+ sends[address] = to_send * 10 * (1 + n)
+
+ self.log.debug("Sending: {}".format(sends))
+ self.nodes[from_node].sendmany("", sends)
+ sync_mempools(self.nodes)
+
+ unconf_balances = self.get_balances(False)
+ self.log.debug("Check unconfirmed balances: {}".format(unconf_balances))
+ assert_equal(unconf_balances[from_node], 0)
+ for n, to_node in enumerate(range(from_node + 1, from_node + 4)):
+ to_node %= 4
+ assert_equal(unconf_balances[to_node], to_send * 10 * (2 + n))
+
+ # node4 collects fee and block subsidy to keep accounting simple
+ self.nodes[4].generate(1)
+ sync_blocks(self.nodes)
+
+ new_balances = self.get_balances()
+ self.log.debug("Check new balances: {}".format(new_balances))
+ # We don't know what fee was set, so we can only check bounds on the balance of the sending node
+ assert_greater_than(new_balances[from_node], to_send * 10)
+ assert_greater_than(to_send * 11, new_balances[from_node])
+ for n, to_node in enumerate(range(from_node + 1, from_node + 4)):
+ to_node %= 4
+ assert_equal(new_balances[to_node], old_balances[to_node] + to_send * 10 * (2 + n))
+
+if __name__ == '__main__':
+ AddressTypeTest().main()
diff --git a/test/functional/assumevalid.py b/test/functional/assumevalid.py
index 65685c48b7..5a09142412 100755
--- a/test/functional/assumevalid.py
+++ b/test/functional/assumevalid.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 logic for skipping signature validation on old blocks.
@@ -38,16 +38,16 @@ from test_framework.mininode import (CBlockHeader,
CTransaction,
CTxIn,
CTxOut,
- NetworkThread,
- NodeConn,
- NodeConnCB,
+ network_thread_join,
+ network_thread_start,
+ P2PInterface,
msg_block,
msg_headers)
from test_framework.script import (CScript, OP_TRUE)
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import (p2p_port, assert_equal)
+from test_framework.util import assert_equal
-class BaseNode(NodeConnCB):
+class BaseNode(P2PInterface):
def send_header_for_blocks(self, new_blocks):
headers_message = msg_headers()
headers_message.headers = [CBlockHeader(b) for b in new_blocks]
@@ -65,13 +65,13 @@ class AssumeValidTest(BitcoinTestFramework):
# signature so we can pass in the block hash as assumevalid.
self.start_node(0)
- def send_blocks_until_disconnected(self, node):
+ def send_blocks_until_disconnected(self, p2p_conn):
"""Keep sending blocks to the node until we're disconnected."""
for i in range(len(self.blocks)):
- if not node.connection:
+ if p2p_conn.state != "connected":
break
try:
- node.send_message(msg_block(self.blocks[i]))
+ p2p_conn.send_message(msg_block(self.blocks[i]))
except IOError as e:
assert str(e) == 'Not connected, no pushbuf'
break
@@ -97,13 +97,10 @@ class AssumeValidTest(BitcoinTestFramework):
def run_test(self):
# Connect to node0
- node0 = BaseNode()
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0))
- node0.add_connection(connections[0])
+ p2p0 = self.nodes[0].add_p2p_connection(BaseNode())
- NetworkThread().start() # Start up network handling in another thread
- node0.wait_for_verack()
+ network_thread_start()
+ self.nodes[0].p2p.wait_for_verack()
# Build the blockchain
self.tip = int(self.nodes[0].getbestblockhash(), 16)
@@ -163,39 +160,44 @@ class AssumeValidTest(BitcoinTestFramework):
self.block_time += 1
height += 1
+ # We're adding new connections so terminate the network thread
+ self.nodes[0].disconnect_p2ps()
+ network_thread_join()
+
# Start node1 and node2 with assumevalid so they accept a block with a bad signature.
self.start_node(1, extra_args=["-assumevalid=" + hex(block102.sha256)])
- node1 = BaseNode() # connects to node1
- connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1], node1))
- node1.add_connection(connections[1])
- node1.wait_for_verack()
-
self.start_node(2, extra_args=["-assumevalid=" + hex(block102.sha256)])
- node2 = BaseNode() # connects to node2
- connections.append(NodeConn('127.0.0.1', p2p_port(2), self.nodes[2], node2))
- node2.add_connection(connections[2])
- node2.wait_for_verack()
+
+ p2p0 = self.nodes[0].add_p2p_connection(BaseNode())
+ p2p1 = self.nodes[1].add_p2p_connection(BaseNode())
+ p2p2 = self.nodes[2].add_p2p_connection(BaseNode())
+
+ network_thread_start()
+
+ p2p0.wait_for_verack()
+ p2p1.wait_for_verack()
+ p2p2.wait_for_verack()
# send header lists to all three nodes
- node0.send_header_for_blocks(self.blocks[0:2000])
- node0.send_header_for_blocks(self.blocks[2000:])
- node1.send_header_for_blocks(self.blocks[0:2000])
- node1.send_header_for_blocks(self.blocks[2000:])
- node2.send_header_for_blocks(self.blocks[0:200])
+ p2p0.send_header_for_blocks(self.blocks[0:2000])
+ p2p0.send_header_for_blocks(self.blocks[2000:])
+ p2p1.send_header_for_blocks(self.blocks[0:2000])
+ p2p1.send_header_for_blocks(self.blocks[2000:])
+ p2p2.send_header_for_blocks(self.blocks[0:200])
# Send blocks to node0. Block 102 will be rejected.
- self.send_blocks_until_disconnected(node0)
+ self.send_blocks_until_disconnected(p2p0)
self.assert_blockchain_height(self.nodes[0], 101)
# Send all blocks to node1. All blocks will be accepted.
for i in range(2202):
- node1.send_message(msg_block(self.blocks[i]))
+ p2p1.send_message(msg_block(self.blocks[i]))
# Syncing 2200 blocks can take a while on slow systems. Give it plenty of time to sync.
- node1.sync_with_ping(120)
+ p2p1.sync_with_ping(120)
assert_equal(self.nodes[1].getblock(self.nodes[1].getbestblockhash())['height'], 2202)
# Send blocks to node2. Block 102 will be rejected.
- self.send_blocks_until_disconnected(node2)
+ self.send_blocks_until_disconnected(p2p2)
self.assert_blockchain_height(self.nodes[2], 101)
if __name__ == '__main__':
diff --git a/test/functional/bip65-cltv-p2p.py b/test/functional/bip65-cltv-p2p.py
index 2cd6df6e37..f62ae31654 100755
--- a/test/functional/bip65-cltv-p2p.py
+++ b/test/functional/bip65-cltv-p2p.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Copyright (c) 2015-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 BIP65 (CHECKLOCKTIMEVERIFY).
@@ -66,15 +66,12 @@ class BIP65Test(BitcoinTestFramework):
self.setup_clean_chain = True
def run_test(self):
- node0 = NodeConnCB()
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0))
- node0.add_connection(connections[0])
+ self.nodes[0].add_p2p_connection(P2PInterface())
- NetworkThread().start() # Start up network handling in another thread
+ network_thread_start()
# wait_for_verack ensures that the P2P connection is fully up.
- node0.wait_for_verack()
+ self.nodes[0].p2p.wait_for_verack()
self.log.info("Mining %d blocks", CLTV_HEIGHT - 2)
self.coinbase_blocks = self.nodes[0].generate(CLTV_HEIGHT - 2)
@@ -95,7 +92,7 @@ class BIP65Test(BitcoinTestFramework):
block.hashMerkleRoot = block.calc_merkle_root()
block.solve()
- node0.send_and_ping(msg_block(block))
+ self.nodes[0].p2p.send_and_ping(msg_block(block))
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
self.log.info("Test that blocks must now be at least version 4")
@@ -104,15 +101,15 @@ class BIP65Test(BitcoinTestFramework):
block = create_block(tip, create_coinbase(CLTV_HEIGHT), block_time)
block.nVersion = 3
block.solve()
- node0.send_and_ping(msg_block(block))
+ self.nodes[0].p2p.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
- wait_until(lambda: "reject" in node0.last_message.keys(), lock=mininode_lock)
+ wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock)
with mininode_lock:
- assert_equal(node0.last_message["reject"].code, REJECT_OBSOLETE)
- assert_equal(node0.last_message["reject"].reason, b'bad-version(0x00000003)')
- assert_equal(node0.last_message["reject"].data, block.sha256)
- del node0.last_message["reject"]
+ assert_equal(self.nodes[0].p2p.last_message["reject"].code, REJECT_OBSOLETE)
+ assert_equal(self.nodes[0].p2p.last_message["reject"].reason, b'bad-version(0x00000003)')
+ assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256)
+ del self.nodes[0].p2p.last_message["reject"]
self.log.info("Test that invalid-according-to-cltv transactions cannot appear in a block")
block.nVersion = 4
@@ -125,7 +122,7 @@ class BIP65Test(BitcoinTestFramework):
# First we show that this tx is valid except for CLTV by getting it
# accepted to the mempool (which we can achieve with
# -promiscuousmempoolflags).
- node0.send_and_ping(msg_tx(spendtx))
+ self.nodes[0].p2p.send_and_ping(msg_tx(spendtx))
assert spendtx.hash in self.nodes[0].getrawmempool()
# Now we verify that a block with this transaction is invalid.
@@ -133,18 +130,18 @@ class BIP65Test(BitcoinTestFramework):
block.hashMerkleRoot = block.calc_merkle_root()
block.solve()
- node0.send_and_ping(msg_block(block))
+ self.nodes[0].p2p.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
- wait_until(lambda: "reject" in node0.last_message.keys(), lock=mininode_lock)
+ wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock)
with mininode_lock:
- assert node0.last_message["reject"].code in [REJECT_INVALID, REJECT_NONSTANDARD]
- assert_equal(node0.last_message["reject"].data, block.sha256)
- if node0.last_message["reject"].code == REJECT_INVALID:
+ assert self.nodes[0].p2p.last_message["reject"].code in [REJECT_INVALID, REJECT_NONSTANDARD]
+ assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256)
+ if self.nodes[0].p2p.last_message["reject"].code == REJECT_INVALID:
# Generic rejection when a block is invalid
- assert_equal(node0.last_message["reject"].reason, b'block-validation-failed')
+ assert_equal(self.nodes[0].p2p.last_message["reject"].reason, b'block-validation-failed')
else:
- assert b'Negative locktime' in node0.last_message["reject"].reason
+ assert b'Negative locktime' in self.nodes[0].p2p.last_message["reject"].reason
self.log.info("Test that a version 4 block with a valid-according-to-CLTV transaction is accepted")
spendtx = cltv_validate(self.nodes[0], spendtx, CLTV_HEIGHT - 1)
@@ -155,7 +152,7 @@ class BIP65Test(BitcoinTestFramework):
block.hashMerkleRoot = block.calc_merkle_root()
block.solve()
- node0.send_and_ping(msg_block(block))
+ self.nodes[0].p2p.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
diff --git a/test/functional/bip68-112-113-p2p.py b/test/functional/bip68-112-113-p2p.py
index 7e6a4f4408..82aa0ff891 100755
--- a/test/functional/bip68-112-113-p2p.py
+++ b/test/functional/bip68-112-113-p2p.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Copyright (c) 2015-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 activation of the first version bits soft fork.
@@ -45,7 +45,7 @@ bip112tx_special - test negative argument to OP_CSV
from test_framework.test_framework import ComparisonTestFramework
from test_framework.util import *
-from test_framework.mininode import ToHex, CTransaction, NetworkThread
+from test_framework.mininode import ToHex, CTransaction, network_thread_start
from test_framework.blocktools import create_coinbase, create_block
from test_framework.comptool import TestInstance, TestManager
from test_framework.script import *
@@ -95,12 +95,12 @@ class BIP68_112_113Test(ComparisonTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
- self.extra_args = [['-whitelist=127.0.0.1', '-blockversion=4']]
+ self.extra_args = [['-whitelist=127.0.0.1', '-blockversion=4', '-addresstype=legacy']]
def run_test(self):
test = TestManager(self, self.options.tmpdir)
test.add_all_connections(self.nodes)
- NetworkThread().start() # Start up network handling in another thread
+ network_thread_start()
test.run()
def send_generic_input_tx(self, node, coinbases):
diff --git a/test/functional/bip68-sequence.py b/test/functional/bip68-sequence.py
index 5f8f21701f..94b13653b9 100755
--- a/test/functional/bip68-sequence.py
+++ b/test/functional/bip68-sequence.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 BIP68 implementation."""
@@ -362,9 +362,10 @@ class BIP68Test(BitcoinTestFramework):
block.vtx.extend([tx1, tx2, tx3])
block.hashMerkleRoot = block.calc_merkle_root()
block.rehash()
+ add_witness_commitment(block)
block.solve()
- self.nodes[0].submitblock(ToHex(block))
+ self.nodes[0].submitblock(bytes_to_hex_str(block.serialize(True)))
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
def activateCSV(self):
diff --git a/test/functional/bip9-softforks.py b/test/functional/bip9-softforks.py
index 904789301a..ae92e9f07c 100755
--- a/test/functional/bip9-softforks.py
+++ b/test/functional/bip9-softforks.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Copyright (c) 2015-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 BIP 9 soft forks.
@@ -22,7 +22,7 @@ import itertools
from test_framework.test_framework import ComparisonTestFramework
from test_framework.util import *
-from test_framework.mininode import CTransaction, NetworkThread
+from test_framework.mininode import CTransaction, network_thread_start
from test_framework.blocktools import create_coinbase, create_block
from test_framework.comptool import TestInstance, TestManager
from test_framework.script import CScript, OP_1NEGATE, OP_CHECKSEQUENCEVERIFY, OP_DROP
@@ -36,7 +36,7 @@ class BIP9SoftForksTest(ComparisonTestFramework):
def run_test(self):
self.test = TestManager(self, self.options.tmpdir)
self.test.add_all_connections(self.nodes)
- NetworkThread().start() # Start up network handling in another thread
+ network_thread_start()
self.test.run()
def create_transaction(self, node, coinbase, to_address, amount):
@@ -245,8 +245,8 @@ class BIP9SoftForksTest(ComparisonTestFramework):
self.setup_chain()
self.setup_network()
self.test.add_all_connections(self.nodes)
- NetworkThread().start()
- self.test.test_nodes[0].wait_for_verack()
+ network_thread_start()
+ self.test.p2p_connections[0].wait_for_verack()
def get_tests(self):
for test in itertools.chain(
diff --git a/test/functional/bipdersig-p2p.py b/test/functional/bipdersig-p2p.py
index c620d3e155..3414571678 100755
--- a/test/functional/bipdersig-p2p.py
+++ b/test/functional/bipdersig-p2p.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Copyright (c) 2015-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 BIP66 (DER SIG).
@@ -54,14 +54,12 @@ class BIP66Test(BitcoinTestFramework):
self.setup_clean_chain = True
def run_test(self):
- node0 = NodeConnCB()
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0))
- node0.add_connection(connections[0])
- NetworkThread().start() # Start up network handling in another thread
+ self.nodes[0].add_p2p_connection(P2PInterface())
+
+ network_thread_start()
# wait_for_verack ensures that the P2P connection is fully up.
- node0.wait_for_verack()
+ self.nodes[0].p2p.wait_for_verack()
self.log.info("Mining %d blocks", DERSIG_HEIGHT - 2)
self.coinbase_blocks = self.nodes[0].generate(DERSIG_HEIGHT - 2)
@@ -83,7 +81,7 @@ class BIP66Test(BitcoinTestFramework):
block.rehash()
block.solve()
- node0.send_and_ping(msg_block(block))
+ self.nodes[0].p2p.send_and_ping(msg_block(block))
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
self.log.info("Test that blocks must now be at least version 3")
@@ -93,15 +91,15 @@ class BIP66Test(BitcoinTestFramework):
block.nVersion = 2
block.rehash()
block.solve()
- node0.send_and_ping(msg_block(block))
+ self.nodes[0].p2p.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
- wait_until(lambda: "reject" in node0.last_message.keys(), lock=mininode_lock)
+ wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock)
with mininode_lock:
- assert_equal(node0.last_message["reject"].code, REJECT_OBSOLETE)
- assert_equal(node0.last_message["reject"].reason, b'bad-version(0x00000002)')
- assert_equal(node0.last_message["reject"].data, block.sha256)
- del node0.last_message["reject"]
+ assert_equal(self.nodes[0].p2p.last_message["reject"].code, REJECT_OBSOLETE)
+ assert_equal(self.nodes[0].p2p.last_message["reject"].reason, b'bad-version(0x00000002)')
+ assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256)
+ del self.nodes[0].p2p.last_message["reject"]
self.log.info("Test that transactions with non-DER signatures cannot appear in a block")
block.nVersion = 3
@@ -114,7 +112,7 @@ class BIP66Test(BitcoinTestFramework):
# First we show that this tx is valid except for DERSIG by getting it
# accepted to the mempool (which we can achieve with
# -promiscuousmempoolflags).
- node0.send_and_ping(msg_tx(spendtx))
+ self.nodes[0].p2p.send_and_ping(msg_tx(spendtx))
assert spendtx.hash in self.nodes[0].getrawmempool()
# Now we verify that a block with this transaction is invalid.
@@ -123,23 +121,23 @@ class BIP66Test(BitcoinTestFramework):
block.rehash()
block.solve()
- node0.send_and_ping(msg_block(block))
+ self.nodes[0].p2p.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
- wait_until(lambda: "reject" in node0.last_message.keys(), lock=mininode_lock)
+ wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock)
with mininode_lock:
# We can receive different reject messages depending on whether
# bitcoind is running with multiple script check threads. If script
# check threads are not in use, then transaction script validation
# happens sequentially, and bitcoind produces more specific reject
# reasons.
- assert node0.last_message["reject"].code in [REJECT_INVALID, REJECT_NONSTANDARD]
- assert_equal(node0.last_message["reject"].data, block.sha256)
- if node0.last_message["reject"].code == REJECT_INVALID:
+ assert self.nodes[0].p2p.last_message["reject"].code in [REJECT_INVALID, REJECT_NONSTANDARD]
+ assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256)
+ if self.nodes[0].p2p.last_message["reject"].code == REJECT_INVALID:
# Generic rejection when a block is invalid
- assert_equal(node0.last_message["reject"].reason, b'block-validation-failed')
+ assert_equal(self.nodes[0].p2p.last_message["reject"].reason, b'block-validation-failed')
else:
- assert b'Non-canonical DER signature' in node0.last_message["reject"].reason
+ assert b'Non-canonical DER signature' in self.nodes[0].p2p.last_message["reject"].reason
self.log.info("Test that a version 3 block with a DERSIG-compliant transaction is accepted")
block.vtx[1] = create_transaction(self.nodes[0],
@@ -148,7 +146,7 @@ class BIP66Test(BitcoinTestFramework):
block.rehash()
block.solve()
- node0.send_and_ping(msg_block(block))
+ self.nodes[0].p2p.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
if __name__ == '__main__':
diff --git a/test/functional/bitcoin_cli.py b/test/functional/bitcoin_cli.py
index 996cbb8a12..d1cd3b3620 100755
--- a/test/functional/bitcoin_cli.py
+++ b/test/functional/bitcoin_cli.py
@@ -35,8 +35,11 @@ class TestBitcoinCli(BitcoinTestFramework):
assert_equal(["foo", "bar"], self.nodes[0].cli('-rpcuser=%s' % user, '-stdin', '-stdinrpcpass', input=password + "\nfoo\nbar").echo())
assert_raises_process_error(1, "incorrect rpcuser or rpcpassword", self.nodes[0].cli('-rpcuser=%s' % user, '-stdin', '-stdinrpcpass', input="foo").echo)
+ self.log.info("Make sure that -getinfo with arguments fails")
+ assert_raises_process_error(1, "-getinfo takes no arguments", self.nodes[0].cli('-getinfo').help)
+
self.log.info("Compare responses from `bitcoin-cli -getinfo` and the RPCs data is retrieved from.")
- cli_get_info = self.nodes[0].cli('-getinfo').help()
+ cli_get_info = self.nodes[0].cli().send_cli('-getinfo')
wallet_info = self.nodes[0].getwalletinfo()
network_info = self.nodes[0].getnetworkinfo()
blockchain_info = self.nodes[0].getblockchaininfo()
diff --git a/test/functional/blockchain.py b/test/functional/blockchain.py
index 4576cb036a..11acff4be1 100755
--- a/test/functional/blockchain.py
+++ b/test/functional/blockchain.py
@@ -1,10 +1,11 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 RPCs related to blockchainstate.
Test the following RPCs:
+ - getblockchaininfo
- gettxoutsetinfo
- getdifficulty
- getbestblockhash
@@ -58,6 +59,7 @@ class BlockchainTest(BitcoinTestFramework):
'chainwork',
'difficulty',
'headers',
+ 'initialblockdownload',
'mediantime',
'pruned',
'size_on_disk',
diff --git a/test/functional/bumpfee.py b/test/functional/bumpfee.py
index 44fa7b0feb..1e5620736b 100755
--- a/test/functional/bumpfee.py
+++ b/test/functional/bumpfee.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016 The Bitcoin Core developers
+# Copyright (c) 2016-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 the bumpfee RPC.
@@ -194,7 +194,7 @@ def test_settxfee(rbf_node, dest_address):
requested_feerate = Decimal("0.00025000")
rbf_node.settxfee(requested_feerate)
bumped_tx = rbf_node.bumpfee(rbfid)
- actual_feerate = bumped_tx["fee"] * 1000 / rbf_node.getrawtransaction(bumped_tx["txid"], True)["size"]
+ actual_feerate = bumped_tx["fee"] * 1000 / rbf_node.getrawtransaction(bumped_tx["txid"], True)["vsize"]
# Assert that the difference between the requested feerate and the actual
# feerate of the bumped transaction is small.
assert_greater_than(Decimal("0.00001000"), abs(requested_feerate - actual_feerate))
@@ -290,6 +290,7 @@ def submit_block_with_tx(node, tx):
block.vtx.append(ctx)
block.rehash()
block.hashMerkleRoot = block.calc_merkle_root()
+ blocktools.add_witness_commitment(block)
block.solve()
node.submitblock(bytes_to_hex_str(block.serialize(True)))
return block
diff --git a/test/functional/conf_args.py b/test/functional/conf_args.py
new file mode 100755
index 0000000000..61abba8082
--- /dev/null
+++ b/test/functional/conf_args.py
@@ -0,0 +1,49 @@
+#!/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 various command line arguments and configuration file parameters."""
+
+import os
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import get_datadir_path
+
+class ConfArgsTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 1
+
+ def run_test(self):
+ self.stop_node(0)
+ # Remove the -datadir argument so it doesn't override the config file
+ self.nodes[0].args = [arg for arg in self.nodes[0].args if not arg.startswith("-datadir")]
+
+ default_data_dir = get_datadir_path(self.options.tmpdir, 0)
+ new_data_dir = os.path.join(default_data_dir, 'newdatadir')
+ new_data_dir_2 = os.path.join(default_data_dir, 'newdatadir2')
+
+ # Check that using -datadir argument on non-existent directory fails
+ self.nodes[0].datadir = new_data_dir
+ self.assert_start_raises_init_error(0, ['-datadir='+new_data_dir], 'Error: Specified data directory "' + new_data_dir + '" does not exist.')
+
+ # Check that using non-existent datadir in conf file fails
+ conf_file = os.path.join(default_data_dir, "bitcoin.conf")
+ with open(conf_file, 'a', encoding='utf8') as f:
+ f.write("datadir=" + new_data_dir + "\n")
+ self.assert_start_raises_init_error(0, ['-conf='+conf_file], 'Error reading configuration file: specified data directory "' + new_data_dir + '" does not exist.')
+
+ # Create the directory and ensure the config file now works
+ os.mkdir(new_data_dir)
+ self.start_node(0, ['-conf='+conf_file, '-wallet=w1'])
+ self.stop_node(0)
+ assert os.path.isfile(os.path.join(new_data_dir, 'regtest', 'wallets', 'w1'))
+
+ # Ensure command line argument overrides datadir in conf
+ os.mkdir(new_data_dir_2)
+ self.nodes[0].datadir = new_data_dir_2
+ self.start_node(0, ['-datadir='+new_data_dir_2, '-conf='+conf_file, '-wallet=w2'])
+ assert os.path.isfile(os.path.join(new_data_dir_2, 'regtest', 'wallets', 'w2'))
+
+if __name__ == '__main__':
+ ConfArgsTest().main()
diff --git a/test/functional/create_cache.py b/test/functional/create_cache.py
index 7d4d1a529b..9665c50a92 100755
--- a/test/functional/create_cache.py
+++ b/test/functional/create_cache.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016 The Bitcoin Core developers
+# Copyright (c) 2016-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.
"""Create a blockchain cache.
@@ -16,6 +16,7 @@ class CreateCache(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 0
+ self.supports_cli = True
def setup_network(self):
pass
diff --git a/test/functional/decodescript.py b/test/functional/decodescript.py
index 6611da8831..1ffc570437 100755
--- a/test/functional/decodescript.py
+++ b/test/functional/decodescript.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Copyright (c) 2015-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 decoding scripts via decodescript RPC command."""
diff --git a/test/functional/disablewallet.py b/test/functional/disablewallet.py
index c75ef9b9f1..b0627d88ac 100755
--- a/test/functional/disablewallet.py
+++ b/test/functional/disablewallet.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Copyright (c) 2015-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 a node with the -disablewallet option.
diff --git a/test/functional/disconnect_ban.py b/test/functional/disconnect_ban.py
index 59655d37fb..c6067befb2 100755
--- a/test/functional/disconnect_ban.py
+++ b/test/functional/disconnect_ban.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 node disconnect and ban behavior"""
diff --git a/test/functional/example_test.py b/test/functional/example_test.py
index 87d73ad14a..12be685ecf 100755
--- a/test/functional/example_test.py
+++ b/test/functional/example_test.py
@@ -17,30 +17,29 @@ from collections import defaultdict
from test_framework.blocktools import (create_block, create_coinbase)
from test_framework.mininode import (
CInv,
- NetworkThread,
- NodeConn,
- NodeConnCB,
+ P2PInterface,
mininode_lock,
msg_block,
msg_getdata,
+ network_thread_join,
+ network_thread_start,
)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
connect_nodes,
- p2p_port,
wait_until,
)
-# NodeConnCB is a class containing callbacks to be executed when a P2P
-# message is received from the node-under-test. Subclass NodeConnCB and
+# P2PInterface is a class containing callbacks to be executed when a P2P
+# message is received from the node-under-test. Subclass P2PInterface and
# override the on_*() methods if you need custom behaviour.
-class BaseNode(NodeConnCB):
+class BaseNode(P2PInterface):
def __init__(self):
- """Initialize the NodeConnCB
+ """Initialize the P2PInterface
Used to inialize custom properties for the Node that aren't
- included by default in the base class. Be aware that the NodeConnCB
+ included by default in the base class. Be aware that the P2PInterface
base class already stores a counter for each P2P message type and the
last received message of each type, which should be sufficient for the
needs of most tests.
@@ -51,14 +50,14 @@ class BaseNode(NodeConnCB):
# Stores a dictionary of all blocks received
self.block_receive_map = defaultdict(int)
- def on_block(self, conn, message):
+ def on_block(self, message):
"""Override the standard on_block callback
Store the hash of a received block in the dictionary."""
message.block.calc_sha256()
self.block_receive_map[message.block.sha256] += 1
- def on_inv(self, conn, message):
+ def on_inv(self, message):
"""Override the standard on_inv callback"""
pass
@@ -133,17 +132,14 @@ class ExampleTest(BitcoinTestFramework):
def run_test(self):
"""Main test logic"""
- # Create a P2P connection to one of the nodes
- node0 = BaseNode()
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0))
- node0.add_connection(connections[0])
+ # Create P2P connections to two of the nodes
+ self.nodes[0].add_p2p_connection(BaseNode())
# Start up network handling in another thread. This needs to be called
# after the P2P connections have been created.
- NetworkThread().start()
+ network_thread_start()
# wait_for_verack ensures that the P2P connection is fully up.
- node0.wait_for_verack()
+ self.nodes[0].p2p.wait_for_verack()
# Generating a block on one of the nodes will get us out of IBD
blocks = [int(self.nodes[0].generate(nblocks=1)[0], 16)]
@@ -179,8 +175,8 @@ class ExampleTest(BitcoinTestFramework):
block = create_block(self.tip, create_coinbase(height), self.block_time)
block.solve()
block_message = msg_block(block)
- # Send message is used to send a P2P message to the node over our NodeConn connection
- node0.send_message(block_message)
+ # Send message is used to send a P2P message to the node over our P2PInterface
+ self.nodes[0].p2p.send_message(block_message)
self.tip = block.sha256
blocks.append(self.tip)
self.block_time += 1
@@ -193,28 +189,33 @@ class ExampleTest(BitcoinTestFramework):
connect_nodes(self.nodes[1], 2)
self.log.info("Add P2P connection to node2")
- node2 = BaseNode()
- connections.append(NodeConn('127.0.0.1', p2p_port(2), self.nodes[2], node2))
- node2.add_connection(connections[1])
- node2.wait_for_verack()
+ # We can't add additional P2P connections once the network thread has started. Disconnect the connection
+ # to node0, wait for the network thread to terminate, then connect to node2. This is specific to
+ # the current implementation of the network thread and may be improved in future.
+ self.nodes[0].disconnect_p2ps()
+ network_thread_join()
+
+ self.nodes[2].add_p2p_connection(BaseNode())
+ network_thread_start()
+ self.nodes[2].p2p.wait_for_verack()
self.log.info("Wait for node2 reach current tip. Test that it has propagated all the blocks to us")
getdata_request = msg_getdata()
for block in blocks:
getdata_request.inv.append(CInv(2, block))
- node2.send_message(getdata_request)
+ self.nodes[2].p2p.send_message(getdata_request)
# wait_until() will loop until a predicate condition is met. Use it to test properties of the
- # NodeConnCB objects.
- wait_until(lambda: sorted(blocks) == sorted(list(node2.block_receive_map.keys())), timeout=5, lock=mininode_lock)
+ # P2PInterface objects.
+ wait_until(lambda: sorted(blocks) == sorted(list(self.nodes[2].p2p.block_receive_map.keys())), timeout=5, lock=mininode_lock)
self.log.info("Check that each block was received only once")
- # The network thread uses a global lock on data access to the NodeConn objects when sending and receiving
- # messages. The test thread should acquire the global lock before accessing any NodeConn data to avoid locking
+ # The network thread uses a global lock on data access to the P2PConnection objects when sending and receiving
+ # messages. The test thread should acquire the global lock before accessing any P2PConnection data to avoid locking
# and synchronization issues. Note wait_until() acquires this global lock when testing the predicate.
with mininode_lock:
- for block in node2.block_receive_map.values():
+ for block in self.nodes[2].p2p.block_receive_map.values():
assert_equal(block, 1)
if __name__ == '__main__':
diff --git a/test/functional/feature_logging.py b/test/functional/feature_logging.py
new file mode 100755
index 0000000000..da4e7b0398
--- /dev/null
+++ b/test/functional/feature_logging.py
@@ -0,0 +1,59 @@
+#!/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 debug logging."""
+
+import os
+
+from test_framework.test_framework import BitcoinTestFramework
+
+class LoggingTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+ self.setup_clean_chain = True
+
+ def run_test(self):
+ # test default log file name
+ assert os.path.isfile(os.path.join(self.nodes[0].datadir, "regtest", "debug.log"))
+
+ # test alternative log file name in datadir
+ self.restart_node(0, ["-debuglogfile=foo.log"])
+ assert os.path.isfile(os.path.join(self.nodes[0].datadir, "regtest", "foo.log"))
+
+ # test alternative log file name outside datadir
+ tempname = os.path.join(self.options.tmpdir, "foo.log")
+ self.restart_node(0, ["-debuglogfile=%s" % tempname])
+ assert os.path.isfile(tempname)
+
+ # check that invalid log (relative) will cause error
+ invdir = os.path.join(self.nodes[0].datadir, "regtest", "foo")
+ invalidname = os.path.join("foo", "foo.log")
+ self.stop_node(0)
+ self.assert_start_raises_init_error(0, ["-debuglogfile=%s" % (invalidname)],
+ "Error: Could not open debug log file")
+ assert not os.path.isfile(os.path.join(invdir, "foo.log"))
+
+ # check that invalid log (relative) works after path exists
+ self.stop_node(0)
+ os.mkdir(invdir)
+ self.start_node(0, ["-debuglogfile=%s" % (invalidname)])
+ assert os.path.isfile(os.path.join(invdir, "foo.log"))
+
+ # check that invalid log (absolute) will cause error
+ self.stop_node(0)
+ invdir = os.path.join(self.options.tmpdir, "foo")
+ invalidname = os.path.join(invdir, "foo.log")
+ self.assert_start_raises_init_error(0, ["-debuglogfile=%s" % invalidname],
+ "Error: Could not open debug log file")
+ assert not os.path.isfile(os.path.join(invdir, "foo.log"))
+
+ # check that invalid log (absolute) works after path exists
+ self.stop_node(0)
+ os.mkdir(invdir)
+ self.start_node(0, ["-debuglogfile=%s" % (invalidname)])
+ assert os.path.isfile(os.path.join(invdir, "foo.log"))
+
+
+if __name__ == '__main__':
+ LoggingTest().main()
diff --git a/test/functional/forknotify.py b/test/functional/forknotify.py
deleted file mode 100755
index afcad1f9cc..0000000000
--- a/test/functional/forknotify.py
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/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 the -alertnotify option."""
-import os
-import time
-
-from test_framework.test_framework import BitcoinTestFramework
-
-class ForkNotifyTest(BitcoinTestFramework):
- def set_test_params(self):
- self.num_nodes = 2
-
- def setup_network(self):
- self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt")
- with open(self.alert_filename, 'w', encoding='utf8'):
- pass # Just open then close to create zero-length file
- self.extra_args = [["-blockversion=2", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""],
- ["-blockversion=211"]]
- super().setup_network()
-
- def run_test(self):
- # Mine 51 up-version blocks
- self.nodes[1].generate(51)
- self.sync_all()
- # -alertnotify should trigger on the 51'st,
- # but mine and sync another to give
- # -alertnotify time to write
- self.nodes[1].generate(1)
- self.sync_all()
-
- # Give bitcoind 10 seconds to write the alert notification
- timeout = 10.0
- while timeout > 0:
- if os.path.exists(self.alert_filename) and os.path.getsize(self.alert_filename):
- break
- time.sleep(0.1)
- timeout -= 0.1
- else:
- assert False, "-alertnotify did not warn of up-version blocks"
-
- with open(self.alert_filename, 'r', encoding='utf8') as f:
- alert_text = f.read()
-
- # Mine more up-version blocks, should not get more alerts:
- self.nodes[1].generate(1)
- self.sync_all()
- self.nodes[1].generate(1)
- self.sync_all()
-
- with open(self.alert_filename, 'r', encoding='utf8') as f:
- alert_text2 = f.read()
-
- if alert_text != alert_text2:
- raise AssertionError("-alertnotify excessive warning of up-version blocks")
-
-if __name__ == '__main__':
- ForkNotifyTest().main()
diff --git a/test/functional/fundrawtransaction.py b/test/functional/fundrawtransaction.py
index d446f56d0e..4739e73c39 100755
--- a/test/functional/fundrawtransaction.py
+++ b/test/functional/fundrawtransaction.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 the fundrawtransaction RPC."""
diff --git a/test/functional/getblocktemplate_longpoll.py b/test/functional/getblocktemplate_longpoll.py
index 89768bd2fb..252ff4dbff 100755
--- a/test/functional/getblocktemplate_longpoll.py
+++ b/test/functional/getblocktemplate_longpoll.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 longpolling with getblocktemplate."""
diff --git a/test/functional/getchaintips.py b/test/functional/getchaintips.py
index 21b67bfc64..277930bb1a 100755
--- a/test/functional/getchaintips.py
+++ b/test/functional/getchaintips.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 the getchaintips RPC.
diff --git a/test/functional/httpbasics.py b/test/functional/httpbasics.py
index c7682cb49d..cd6d744545 100755
--- a/test/functional/httpbasics.py
+++ b/test/functional/httpbasics.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 the RPC HTTP basics."""
diff --git a/test/functional/import-rescan.py b/test/functional/import-rescan.py
index 6807fa6696..d193a99d5b 100755
--- a/test/functional/import-rescan.py
+++ b/test/functional/import-rescan.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 wallet import RPCs.
@@ -119,7 +119,7 @@ class ImportRescanTest(BitcoinTestFramework):
self.num_nodes = 2 + len(IMPORT_NODES)
def setup_network(self):
- extra_args = [[] for _ in range(self.num_nodes)]
+ extra_args = [["-addresstype=legacy"] for _ in range(self.num_nodes)]
for i, import_node in enumerate(IMPORT_NODES, 2):
if import_node.prune:
extra_args[i] += ["-prune=1"]
diff --git a/test/functional/importmulti.py b/test/functional/importmulti.py
index c1a42870ec..ab4ca48e48 100755
--- a/test/functional/importmulti.py
+++ b/test/functional/importmulti.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 the importmulti RPC."""
@@ -9,6 +9,7 @@ from test_framework.util import *
class ImportMultiTest (BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
+ self.extra_args = [["-addresstype=legacy"], ["-addresstype=legacy"]]
self.setup_clean_chain = True
def setup_network(self):
@@ -160,6 +161,18 @@ class ImportMultiTest (BitcoinTestFramework):
assert_equal(address_assert['ismine'], True)
assert_equal(address_assert['timestamp'], timestamp)
+ self.log.info("Should not import an address with private key if is already imported")
+ result = self.nodes[1].importmulti([{
+ "scriptPubKey": {
+ "address": address['address']
+ },
+ "timestamp": "now",
+ "keys": [ self.nodes[0].dumpprivkey(address['address']) ]
+ }])
+ assert_equal(result[0]['success'], False)
+ assert_equal(result[0]['error']['code'], -4)
+ assert_equal(result[0]['error']['message'], 'The wallet already contains the private key for this address or script')
+
# Address + Private key + watchonly
self.log.info("Should not import an address with private key and with watchonly")
address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
diff --git a/test/functional/importprunedfunds.py b/test/functional/importprunedfunds.py
index 068052409a..6b2919b5ae 100755
--- a/test/functional/importprunedfunds.py
+++ b/test/functional/importprunedfunds.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 the importprunedfunds and removeprunedfunds RPCs."""
diff --git a/test/functional/invalidateblock.py b/test/functional/invalidateblock.py
index dd3daf1e07..b037c2431d 100755
--- a/test/functional/invalidateblock.py
+++ b/test/functional/invalidateblock.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 the invalidateblock RPC."""
diff --git a/test/functional/invalidblockrequest.py b/test/functional/invalidblockrequest.py
index 9f44b44927..edcade63c1 100755
--- a/test/functional/invalidblockrequest.py
+++ b/test/functional/invalidblockrequest.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Copyright (c) 2015-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 node responses to invalid blocks.
@@ -15,6 +15,7 @@ from test_framework.test_framework import ComparisonTestFramework
from test_framework.util import *
from test_framework.comptool import TestManager, TestInstance, RejectResult
from test_framework.blocktools import *
+from test_framework.mininode import network_thread_start
import copy
import time
@@ -32,7 +33,7 @@ class InvalidBlockRequestTest(ComparisonTestFramework):
test.add_all_connections(self.nodes)
self.tip = None
self.block_time = None
- NetworkThread().start() # Start up network handling in another thread
+ network_thread_start()
test.run()
def get_tests(self):
diff --git a/test/functional/invalidtxrequest.py b/test/functional/invalidtxrequest.py
index a22bd8f8cd..9c1100e070 100755
--- a/test/functional/invalidtxrequest.py
+++ b/test/functional/invalidtxrequest.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Copyright (c) 2015-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 node responses to invalid transactions.
@@ -28,7 +28,7 @@ class InvalidTxRequestTest(ComparisonTestFramework):
test.add_all_connections(self.nodes)
self.tip = None
self.block_time = None
- NetworkThread().start() # Start up network handling in another thread
+ network_thread_start()
test.run()
def get_tests(self):
diff --git a/test/functional/keypool-topup.py b/test/functional/keypool-topup.py
index 160a0f7ae5..e7af3c3987 100755
--- a/test/functional/keypool-topup.py
+++ b/test/functional/keypool-topup.py
@@ -33,7 +33,7 @@ class KeypoolRestoreTest(BitcoinTestFramework):
self.stop_node(1)
- shutil.copyfile(self.tmpdir + "/node1/regtest/wallet.dat", self.tmpdir + "/wallet.bak")
+ shutil.copyfile(self.tmpdir + "/node1/regtest/wallets/wallet.dat", self.tmpdir + "/wallet.bak")
self.start_node(1, self.extra_args[1])
connect_nodes_bi(self.nodes, 0, 1)
@@ -56,7 +56,7 @@ class KeypoolRestoreTest(BitcoinTestFramework):
self.stop_node(1)
- shutil.copyfile(self.tmpdir + "/wallet.bak", self.tmpdir + "/node1/regtest/wallet.dat")
+ shutil.copyfile(self.tmpdir + "/wallet.bak", self.tmpdir + "/node1/regtest/wallets/wallet.dat")
self.log.info("Verify keypool is restored and balance is correct")
diff --git a/test/functional/keypool.py b/test/functional/keypool.py
index f2701c36bd..45a5eed8ec 100755
--- a/test/functional/keypool.py
+++ b/test/functional/keypool.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 the wallet keypool and interaction with wallet encryption/locking."""
diff --git a/test/functional/listsinceblock.py b/test/functional/listsinceblock.py
index 6f428388ec..67e7744bf8 100755
--- a/test/functional/listsinceblock.py
+++ b/test/functional/listsinceblock.py
@@ -5,7 +5,7 @@
"""Test the listsincelast RPC."""
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal
+from test_framework.util import assert_equal, assert_array_result, assert_raises_rpc_error
class ListSinceBlockTest (BitcoinTestFramework):
def set_test_params(self):
@@ -16,10 +16,43 @@ class ListSinceBlockTest (BitcoinTestFramework):
self.nodes[2].generate(101)
self.sync_all()
+ self.test_no_blockhash()
+ self.test_invalid_blockhash()
self.test_reorg()
self.test_double_spend()
self.test_double_send()
+ def test_no_blockhash(self):
+ txid = self.nodes[2].sendtoaddress(self.nodes[0].getnewaddress(), 1)
+ blockhash, = self.nodes[2].generate(1)
+ self.sync_all()
+
+ txs = self.nodes[0].listtransactions()
+ assert_array_result(txs, {"txid": txid}, {
+ "category": "receive",
+ "amount": 1,
+ "blockhash": blockhash,
+ "confirmations": 1,
+ })
+ assert_equal(
+ self.nodes[0].listsinceblock(),
+ {"lastblock": blockhash,
+ "removed": [],
+ "transactions": txs})
+ assert_equal(
+ self.nodes[0].listsinceblock(""),
+ {"lastblock": blockhash,
+ "removed": [],
+ "transactions": txs})
+
+ def test_invalid_blockhash(self):
+ assert_raises_rpc_error(-5, "Block not found", self.nodes[0].listsinceblock,
+ "42759cde25462784395a337460bde75f58e73d3f08bd31fdc3507cbac856a2c4")
+ assert_raises_rpc_error(-5, "Block not found", self.nodes[0].listsinceblock,
+ "0000000000000000000000000000000000000000000000000000000000000000")
+ assert_raises_rpc_error(-5, "Block not found", self.nodes[0].listsinceblock,
+ "invalid-hex")
+
def test_reorg(self):
'''
`listsinceblock` did not behave correctly when handed a block that was
diff --git a/test/functional/listtransactions.py b/test/functional/listtransactions.py
index e4522cc3b5..cbed065928 100755
--- a/test/functional/listtransactions.py
+++ b/test/functional/listtransactions.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 the listtransactions API."""
diff --git a/test/functional/maxuploadtarget.py b/test/functional/maxuploadtarget.py
index 1f402798e7..45336ee801 100755
--- a/test/functional/maxuploadtarget.py
+++ b/test/functional/maxuploadtarget.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Copyright (c) 2015-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 behavior of -maxuploadtarget.
@@ -17,15 +17,15 @@ from test_framework.mininode import *
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
-class TestNode(NodeConnCB):
+class TestNode(P2PInterface):
def __init__(self):
super().__init__()
self.block_receive_map = defaultdict(int)
- def on_inv(self, conn, message):
+ def on_inv(self, message):
pass
- def on_block(self, conn, message):
+ def on_block(self, message):
message.block.calc_sha256()
self.block_receive_map[message.block.sha256] += 1
@@ -49,19 +49,17 @@ class MaxUploadTest(BitcoinTestFramework):
# Generate some old blocks
self.nodes[0].generate(130)
- # test_nodes[0] will only request old blocks
- # test_nodes[1] will only request new blocks
- # test_nodes[2] will test resetting the counters
- test_nodes = []
- connections = []
+ # p2p_conns[0] will only request old blocks
+ # p2p_conns[1] will only request new blocks
+ # p2p_conns[2] will test resetting the counters
+ p2p_conns = []
- for i in range(3):
- test_nodes.append(TestNode())
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_nodes[i]))
- test_nodes[i].add_connection(connections[i])
+ for _ in range(3):
+ p2p_conns.append(self.nodes[0].add_p2p_connection(TestNode()))
- NetworkThread().start() # Start up network handling in another thread
- [x.wait_for_verack() for x in test_nodes]
+ network_thread_start()
+ for p2pc in p2p_conns:
+ p2pc.wait_for_verack()
# Test logic begins here
@@ -83,7 +81,7 @@ class MaxUploadTest(BitcoinTestFramework):
big_new_block = self.nodes[0].getbestblockhash()
big_new_block = int(big_new_block, 16)
- # test_nodes[0] will test what happens if we just keep requesting the
+ # p2p_conns[0] will test what happens if we just keep requesting the
# the same big old block too many times (expect: disconnect)
getdata_request = msg_getdata()
@@ -97,34 +95,34 @@ class MaxUploadTest(BitcoinTestFramework):
# 576MB will be reserved for relaying new blocks, so expect this to
# succeed for ~235 tries.
for i in range(success_count):
- test_nodes[0].send_message(getdata_request)
- test_nodes[0].sync_with_ping()
- assert_equal(test_nodes[0].block_receive_map[big_old_block], i+1)
+ p2p_conns[0].send_message(getdata_request)
+ p2p_conns[0].sync_with_ping()
+ assert_equal(p2p_conns[0].block_receive_map[big_old_block], i+1)
assert_equal(len(self.nodes[0].getpeerinfo()), 3)
# At most a couple more tries should succeed (depending on how long
# the test has been running so far).
for i in range(3):
- test_nodes[0].send_message(getdata_request)
- test_nodes[0].wait_for_disconnect()
+ p2p_conns[0].send_message(getdata_request)
+ p2p_conns[0].wait_for_disconnect()
assert_equal(len(self.nodes[0].getpeerinfo()), 2)
self.log.info("Peer 0 disconnected after downloading old block too many times")
- # Requesting the current block on test_nodes[1] should succeed indefinitely,
+ # Requesting the current block on p2p_conns[1] should succeed indefinitely,
# even when over the max upload target.
# We'll try 800 times
getdata_request.inv = [CInv(2, big_new_block)]
for i in range(800):
- test_nodes[1].send_message(getdata_request)
- test_nodes[1].sync_with_ping()
- assert_equal(test_nodes[1].block_receive_map[big_new_block], i+1)
+ p2p_conns[1].send_message(getdata_request)
+ p2p_conns[1].sync_with_ping()
+ assert_equal(p2p_conns[1].block_receive_map[big_new_block], i+1)
self.log.info("Peer 1 able to repeatedly download new block")
- # But if test_nodes[1] tries for an old block, it gets disconnected too.
+ # But if p2p_conns[1] tries for an old block, it gets disconnected too.
getdata_request.inv = [CInv(2, big_old_block)]
- test_nodes[1].send_message(getdata_request)
- test_nodes[1].wait_for_disconnect()
+ p2p_conns[1].send_message(getdata_request)
+ p2p_conns[1].wait_for_disconnect()
assert_equal(len(self.nodes[0].getpeerinfo()), 1)
self.log.info("Peer 1 disconnected after trying to download old block")
@@ -132,39 +130,37 @@ class MaxUploadTest(BitcoinTestFramework):
self.log.info("Advancing system time on node to clear counters...")
# If we advance the time by 24 hours, then the counters should reset,
- # and test_nodes[2] should be able to retrieve the old block.
+ # and p2p_conns[2] should be able to retrieve the old block.
self.nodes[0].setmocktime(int(time.time()))
- test_nodes[2].sync_with_ping()
- test_nodes[2].send_message(getdata_request)
- test_nodes[2].sync_with_ping()
- assert_equal(test_nodes[2].block_receive_map[big_old_block], 1)
+ p2p_conns[2].sync_with_ping()
+ p2p_conns[2].send_message(getdata_request)
+ p2p_conns[2].sync_with_ping()
+ assert_equal(p2p_conns[2].block_receive_map[big_old_block], 1)
self.log.info("Peer 2 able to download old block")
- [c.disconnect_node() for c in connections]
+ self.nodes[0].disconnect_p2ps()
#stop and start node 0 with 1MB maxuploadtarget, whitelist 127.0.0.1
self.log.info("Restarting nodes with -whitelist=127.0.0.1")
self.stop_node(0)
self.start_node(0, ["-whitelist=127.0.0.1", "-maxuploadtarget=1", "-blockmaxsize=999000"])
- #recreate/reconnect a test node
- test_nodes = [TestNode()]
- connections = [NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_nodes[0])]
- test_nodes[0].add_connection(connections[0])
+ # Reconnect to self.nodes[0]
+ self.nodes[0].add_p2p_connection(TestNode())
- NetworkThread().start() # Start up network handling in another thread
- test_nodes[0].wait_for_verack()
+ network_thread_start()
+ self.nodes[0].p2p.wait_for_verack()
#retrieve 20 blocks which should be enough to break the 1MB limit
getdata_request.inv = [CInv(2, big_new_block)]
for i in range(20):
- test_nodes[0].send_message(getdata_request)
- test_nodes[0].sync_with_ping()
- assert_equal(test_nodes[0].block_receive_map[big_new_block], i+1)
+ self.nodes[0].p2p.send_message(getdata_request)
+ self.nodes[0].p2p.sync_with_ping()
+ assert_equal(self.nodes[0].p2p.block_receive_map[big_new_block], i+1)
getdata_request.inv = [CInv(2, big_old_block)]
- test_nodes[0].send_and_ping(getdata_request)
+ self.nodes[0].p2p.send_and_ping(getdata_request)
assert_equal(len(self.nodes[0].getpeerinfo()), 1) #node is still connected because of the whitelist
self.log.info("Peer still connected after trying to download old block (whitelisted)")
diff --git a/test/functional/mempool_limit.py b/test/functional/mempool_limit.py
index e24dc5a464..e7ce3820d2 100755
--- a/test/functional/mempool_limit.py
+++ b/test/functional/mempool_limit.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 mempool limiting together/eviction with the wallet."""
@@ -17,10 +17,14 @@ class MempoolLimitTest(BitcoinTestFramework):
txouts = gen_return_txouts()
relayfee = self.nodes[0].getnetworkinfo()['relayfee']
+ self.log.info('Check that mempoolminfee is minrelytxfee')
+ assert_equal(self.nodes[0].getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000'))
+ assert_equal(self.nodes[0].getmempoolinfo()['mempoolminfee'], Decimal('0.00001000'))
+
txids = []
utxos = create_confirmed_utxos(relayfee, self.nodes[0], 91)
- #create a mempool tx that will be evicted
+ self.log.info('Create a mempool tx that will be evicted')
us0 = utxos.pop()
inputs = [{ "txid" : us0["txid"], "vout" : us0["vout"]}]
outputs = {self.nodes[0].getnewaddress() : 0.0001}
@@ -37,10 +41,14 @@ class MempoolLimitTest(BitcoinTestFramework):
txids.append([])
txids[i] = create_lots_of_big_transactions(self.nodes[0], txouts, utxos[30*i:30*i+30], 30, (i+1)*base_fee)
- # by now, the tx should be evicted, check confirmation state
+ self.log.info('The tx should be evicted by now')
assert(txid not in self.nodes[0].getrawmempool())
txdata = self.nodes[0].gettransaction(txid)
assert(txdata['confirmations'] == 0) #confirmation should still be 0
+ self.log.info('Check that mempoolminfee is larger than minrelytxfee')
+ assert_equal(self.nodes[0].getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000'))
+ assert_greater_than(self.nodes[0].getmempoolinfo()['mempoolminfee'], Decimal('0.00001000'))
+
if __name__ == '__main__':
MempoolLimitTest().main()
diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py
index b845c75681..a3e872a8c6 100755
--- a/test/functional/mempool_packages.py
+++ b/test/functional/mempool_packages.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 descendant package tracking code."""
diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py
index 92f66be2ff..31a96ec60e 100755
--- a/test/functional/mempool_persist.py
+++ b/test/functional/mempool_persist.py
@@ -57,21 +57,27 @@ class MempoolPersistTest(BitcoinTestFramework):
self.log.debug("Send 5 transactions from node2 (to its own address)")
for i in range(5):
self.nodes[2].sendtoaddress(self.nodes[2].getnewaddress(), Decimal("10"))
+ node2_balance = self.nodes[2].getbalance()
self.sync_all()
self.log.debug("Verify that node0 and node1 have 5 transactions in their mempools")
assert_equal(len(self.nodes[0].getrawmempool()), 5)
assert_equal(len(self.nodes[1].getrawmempool()), 5)
- self.log.debug("Stop-start node0 and node1. Verify that node0 has the transactions in its mempool and node1 does not.")
+ self.log.debug("Stop-start the nodes. Verify that node0 has the transactions in its mempool and node1 does not. Verify that node2 calculates its balance correctly after loading wallet transactions.")
self.stop_nodes()
self.start_node(0)
self.start_node(1)
+ self.start_node(2)
# Give bitcoind a second to reload the mempool
time.sleep(1)
wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5)
+ wait_until(lambda: len(self.nodes[2].getrawmempool()) == 5)
assert_equal(len(self.nodes[1].getrawmempool()), 0)
+ # Verify accounting of mempool transactions after restart is correct
+ assert_equal(node2_balance, self.nodes[2].getbalance())
+
self.log.debug("Stop-start node0 with -persistmempool=0. Verify that it doesn't load its mempool.dat file.")
self.stop_nodes()
self.start_node(0, extra_args=["-persistmempool=0"])
diff --git a/test/functional/mempool_reorg.py b/test/functional/mempool_reorg.py
index 2803371f5b..d6bb292a58 100755
--- a/test/functional/mempool_reorg.py
+++ b/test/functional/mempool_reorg.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 mempool re-org scenarios.
diff --git a/test/functional/mempool_resurrect_test.py b/test/functional/mempool_resurrect_test.py
index 1263c9306b..83e84da4bc 100755
--- a/test/functional/mempool_resurrect_test.py
+++ b/test/functional/mempool_resurrect_test.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 resurrection of mined transactions when the blockchain is re-organized."""
diff --git a/test/functional/mempool_spendcoinbase.py b/test/functional/mempool_spendcoinbase.py
index 6e8a635a76..db0738c08a 100755
--- a/test/functional/mempool_spendcoinbase.py
+++ b/test/functional/mempool_spendcoinbase.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 spending coinbase transactions.
diff --git a/test/functional/merkle_blocks.py b/test/functional/merkle_blocks.py
index b3989a4c54..50e0371fdf 100755
--- a/test/functional/merkle_blocks.py
+++ b/test/functional/merkle_blocks.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 gettxoutproof and verifytxoutproof RPCs."""
diff --git a/test/functional/minchainwork.py b/test/functional/minchainwork.py
index c7579d2548..90a3de0e0d 100755
--- a/test/functional/minchainwork.py
+++ b/test/functional/minchainwork.py
@@ -18,7 +18,7 @@ only succeeds past a given node once its nMinimumChainWork has been exceeded.
import time
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import sync_blocks, connect_nodes, assert_equal
+from test_framework.util import connect_nodes, assert_equal
# 2 hashes required per regtest block (with no difficulty adjustment)
REGTEST_WORK_PER_BLOCK = 2
@@ -27,6 +27,7 @@ class MinimumChainWorkTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 3
+
self.extra_args = [[], ["-minimumchainwork=0x65"], ["-minimumchainwork=0x65"]]
self.node_min_work = [0, 101, 101]
@@ -74,6 +75,13 @@ class MinimumChainWorkTest(BitcoinTestFramework):
self.nodes[0].generate(1)
self.log.info("Verifying nodes are all synced")
+
+ # Because nodes in regtest are all manual connections (eg using
+ # addnode), node1 should not have disconnected node0. If not for that,
+ # we'd expect node1 to have disconnected node0 for serving an
+ # insufficient work chain, in which case we'd need to reconnect them to
+ # continue the test.
+
self.sync_all()
self.log.info("Blockcounts: %s", [n.getblockcount() for n in self.nodes])
diff --git a/test/functional/mining.py b/test/functional/mining.py
index 9aee06864e..569bf71933 100755
--- a/test/functional/mining.py
+++ b/test/functional/mining.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 mining RPCs
diff --git a/test/functional/multi_rpc.py b/test/functional/multi_rpc.py
index a2b346f274..01f68344ae 100755
--- a/test/functional/multi_rpc.py
+++ b/test/functional/multi_rpc.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Copyright (c) 2015-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 multiple RPC users."""
diff --git a/test/functional/multiwallet.py b/test/functional/multiwallet.py
index 6adcc1fd88..12d9e9f48d 100755
--- a/test/functional/multiwallet.py
+++ b/test/functional/multiwallet.py
@@ -7,6 +7,7 @@
Verify that a bitcoind node can load multiple wallet files
"""
import os
+import shutil
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error
@@ -15,10 +16,17 @@ class MultiWalletTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
- self.extra_args = [['-wallet=w1', '-wallet=w2', '-wallet=w3']]
+ self.extra_args = [['-wallet=w1', '-wallet=w2', '-wallet=w3', '-wallet=w']]
+ self.supports_cli = True
def run_test(self):
- assert_equal(set(self.nodes[0].listwallets()), {"w1", "w2", "w3"})
+ node = self.nodes[0]
+
+ data_dir = lambda *p: os.path.join(node.datadir, 'regtest', *p)
+ wallet_dir = lambda *p: data_dir('wallets', *p)
+ wallet = lambda name: node.get_wallet_rpc(name)
+
+ assert_equal(set(node.listwallets()), {"w1", "w2", "w3", "w"})
self.stop_node(0)
@@ -26,19 +34,50 @@ class MultiWalletTest(BitcoinTestFramework):
self.assert_start_raises_init_error(0, ['-wallet=w1', '-wallet=w1'], 'Error loading wallet w1. Duplicate -wallet filename specified.')
# should not initialize if wallet file is a directory
- os.mkdir(os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w11'))
+ os.mkdir(wallet_dir('w11'))
self.assert_start_raises_init_error(0, ['-wallet=w11'], 'Error loading wallet w11. -wallet filename must be a regular file.')
+ # should not initialize if one wallet is a copy of another
+ shutil.copyfile(wallet_dir('w2'), wallet_dir('w22'))
+ self.assert_start_raises_init_error(0, ['-wallet=w2', '-wallet=w22'], 'duplicates fileid')
+
# should not initialize if wallet file is a symlink
- os.symlink(os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w1'), os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w12'))
+ os.symlink(wallet_dir('w1'), wallet_dir('w12'))
self.assert_start_raises_init_error(0, ['-wallet=w12'], 'Error loading wallet w12. -wallet filename must be a regular file.')
+ # should not initialize if the specified walletdir does not exist
+ self.assert_start_raises_init_error(0, ['-walletdir=bad'], 'Error: Specified -walletdir "bad" does not exist')
+ # should not initialize if the specified walletdir is not a directory
+ not_a_dir = wallet_dir('notadir')
+ open(not_a_dir, 'a').close()
+ self.assert_start_raises_init_error(0, ['-walletdir=' + not_a_dir], 'Error: Specified -walletdir "' + not_a_dir + '" is not a directory')
+
+ # if wallets/ doesn't exist, datadir should be the default wallet dir
+ wallet_dir2 = data_dir('walletdir')
+ os.rename(wallet_dir(), wallet_dir2)
+ self.start_node(0, ['-wallet=w4', '-wallet=w5'])
+ assert_equal(set(node.listwallets()), {"w4", "w5"})
+ w5 = wallet("w5")
+ w5.generate(1)
+ self.stop_node(0)
+
+ # now if wallets/ exists again, but the rootdir is specified as the walletdir, w4 and w5 should still be loaded
+ os.rename(wallet_dir2, wallet_dir())
+ self.start_node(0, ['-wallet=w4', '-wallet=w5', '-walletdir=' + data_dir()])
+ assert_equal(set(node.listwallets()), {"w4", "w5"})
+ w5 = wallet("w5")
+ w5_info = w5.getwalletinfo()
+ assert_equal(w5_info['immature_balance'], 50)
+
+ self.stop_node(0)
+
self.start_node(0, self.extra_args[0])
- w1 = self.nodes[0].get_wallet_rpc("w1")
- w2 = self.nodes[0].get_wallet_rpc("w2")
- w3 = self.nodes[0].get_wallet_rpc("w3")
- wallet_bad = self.nodes[0].get_wallet_rpc("bad")
+ w1 = wallet("w1")
+ w2 = wallet("w2")
+ w3 = wallet("w3")
+ w4 = wallet("w")
+ wallet_bad = wallet("bad")
w1.generate(1)
@@ -46,7 +85,7 @@ class MultiWalletTest(BitcoinTestFramework):
assert_raises_rpc_error(-18, "Requested wallet does not exist or is not loaded", wallet_bad.getwalletinfo)
# accessing wallet RPC without using wallet endpoint fails
- assert_raises_rpc_error(-19, "Wallet file not specified", self.nodes[0].getwalletinfo)
+ assert_raises_rpc_error(-19, "Wallet file not specified", node.getwalletinfo)
# check w1 wallet balance
w1_info = w1.getwalletinfo()
@@ -63,18 +102,26 @@ class MultiWalletTest(BitcoinTestFramework):
w3_name = w3.getwalletinfo()['walletname']
assert_equal(w3_name, "w3")
- assert_equal({"w1", "w2", "w3"}, {w1_name, w2_name, w3_name})
+ w4_name = w4.getwalletinfo()['walletname']
+ assert_equal(w4_name, "w")
w1.generate(101)
assert_equal(w1.getbalance(), 100)
assert_equal(w2.getbalance(), 0)
assert_equal(w3.getbalance(), 0)
+ assert_equal(w4.getbalance(), 0)
w1.sendtoaddress(w2.getnewaddress(), 1)
w1.sendtoaddress(w3.getnewaddress(), 2)
+ w1.sendtoaddress(w4.getnewaddress(), 3)
w1.generate(1)
assert_equal(w2.getbalance(), 1)
assert_equal(w3.getbalance(), 2)
+ assert_equal(w4.getbalance(), 3)
+
+ batch = w1.batch([w1.getblockchaininfo.get_request(), w1.getwalletinfo.get_request()])
+ assert_equal(batch[0]["result"]["chain"], "regtest")
+ assert_equal(batch[1]["result"]["walletname"], "w1")
if __name__ == '__main__':
MultiWalletTest().main()
diff --git a/test/functional/node_network_limited.py b/test/functional/node_network_limited.py
new file mode 100755
index 0000000000..70415e0168
--- /dev/null
+++ b/test/functional/node_network_limited.py
@@ -0,0 +1,57 @@
+#!/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.
+"""Tests NODE_NETWORK_LIMITED.
+
+Tests that a node configured with -prune=550 signals NODE_NETWORK_LIMITED correctly
+and that it responds to getdata requests for blocks correctly:
+ - send a block within 288 + 2 of the tip
+ - disconnect peers who request blocks older than that."""
+from test_framework.messages import CInv, msg_getdata
+from test_framework.mininode import NODE_BLOOM, NODE_NETWORK_LIMITED, NODE_WITNESS, NetworkThread, P2PInterface
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+
+class P2PIgnoreInv(P2PInterface):
+ def on_inv(self, message):
+ # The node will send us invs for other blocks. Ignore them.
+ pass
+
+ def send_getdata_for_block(self, blockhash):
+ getdata_request = msg_getdata()
+ getdata_request.inv.append(CInv(2, int(blockhash, 16)))
+ self.send_message(getdata_request)
+
+class NodeNetworkLimitedTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 1
+ self.extra_args = [['-prune=550']]
+
+ def run_test(self):
+ node = self.nodes[0].add_p2p_connection(P2PIgnoreInv())
+ NetworkThread().start()
+ node.wait_for_verack()
+
+ expected_services = NODE_BLOOM | NODE_WITNESS | NODE_NETWORK_LIMITED
+
+ self.log.info("Check that node has signalled expected services.")
+ assert_equal(node.nServices, expected_services)
+
+ self.log.info("Check that the localservices is as expected.")
+ assert_equal(int(self.nodes[0].getnetworkinfo()['localservices'], 16), expected_services)
+
+ self.log.info("Mine enough blocks to reach the NODE_NETWORK_LIMITED range.")
+ blocks = self.nodes[0].generate(292)
+
+ self.log.info("Make sure we can max retrive block at tip-288.")
+ node.send_getdata_for_block(blocks[1]) # last block in valid range
+ node.wait_for_block(int(blocks[1], 16), timeout=3)
+
+ self.log.info("Requesting block at height 2 (tip-289) must fail (ignored).")
+ node.send_getdata_for_block(blocks[0]) # first block outside of the 288+2 limit
+ node.wait_for_disconnect(5)
+
+if __name__ == '__main__':
+ NodeNetworkLimitedTest().main()
diff --git a/test/functional/notifications.py b/test/functional/notifications.py
new file mode 100755
index 0000000000..980bef5fc8
--- /dev/null
+++ b/test/functional/notifications.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python3
+# Copyright (c) 2014-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 the -alertnotify, -blocknotify and -walletnotify options."""
+import os
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal, wait_until, connect_nodes_bi
+
+class NotificationsTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 2
+ self.setup_clean_chain = True
+
+ def setup_network(self):
+ self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt")
+ self.block_filename = os.path.join(self.options.tmpdir, "blocks.txt")
+ self.tx_filename = os.path.join(self.options.tmpdir, "transactions.txt")
+
+ # -alertnotify and -blocknotify on node0, walletnotify on node1
+ self.extra_args = [["-blockversion=2",
+ "-alertnotify=echo %%s >> %s" % self.alert_filename,
+ "-blocknotify=echo %%s >> %s" % self.block_filename],
+ ["-blockversion=211",
+ "-rescan",
+ "-walletnotify=echo %%s >> %s" % self.tx_filename]]
+ super().setup_network()
+
+ def run_test(self):
+ self.log.info("test -blocknotify")
+ block_count = 10
+ blocks = self.nodes[1].generate(block_count)
+
+ # wait at most 10 seconds for expected file size before reading the content
+ wait_until(lambda: os.path.isfile(self.block_filename) and os.stat(self.block_filename).st_size >= (block_count * 65), timeout=10)
+
+ # file content should equal the generated blocks hashes
+ with open(self.block_filename, 'r') as f:
+ assert_equal(sorted(blocks), sorted(f.read().splitlines()))
+
+ self.log.info("test -walletnotify")
+ # wait at most 10 seconds for expected file size before reading the content
+ wait_until(lambda: os.path.isfile(self.tx_filename) and os.stat(self.tx_filename).st_size >= (block_count * 65), timeout=10)
+
+ # file content should equal the generated transaction hashes
+ txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count)))
+ with open(self.tx_filename, 'r') as f:
+ assert_equal(sorted(txids_rpc), sorted(f.read().splitlines()))
+ os.remove(self.tx_filename)
+
+ self.log.info("test -walletnotify after rescan")
+ # restart node to rescan to force wallet notifications
+ self.restart_node(1)
+ connect_nodes_bi(self.nodes, 0, 1)
+
+ wait_until(lambda: os.path.isfile(self.tx_filename) and os.stat(self.tx_filename).st_size >= (block_count * 65), timeout=10)
+
+ # file content should equal the generated transaction hashes
+ txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count)))
+ with open(self.tx_filename, 'r') as f:
+ assert_equal(sorted(txids_rpc), sorted(f.read().splitlines()))
+
+ # Mine another 41 up-version blocks. -alertnotify should trigger on the 51st.
+ self.log.info("test -alertnotify")
+ self.nodes[1].generate(41)
+ self.sync_all()
+
+ # Give bitcoind 10 seconds to write the alert notification
+ wait_until(lambda: os.path.isfile(self.alert_filename) and os.path.getsize(self.alert_filename), timeout=10)
+
+ with open(self.alert_filename, 'r', encoding='utf8') as f:
+ alert_text = f.read()
+
+ # Mine more up-version blocks, should not get more alerts:
+ self.nodes[1].generate(2)
+ self.sync_all()
+
+ with open(self.alert_filename, 'r', encoding='utf8') as f:
+ alert_text2 = f.read()
+
+ self.log.info("-alertnotify should not continue notifying for more unknown version blocks")
+ assert_equal(alert_text, alert_text2)
+
+if __name__ == '__main__':
+ NotificationsTest().main()
diff --git a/test/functional/nulldummy.py b/test/functional/nulldummy.py
index 91c4550653..110b7a655e 100755
--- a/test/functional/nulldummy.py
+++ b/test/functional/nulldummy.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016 The Bitcoin Core developers
+# Copyright (c) 2016-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 NULLDUMMY softfork.
@@ -15,7 +15,7 @@ Generate 427 more blocks.
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
-from test_framework.mininode import CTransaction, NetworkThread
+from test_framework.mininode import CTransaction, network_thread_start
from test_framework.blocktools import create_coinbase, create_block, add_witness_commitment
from test_framework.script import CScript
from io import BytesIO
@@ -40,7 +40,9 @@ class NULLDUMMYTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
- self.extra_args = [['-whitelist=127.0.0.1', '-walletprematurewitness']]
+ # This script tests NULLDUMMY activation, which is part of the 'segwit' deployment, so we go through
+ # normal segwit activation here (and don't use the default always-on behaviour).
+ self.extra_args = [['-whitelist=127.0.0.1', '-walletprematurewitness', '-vbparams=segwit:0:999999999999', '-addresstype=legacy']]
def run_test(self):
self.address = self.nodes[0].getnewaddress()
@@ -48,7 +50,7 @@ class NULLDUMMYTest(BitcoinTestFramework):
self.wit_address = self.nodes[0].addwitnessaddress(self.address)
self.wit_ms_address = self.nodes[0].addwitnessaddress(self.ms_address)
- NetworkThread().start() # Start up network handling in another thread
+ network_thread_start()
self.coinbase_blocks = self.nodes[0].generate(2) # Block 2
coinbase_txid = []
for i in self.coinbase_blocks:
diff --git a/test/functional/p2p-acceptblock.py b/test/functional/p2p-acceptblock.py
index 5b6429b410..672626f15b 100755
--- a/test/functional/p2p-acceptblock.py
+++ b/test/functional/p2p-acceptblock.py
@@ -1,40 +1,35 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Copyright (c) 2015-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 processing of unrequested blocks.
-Since behavior differs when receiving unrequested blocks from whitelisted peers
-versus non-whitelisted peers, this tests the behavior of both (effectively two
-separate tests running in parallel).
+Setup: two nodes, node0+node1, not connected to each other. Node1 will have
+nMinimumChainWork set to 0x10, so it won't process low-work unrequested blocks.
-Setup: two nodes, node0 and node1, not connected to each other. Node0 does not
-whitelist localhost, but node1 does. They will each be on their own chain for
-this test.
-
-We have one NodeConn connection to each, test_node and white_node respectively.
+We have one P2PInterface connection to node0 called test_node, and one to node1
+called min_work_node.
The test:
1. Generate one block on each node, to leave IBD.
2. Mine a new block on each tip, and deliver to each node from node's peer.
- The tip should advance.
+ The tip should advance for node0, but node1 should skip processing due to
+ nMinimumChainWork.
+
+Node1 is unused in tests 3-7:
-3. Mine a block that forks the previous block, and deliver to each node from
- corresponding peer.
- Node0 should not process this block (just accept the header), because it is
- unrequested and doesn't have more work than the tip.
- Node1 should process because this is coming from a whitelisted peer.
+3. Mine a block that forks from the genesis block, and deliver to test_node.
+ Node0 should not process this block (just accept the header), because it
+ is unrequested and doesn't have more or equal work to the tip.
-4. Send another block that builds on the forking block.
- Node0 should process this block but be stuck on the shorter chain, because
- it's missing an intermediate block.
- Node1 should reorg to this longer chain.
+4a,b. Send another two blocks that build on the forking block.
+ Node0 should process the second block but be stuck on the shorter chain,
+ because it's missing an intermediate block.
-4b.Send 288 more blocks on the longer chain.
+4c.Send 288 more blocks on the longer chain (the number of blocks ahead
+ we currently store).
Node0 should process all but the last block (too far ahead in height).
- Send all headers to Node1, and then send the last block in that chain.
- Node1 should accept the block because it's coming from a whitelisted peer.
5. Send a duplicate of the block in #3 to Node0.
Node0 should not process the block because it is unrequested, and stay on
@@ -46,13 +41,21 @@ The test:
7. Send Node0 the missing block again.
Node0 should process and the tip should advance.
+
+8. Create a fork which is invalid at a height longer than the current chain
+ (ie to which the node will try to reorg) but which has headers built on top
+ of the invalid block. Check that we get disconnected if we send more headers
+ on the chain the node now knows to be invalid.
+
+9. Test Node1 is able to sync when connected to node0 (which should have sufficient
+ work on its chain).
"""
from test_framework.mininode import *
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
import time
-from test_framework.blocktools import create_block, create_coinbase
+from test_framework.blocktools import create_block, create_coinbase, create_transaction
class AcceptBlockTest(BitcoinTestFramework):
def add_options(self, parser):
@@ -63,37 +66,35 @@ class AcceptBlockTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 2
- self.extra_args = [[], ["-whitelist=127.0.0.1"]]
+ self.extra_args = [[], ["-minimumchainwork=0x10"]]
def setup_network(self):
# Node0 will be used to test behavior of processing unrequested blocks
# from peers which are not whitelisted, while Node1 will be used for
# the whitelisted case.
+ # Node2 will be used for non-whitelisted peers to test the interaction
+ # with nMinimumChainWork.
self.setup_nodes()
def run_test(self):
# Setup the p2p connections and start up the network thread.
- test_node = NodeConnCB() # connects to node0 (not whitelisted)
- white_node = NodeConnCB() # connects to node1 (whitelisted)
-
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node))
- connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1], white_node))
- test_node.add_connection(connections[0])
- white_node.add_connection(connections[1])
+ # test_node connects to node0 (not whitelisted)
+ test_node = self.nodes[0].add_p2p_connection(P2PInterface())
+ # min_work_node connects to node1 (whitelisted)
+ min_work_node = self.nodes[1].add_p2p_connection(P2PInterface())
- NetworkThread().start() # Start up network handling in another thread
+ network_thread_start()
# Test logic begins here
test_node.wait_for_verack()
- white_node.wait_for_verack()
+ min_work_node.wait_for_verack()
- # 1. Have both nodes mine a block (leave IBD)
+ # 1. Have nodes mine a block (leave IBD)
[ n.generate(1) for n in self.nodes ]
tips = [ int("0x" + n.getbestblockhash(), 0) for n in self.nodes ]
# 2. Send one block that builds on each tip.
- # This should be accepted.
+ # This should be accepted by node0
blocks_h2 = [] # the height 2 blocks on each node's chain
block_time = int(time.time()) + 1
for i in range(2):
@@ -101,95 +102,120 @@ class AcceptBlockTest(BitcoinTestFramework):
blocks_h2[i].solve()
block_time += 1
test_node.send_message(msg_block(blocks_h2[0]))
- white_node.send_message(msg_block(blocks_h2[1]))
+ min_work_node.send_message(msg_block(blocks_h2[1]))
- [ x.sync_with_ping() for x in [test_node, white_node] ]
+ for x in [test_node, min_work_node]:
+ x.sync_with_ping()
assert_equal(self.nodes[0].getblockcount(), 2)
- assert_equal(self.nodes[1].getblockcount(), 2)
- self.log.info("First height 2 block accepted by both nodes")
+ assert_equal(self.nodes[1].getblockcount(), 1)
+ self.log.info("First height 2 block accepted by node0; correctly rejected by node1")
- # 3. Send another block that builds on the original tip.
- blocks_h2f = [] # Blocks at height 2 that fork off the main chain
- for i in range(2):
- blocks_h2f.append(create_block(tips[i], create_coinbase(2), blocks_h2[i].nTime+1))
- blocks_h2f[i].solve()
- test_node.send_message(msg_block(blocks_h2f[0]))
- white_node.send_message(msg_block(blocks_h2f[1]))
+ # 3. Send another block that builds on genesis.
+ block_h1f = create_block(int("0x" + self.nodes[0].getblockhash(0), 0), create_coinbase(1), block_time)
+ block_time += 1
+ block_h1f.solve()
+ test_node.send_message(msg_block(block_h1f))
- [ x.sync_with_ping() for x in [test_node, white_node] ]
+ test_node.sync_with_ping()
+ tip_entry_found = False
for x in self.nodes[0].getchaintips():
- if x['hash'] == blocks_h2f[0].hash:
+ if x['hash'] == block_h1f.hash:
assert_equal(x['status'], "headers-only")
+ tip_entry_found = True
+ assert(tip_entry_found)
+ assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, block_h1f.hash)
- for x in self.nodes[1].getchaintips():
- if x['hash'] == blocks_h2f[1].hash:
- assert_equal(x['status'], "valid-headers")
+ # 4. Send another two block that build on the fork.
+ block_h2f = create_block(block_h1f.sha256, create_coinbase(2), block_time)
+ block_time += 1
+ block_h2f.solve()
+ test_node.send_message(msg_block(block_h2f))
- self.log.info("Second height 2 block accepted only from whitelisted peer")
+ test_node.sync_with_ping()
+ # Since the earlier block was not processed by node, the new block
+ # can't be fully validated.
+ tip_entry_found = False
+ for x in self.nodes[0].getchaintips():
+ if x['hash'] == block_h2f.hash:
+ assert_equal(x['status'], "headers-only")
+ tip_entry_found = True
+ assert(tip_entry_found)
- # 4. Now send another block that builds on the forking chain.
- blocks_h3 = []
- for i in range(2):
- blocks_h3.append(create_block(blocks_h2f[i].sha256, create_coinbase(3), blocks_h2f[i].nTime+1))
- blocks_h3[i].solve()
- test_node.send_message(msg_block(blocks_h3[0]))
- white_node.send_message(msg_block(blocks_h3[1]))
+ # But this block should be accepted by node since it has equal work.
+ self.nodes[0].getblock(block_h2f.hash)
+ self.log.info("Second height 2 block accepted, but not reorg'ed to")
+
+ # 4b. Now send another block that builds on the forking chain.
+ block_h3 = create_block(block_h2f.sha256, create_coinbase(3), block_h2f.nTime+1)
+ block_h3.solve()
+ test_node.send_message(msg_block(block_h3))
- [ x.sync_with_ping() for x in [test_node, white_node] ]
- # Since the earlier block was not processed by node0, the new block
+ test_node.sync_with_ping()
+ # Since the earlier block was not processed by node, the new block
# can't be fully validated.
+ tip_entry_found = False
for x in self.nodes[0].getchaintips():
- if x['hash'] == blocks_h3[0].hash:
+ if x['hash'] == block_h3.hash:
assert_equal(x['status'], "headers-only")
+ tip_entry_found = True
+ assert(tip_entry_found)
+ self.nodes[0].getblock(block_h3.hash)
+
+ # But this block should be accepted by node since it has more work.
+ self.nodes[0].getblock(block_h3.hash)
+ self.log.info("Unrequested more-work block accepted")
+
+ # 4c. Now mine 288 more blocks and deliver; all should be processed but
+ # the last (height-too-high) on node (as long as its not missing any headers)
+ tip = block_h3
+ all_blocks = []
+ for i in range(288):
+ next_block = create_block(tip.sha256, create_coinbase(i + 4), tip.nTime+1)
+ next_block.solve()
+ all_blocks.append(next_block)
+ tip = next_block
+
+ # Now send the block at height 5 and check that it wasn't accepted (missing header)
+ test_node.send_message(msg_block(all_blocks[1]))
+ test_node.sync_with_ping()
+ assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getblock, all_blocks[1].hash)
+ assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getblockheader, all_blocks[1].hash)
- # But this block should be accepted by node0 since it has more work.
- self.nodes[0].getblock(blocks_h3[0].hash)
- self.log.info("Unrequested more-work block accepted from non-whitelisted peer")
+ # The block at height 5 should be accepted if we provide the missing header, though
+ headers_message = msg_headers()
+ headers_message.headers.append(CBlockHeader(all_blocks[0]))
+ test_node.send_message(headers_message)
+ test_node.send_message(msg_block(all_blocks[1]))
+ test_node.sync_with_ping()
+ self.nodes[0].getblock(all_blocks[1].hash)
- # Node1 should have accepted and reorged.
- assert_equal(self.nodes[1].getblockcount(), 3)
- self.log.info("Successfully reorged to length 3 chain from whitelisted peer")
+ # Now send the blocks in all_blocks
+ for i in range(288):
+ test_node.send_message(msg_block(all_blocks[i]))
+ test_node.sync_with_ping()
- # 4b. Now mine 288 more blocks and deliver; all should be processed but
- # the last (height-too-high) on node0. Node1 should process the tip if
- # we give it the headers chain leading to the tip.
- tips = blocks_h3
- headers_message = msg_headers()
- all_blocks = [] # node0's blocks
- for j in range(2):
- for i in range(288):
- next_block = create_block(tips[j].sha256, create_coinbase(i + 4), tips[j].nTime+1)
- next_block.solve()
- if j==0:
- test_node.send_message(msg_block(next_block))
- all_blocks.append(next_block)
- else:
- headers_message.headers.append(CBlockHeader(next_block))
- tips[j] = next_block
-
- time.sleep(2)
# Blocks 1-287 should be accepted, block 288 should be ignored because it's too far ahead
for x in all_blocks[:-1]:
self.nodes[0].getblock(x.hash)
assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, all_blocks[-1].hash)
- headers_message.headers.pop() # Ensure the last block is unrequested
- white_node.send_message(headers_message) # Send headers leading to tip
- white_node.send_message(msg_block(tips[1])) # Now deliver the tip
- white_node.sync_with_ping()
- self.nodes[1].getblock(tips[1].hash)
- self.log.info("Unrequested block far ahead of tip accepted from whitelisted peer")
-
# 5. Test handling of unrequested block on the node that didn't process
# Should still not be processed (even though it has a child that has more
# work).
- test_node.send_message(msg_block(blocks_h2f[0]))
- # Here, if the sleep is too short, the test could falsely succeed (if the
- # node hasn't processed the block by the time the sleep returns, and then
- # the node processes it and incorrectly advances the tip).
- # But this would be caught later on, when we verify that an inv triggers
- # a getdata request for this block.
+ # The node should have requested the blocks at some point, so
+ # disconnect/reconnect first
+
+ self.nodes[0].disconnect_p2ps()
+ self.nodes[1].disconnect_p2ps()
+ network_thread_join()
+
+ test_node = self.nodes[0].add_p2p_connection(P2PInterface())
+ network_thread_start()
+ test_node.wait_for_verack()
+
+ test_node.send_message(msg_block(block_h1f))
+
test_node.sync_with_ping()
assert_equal(self.nodes[0].getblockcount(), 2)
self.log.info("Unrequested block that would complete more-work chain was ignored")
@@ -200,24 +226,98 @@ class AcceptBlockTest(BitcoinTestFramework):
with mininode_lock:
# Clear state so we can check the getdata request
test_node.last_message.pop("getdata", None)
- test_node.send_message(msg_inv([CInv(2, blocks_h3[0].sha256)]))
+ test_node.send_message(msg_inv([CInv(2, block_h3.sha256)]))
test_node.sync_with_ping()
with mininode_lock:
getdata = test_node.last_message["getdata"]
# Check that the getdata includes the right block
- assert_equal(getdata.inv[0].hash, blocks_h2f[0].sha256)
+ assert_equal(getdata.inv[0].hash, block_h1f.sha256)
self.log.info("Inv at tip triggered getdata for unprocessed block")
# 7. Send the missing block for the third time (now it is requested)
- test_node.send_message(msg_block(blocks_h2f[0]))
+ test_node.send_message(msg_block(block_h1f))
test_node.sync_with_ping()
assert_equal(self.nodes[0].getblockcount(), 290)
+ self.nodes[0].getblock(all_blocks[286].hash)
+ assert_equal(self.nodes[0].getbestblockhash(), all_blocks[286].hash)
+ assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, all_blocks[287].hash)
self.log.info("Successfully reorged to longer chain from non-whitelisted peer")
- [ c.disconnect_node() for c in connections ]
+ # 8. Create a chain which is invalid at a height longer than the
+ # current chain, but which has more blocks on top of that
+ block_289f = create_block(all_blocks[284].sha256, create_coinbase(289), all_blocks[284].nTime+1)
+ block_289f.solve()
+ block_290f = create_block(block_289f.sha256, create_coinbase(290), block_289f.nTime+1)
+ block_290f.solve()
+ block_291 = create_block(block_290f.sha256, create_coinbase(291), block_290f.nTime+1)
+ # block_291 spends a coinbase below maturity!
+ block_291.vtx.append(create_transaction(block_290f.vtx[0], 0, b"42", 1))
+ block_291.hashMerkleRoot = block_291.calc_merkle_root()
+ block_291.solve()
+ block_292 = create_block(block_291.sha256, create_coinbase(292), block_291.nTime+1)
+ block_292.solve()
+
+ # Now send all the headers on the chain and enough blocks to trigger reorg
+ headers_message = msg_headers()
+ headers_message.headers.append(CBlockHeader(block_289f))
+ headers_message.headers.append(CBlockHeader(block_290f))
+ headers_message.headers.append(CBlockHeader(block_291))
+ headers_message.headers.append(CBlockHeader(block_292))
+ test_node.send_message(headers_message)
+
+ test_node.sync_with_ping()
+ tip_entry_found = False
+ for x in self.nodes[0].getchaintips():
+ if x['hash'] == block_292.hash:
+ assert_equal(x['status'], "headers-only")
+ tip_entry_found = True
+ assert(tip_entry_found)
+ assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, block_292.hash)
+
+ test_node.send_message(msg_block(block_289f))
+ test_node.send_message(msg_block(block_290f))
+
+ test_node.sync_with_ping()
+ self.nodes[0].getblock(block_289f.hash)
+ self.nodes[0].getblock(block_290f.hash)
+
+ test_node.send_message(msg_block(block_291))
+
+ # At this point we've sent an obviously-bogus block, wait for full processing
+ # without assuming whether we will be disconnected or not
+ try:
+ # Only wait a short while so the test doesn't take forever if we do get
+ # disconnected
+ test_node.sync_with_ping(timeout=1)
+ except AssertionError:
+ test_node.wait_for_disconnect()
+
+ self.nodes[0].disconnect_p2ps()
+ test_node = self.nodes[0].add_p2p_connection(P2PInterface())
+
+ network_thread_start()
+ test_node.wait_for_verack()
+
+ # We should have failed reorg and switched back to 290 (but have block 291)
+ assert_equal(self.nodes[0].getblockcount(), 290)
+ assert_equal(self.nodes[0].getbestblockhash(), all_blocks[286].hash)
+ assert_equal(self.nodes[0].getblock(block_291.hash)["confirmations"], -1)
+
+ # Now send a new header on the invalid chain, indicating we're forked off, and expect to get disconnected
+ block_293 = create_block(block_292.sha256, create_coinbase(293), block_292.nTime+1)
+ block_293.solve()
+ headers_message = msg_headers()
+ headers_message.headers.append(CBlockHeader(block_293))
+ test_node.send_message(headers_message)
+ test_node.wait_for_disconnect()
+
+ # 9. Connect node1 to node0 and ensure it is able to sync
+ connect_nodes(self.nodes[0], 1)
+ sync_blocks([self.nodes[0], self.nodes[1]])
+ self.log.info("Successfully synced nodes 1 and 0")
if __name__ == '__main__':
AcceptBlockTest().main()
diff --git a/test/functional/p2p-compactblocks.py b/test/functional/p2p-compactblocks.py
index 94513d3f43..e98ae31a89 100755
--- a/test/functional/p2p-compactblocks.py
+++ b/test/functional/p2p-compactblocks.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016 The Bitcoin Core developers
+# Copyright (c) 2016-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 compact blocks (BIP 152).
@@ -15,7 +15,7 @@ from test_framework.blocktools import create_block, create_coinbase, add_witness
from test_framework.script import CScript, OP_TRUE
# TestNode: A peer we use to send messages to bitcoind, and store responses.
-class TestNode(NodeConnCB):
+class TestNode(P2PInterface):
def __init__(self):
super().__init__()
self.last_sendcmpct = []
@@ -25,21 +25,21 @@ class TestNode(NodeConnCB):
# so we can eg wait until a particular block is announced.
self.announced_blockhashes = set()
- def on_sendcmpct(self, conn, message):
+ def on_sendcmpct(self, message):
self.last_sendcmpct.append(message)
- def on_cmpctblock(self, conn, message):
+ def on_cmpctblock(self, message):
self.block_announced = True
self.last_message["cmpctblock"].header_and_shortids.header.calc_sha256()
self.announced_blockhashes.add(self.last_message["cmpctblock"].header_and_shortids.header.sha256)
- def on_headers(self, conn, message):
+ def on_headers(self, message):
self.block_announced = True
for x in self.last_message["headers"].headers:
x.calc_sha256()
self.announced_blockhashes.add(x.sha256)
- def on_inv(self, conn, message):
+ def on_inv(self, message):
for x in self.last_message["inv"].inv:
if x.type == 2:
self.block_announced = True
@@ -60,7 +60,7 @@ class TestNode(NodeConnCB):
msg = msg_getheaders()
msg.locator.vHave = locator
msg.hashstop = hashstop
- self.connection.send_message(msg)
+ self.send_message(msg)
def send_header_for_blocks(self, new_blocks):
headers_message = msg_headers()
@@ -86,14 +86,16 @@ class TestNode(NodeConnCB):
This is used when we want to send a message into the node that we expect
will get us disconnected, eg an invalid block."""
self.send_message(message)
- wait_until(lambda: not self.connected, timeout=timeout, lock=mininode_lock)
+ wait_until(lambda: self.state != "connected", timeout=timeout, lock=mininode_lock)
class CompactBlocksTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
# Node0 = pre-segwit, node1 = segwit-aware
self.num_nodes = 2
- self.extra_args = [["-vbparams=segwit:0:0"], ["-txindex"]]
+ # This test was written assuming SegWit is activated using BIP9 at height 432 (3x confirmation window).
+ # TODO: Rewrite this test to support SegWit being always active.
+ self.extra_args = [["-vbparams=segwit:0:0"], ["-vbparams=segwit:0:999999999999", "-txindex"]]
self.utxos = []
def build_block_on_tip(self, node, segwit=False):
@@ -786,23 +788,12 @@ class CompactBlocksTest(BitcoinTestFramework):
def run_test(self):
# Setup the p2p connections and start up the network thread.
- self.test_node = TestNode()
- self.segwit_node = TestNode()
- self.old_node = TestNode() # version 1 peer <--> segwit node
-
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.test_node))
- connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1],
- self.segwit_node, services=NODE_NETWORK|NODE_WITNESS))
- connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1],
- self.old_node, services=NODE_NETWORK))
- self.test_node.add_connection(connections[0])
- self.segwit_node.add_connection(connections[1])
- self.old_node.add_connection(connections[2])
-
- NetworkThread().start() # Start up network handling in another thread
-
- # Test logic begins here
+ self.test_node = self.nodes[0].add_p2p_connection(TestNode())
+ self.segwit_node = self.nodes[1].add_p2p_connection(TestNode(), services=NODE_NETWORK|NODE_WITNESS)
+ self.old_node = self.nodes[1].add_p2p_connection(TestNode(), services=NODE_NETWORK)
+
+ network_thread_start()
+
self.test_node.wait_for_verack()
# We will need UTXOs to construct transactions in later tests.
diff --git a/test/functional/p2p-feefilter.py b/test/functional/p2p-feefilter.py
index 8c92365ced..47d9c55160 100755
--- a/test/functional/p2p-feefilter.py
+++ b/test/functional/p2p-feefilter.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016 The Bitcoin Core developers
+# Copyright (c) 2016-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 processing of feefilter messages."""
@@ -22,12 +22,12 @@ def allInvsMatch(invsExpected, testnode):
time.sleep(1)
return False
-class TestNode(NodeConnCB):
+class TestNode(P2PInterface):
def __init__(self):
super().__init__()
self.txinvs = []
- def on_inv(self, conn, message):
+ def on_inv(self, message):
for i in message.inv:
if (i.type == 1):
self.txinvs.append(hashToHex(i.hash))
@@ -48,25 +48,23 @@ class FeeFilterTest(BitcoinTestFramework):
sync_blocks(self.nodes)
# Setup the p2p connections and start up the network thread.
- test_node = TestNode()
- connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node)
- test_node.add_connection(connection)
- NetworkThread().start()
- test_node.wait_for_verack()
+ self.nodes[0].add_p2p_connection(TestNode())
+ network_thread_start()
+ self.nodes[0].p2p.wait_for_verack()
# Test that invs are received for all txs at feerate of 20 sat/byte
node1.settxfee(Decimal("0.00020000"))
txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3)]
- assert(allInvsMatch(txids, test_node))
- test_node.clear_invs()
+ assert(allInvsMatch(txids, self.nodes[0].p2p))
+ self.nodes[0].p2p.clear_invs()
# Set a filter of 15 sat/byte
- test_node.send_and_ping(msg_feefilter(15000))
+ self.nodes[0].p2p.send_and_ping(msg_feefilter(15000))
# Test that txs are still being received (paying 20 sat/byte)
txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3)]
- assert(allInvsMatch(txids, test_node))
- test_node.clear_invs()
+ assert(allInvsMatch(txids, self.nodes[0].p2p))
+ self.nodes[0].p2p.clear_invs()
# Change tx fee rate to 10 sat/byte and test they are no longer received
node1.settxfee(Decimal("0.00010000"))
@@ -82,14 +80,14 @@ class FeeFilterTest(BitcoinTestFramework):
# as well.
node0.settxfee(Decimal("0.00020000"))
txids = [node0.sendtoaddress(node0.getnewaddress(), 1)]
- assert(allInvsMatch(txids, test_node))
- test_node.clear_invs()
+ assert(allInvsMatch(txids, self.nodes[0].p2p))
+ self.nodes[0].p2p.clear_invs()
# Remove fee filter and check that txs are received again
- test_node.send_and_ping(msg_feefilter(0))
+ self.nodes[0].p2p.send_and_ping(msg_feefilter(0))
txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3)]
- assert(allInvsMatch(txids, test_node))
- test_node.clear_invs()
+ assert(allInvsMatch(txids, self.nodes[0].p2p))
+ self.nodes[0].p2p.clear_invs()
if __name__ == '__main__':
FeeFilterTest().main()
diff --git a/test/functional/p2p-fingerprint.py b/test/functional/p2p-fingerprint.py
new file mode 100755
index 0000000000..93ef73e25e
--- /dev/null
+++ b/test/functional/p2p-fingerprint.py
@@ -0,0 +1,152 @@
+#!/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 various fingerprinting protections.
+
+If an stale block more than a month old or its header are requested by a peer,
+the node should pretend that it does not have it to avoid fingerprinting.
+"""
+
+import time
+
+from test_framework.blocktools import (create_block, create_coinbase)
+from test_framework.mininode import (
+ CInv,
+ P2PInterface,
+ msg_headers,
+ msg_block,
+ msg_getdata,
+ msg_getheaders,
+ network_thread_start,
+ wait_until,
+)
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+)
+
+class P2PFingerprintTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 1
+
+ # Build a chain of blocks on top of given one
+ def build_chain(self, nblocks, prev_hash, prev_height, prev_median_time):
+ blocks = []
+ for _ in range(nblocks):
+ coinbase = create_coinbase(prev_height + 1)
+ block_time = prev_median_time + 1
+ block = create_block(int(prev_hash, 16), coinbase, block_time)
+ block.solve()
+
+ blocks.append(block)
+ prev_hash = block.hash
+ prev_height += 1
+ prev_median_time = block_time
+ return blocks
+
+ # Send a getdata request for a given block hash
+ def send_block_request(self, block_hash, node):
+ msg = msg_getdata()
+ msg.inv.append(CInv(2, block_hash)) # 2 == "Block"
+ node.send_message(msg)
+
+ # Send a getheaders request for a given single block hash
+ def send_header_request(self, block_hash, node):
+ msg = msg_getheaders()
+ msg.hashstop = block_hash
+ node.send_message(msg)
+
+ # Check whether last block received from node has a given hash
+ def last_block_equals(self, expected_hash, node):
+ block_msg = node.last_message.get("block")
+ return block_msg and block_msg.block.rehash() == expected_hash
+
+ # Check whether last block header received from node has a given hash
+ def last_header_equals(self, expected_hash, node):
+ headers_msg = node.last_message.get("headers")
+ return (headers_msg and
+ headers_msg.headers and
+ headers_msg.headers[0].rehash() == expected_hash)
+
+ # Checks that stale blocks timestamped more than a month ago are not served
+ # by the node while recent stale blocks and old active chain blocks are.
+ # This does not currently test that stale blocks timestamped within the
+ # last month but that have over a month's worth of work are also withheld.
+ def run_test(self):
+ node0 = self.nodes[0].add_p2p_connection(P2PInterface())
+
+ network_thread_start()
+ node0.wait_for_verack()
+
+ # Set node time to 60 days ago
+ self.nodes[0].setmocktime(int(time.time()) - 60 * 24 * 60 * 60)
+
+ # Generating a chain of 10 blocks
+ block_hashes = self.nodes[0].generate(nblocks=10)
+
+ # Create longer chain starting 2 blocks before current tip
+ height = len(block_hashes) - 2
+ block_hash = block_hashes[height - 1]
+ block_time = self.nodes[0].getblockheader(block_hash)["mediantime"] + 1
+ new_blocks = self.build_chain(5, block_hash, height, block_time)
+
+ # Force reorg to a longer chain
+ node0.send_message(msg_headers(new_blocks))
+ node0.wait_for_getdata()
+ for block in new_blocks:
+ node0.send_and_ping(msg_block(block))
+
+ # Check that reorg succeeded
+ assert_equal(self.nodes[0].getblockcount(), 13)
+
+ stale_hash = int(block_hashes[-1], 16)
+
+ # Check that getdata request for stale block succeeds
+ self.send_block_request(stale_hash, node0)
+ test_function = lambda: self.last_block_equals(stale_hash, node0)
+ wait_until(test_function, timeout=3)
+
+ # Check that getheader request for stale block header succeeds
+ self.send_header_request(stale_hash, node0)
+ test_function = lambda: self.last_header_equals(stale_hash, node0)
+ wait_until(test_function, timeout=3)
+
+ # Longest chain is extended so stale is much older than chain tip
+ self.nodes[0].setmocktime(0)
+ tip = self.nodes[0].generate(nblocks=1)[0]
+ assert_equal(self.nodes[0].getblockcount(), 14)
+
+ # Send getdata & getheaders to refresh last received getheader message
+ block_hash = int(tip, 16)
+ self.send_block_request(block_hash, node0)
+ self.send_header_request(block_hash, node0)
+ node0.sync_with_ping()
+
+ # Request for very old stale block should now fail
+ self.send_block_request(stale_hash, node0)
+ time.sleep(3)
+ assert not self.last_block_equals(stale_hash, node0)
+
+ # Request for very old stale block header should now fail
+ self.send_header_request(stale_hash, node0)
+ time.sleep(3)
+ assert not self.last_header_equals(stale_hash, node0)
+
+ # Verify we can fetch very old blocks and headers on the active chain
+ block_hash = int(block_hashes[2], 16)
+ self.send_block_request(block_hash, node0)
+ self.send_header_request(block_hash, node0)
+ node0.sync_with_ping()
+
+ self.send_block_request(block_hash, node0)
+ test_function = lambda: self.last_block_equals(block_hash, node0)
+ wait_until(test_function, timeout=3)
+
+ self.send_header_request(block_hash, node0)
+ test_function = lambda: self.last_header_equals(block_hash, node0)
+ wait_until(test_function, timeout=3)
+
+if __name__ == '__main__':
+ P2PFingerprintTest().main()
diff --git a/test/functional/p2p-fullblocktest.py b/test/functional/p2p-fullblocktest.py
index 1d969fc7c1..fe9bbda14b 100755
--- a/test/functional/p2p-fullblocktest.py
+++ b/test/functional/p2p-fullblocktest.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Copyright (c) 2015-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 block processing.
@@ -18,9 +18,10 @@ from test_framework.blocktools import *
import time
from test_framework.key import CECKey
from test_framework.script import *
+from test_framework.mininode import network_thread_start
import struct
-class PreviousSpendableOutput(object):
+class PreviousSpendableOutput():
def __init__(self, tx = CTransaction(), n = -1):
self.tx = tx
self.n = n # the output we're spending
@@ -35,12 +36,15 @@ class CBrokenBlock(CBlock):
self.vtx = copy.deepcopy(base_block.vtx)
self.hashMerkleRoot = self.calc_merkle_root()
- def serialize(self):
+ def serialize(self, with_witness=False):
r = b""
r += super(CBlock, self).serialize()
r += struct.pack("<BQ", 255, len(self.vtx))
for tx in self.vtx:
- r += tx.serialize()
+ if with_witness:
+ r += tx.serialize_with_witness()
+ else:
+ r += tx.serialize_without_witness()
return r
def normal_serialize(self):
@@ -68,7 +72,7 @@ class FullBlockTest(ComparisonTestFramework):
def run_test(self):
self.test = TestManager(self, self.options.tmpdir)
self.test.add_all_connections(self.nodes)
- NetworkThread().start() # Start up network handling in another thread
+ network_thread_start()
self.test.run()
def add_transactions_to_block(self, block, tx_list):
diff --git a/test/functional/p2p-leaktests.py b/test/functional/p2p-leaktests.py
index 1dc8f72cd6..ce4e6e9144 100755
--- a/test/functional/p2p-leaktests.py
+++ b/test/functional/p2p-leaktests.py
@@ -20,7 +20,7 @@ from test_framework.util import *
banscore = 10
-class CLazyNode(NodeConnCB):
+class CLazyNode(P2PInterface):
def __init__(self):
super().__init__()
self.unexpected_msg = False
@@ -30,44 +30,42 @@ class CLazyNode(NodeConnCB):
self.unexpected_msg = True
self.log.info("should not have received message: %s" % message.command)
- def on_open(self, conn):
- self.connected = True
+ def on_open(self):
self.ever_connected = True
- def on_version(self, conn, message): self.bad_message(message)
- def on_verack(self, conn, message): self.bad_message(message)
- def on_reject(self, conn, message): self.bad_message(message)
- def on_inv(self, conn, message): self.bad_message(message)
- def on_addr(self, conn, message): self.bad_message(message)
- def on_alert(self, conn, message): self.bad_message(message)
- def on_getdata(self, conn, message): self.bad_message(message)
- def on_getblocks(self, conn, message): self.bad_message(message)
- def on_tx(self, conn, message): self.bad_message(message)
- def on_block(self, conn, message): self.bad_message(message)
- def on_getaddr(self, conn, message): self.bad_message(message)
- def on_headers(self, conn, message): self.bad_message(message)
- def on_getheaders(self, conn, message): self.bad_message(message)
- def on_ping(self, conn, message): self.bad_message(message)
- def on_mempool(self, conn): self.bad_message(message)
- def on_pong(self, conn, message): self.bad_message(message)
- def on_feefilter(self, conn, message): self.bad_message(message)
- def on_sendheaders(self, conn, message): self.bad_message(message)
- def on_sendcmpct(self, conn, message): self.bad_message(message)
- def on_cmpctblock(self, conn, message): self.bad_message(message)
- def on_getblocktxn(self, conn, message): self.bad_message(message)
- def on_blocktxn(self, conn, message): self.bad_message(message)
+ def on_version(self, message): self.bad_message(message)
+ def on_verack(self, message): self.bad_message(message)
+ def on_reject(self, message): self.bad_message(message)
+ def on_inv(self, message): self.bad_message(message)
+ def on_addr(self, message): self.bad_message(message)
+ def on_getdata(self, message): self.bad_message(message)
+ def on_getblocks(self, message): self.bad_message(message)
+ def on_tx(self, message): self.bad_message(message)
+ def on_block(self, message): self.bad_message(message)
+ def on_getaddr(self, message): self.bad_message(message)
+ def on_headers(self, message): self.bad_message(message)
+ def on_getheaders(self, message): self.bad_message(message)
+ def on_ping(self, message): self.bad_message(message)
+ def on_mempool(self, message): self.bad_message(message)
+ def on_pong(self, message): self.bad_message(message)
+ def on_feefilter(self, message): self.bad_message(message)
+ def on_sendheaders(self, message): self.bad_message(message)
+ def on_sendcmpct(self, message): self.bad_message(message)
+ def on_cmpctblock(self, message): self.bad_message(message)
+ def on_getblocktxn(self, message): self.bad_message(message)
+ def on_blocktxn(self, message): self.bad_message(message)
# Node that never sends a version. We'll use this to send a bunch of messages
# anyway, and eventually get disconnected.
class CNodeNoVersionBan(CLazyNode):
# send a bunch of veracks without sending a message. This should get us disconnected.
# NOTE: implementation-specific check here. Remove if bitcoind ban behavior changes
- def on_open(self, conn):
- super().on_open(conn)
+ def on_open(self):
+ super().on_open()
for i in range(banscore):
self.send_message(msg_verack())
- def on_reject(self, conn, message): pass
+ def on_reject(self, message): pass
# Node that never sends a version. This one just sits idle and hopes to receive
# any message (it shouldn't!)
@@ -81,15 +79,15 @@ class CNodeNoVerackIdle(CLazyNode):
self.version_received = False
super().__init__()
- def on_reject(self, conn, message): pass
- def on_verack(self, conn, message): pass
+ def on_reject(self, message): pass
+ def on_verack(self, message): pass
# When version is received, don't reply with a verack. Instead, see if the
# node will give us a message that it shouldn't. This is not an exhaustive
# list!
- def on_version(self, conn, message):
+ def on_version(self, message):
self.version_received = True
- conn.send_message(msg_ping())
- conn.send_message(msg_getaddr())
+ self.send_message(msg_ping())
+ self.send_message(msg_getaddr())
class P2PLeakTest(BitcoinTestFramework):
def set_test_params(self):
@@ -97,26 +95,15 @@ class P2PLeakTest(BitcoinTestFramework):
self.extra_args = [['-banscore='+str(banscore)]]
def run_test(self):
- no_version_bannode = CNodeNoVersionBan()
- no_version_idlenode = CNodeNoVersionIdle()
- no_verack_idlenode = CNodeNoVerackIdle()
- unsupported_service_bit5_node = CLazyNode()
- unsupported_service_bit7_node = CLazyNode()
-
self.nodes[0].setmocktime(1501545600) # August 1st 2017
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], no_version_bannode, send_version=False))
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], no_version_idlenode, send_version=False))
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], no_verack_idlenode))
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], unsupported_service_bit5_node, services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_5))
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], unsupported_service_bit7_node, services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_7))
- no_version_bannode.add_connection(connections[0])
- no_version_idlenode.add_connection(connections[1])
- no_verack_idlenode.add_connection(connections[2])
- unsupported_service_bit5_node.add_connection(connections[3])
- unsupported_service_bit7_node.add_connection(connections[4])
-
- NetworkThread().start() # Start up network handling in another thread
+
+ no_version_bannode = self.nodes[0].add_p2p_connection(CNodeNoVersionBan(), send_version=False)
+ no_version_idlenode = self.nodes[0].add_p2p_connection(CNodeNoVersionIdle(), send_version=False)
+ no_verack_idlenode = self.nodes[0].add_p2p_connection(CNodeNoVerackIdle())
+ unsupported_service_bit5_node = self.nodes[0].add_p2p_connection(CLazyNode(), services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_5)
+ unsupported_service_bit7_node = self.nodes[0].add_p2p_connection(CLazyNode(), services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_7)
+
+ network_thread_start()
wait_until(lambda: no_version_bannode.ever_connected, timeout=10, lock=mininode_lock)
wait_until(lambda: no_version_idlenode.ever_connected, timeout=10, lock=mininode_lock)
@@ -131,16 +118,17 @@ class P2PLeakTest(BitcoinTestFramework):
time.sleep(5)
#This node should have been banned
- assert not no_version_bannode.connected
+ assert no_version_bannode.state != "connected"
# These nodes should have been disconnected
- assert not unsupported_service_bit5_node.connected
- assert not unsupported_service_bit7_node.connected
+ assert unsupported_service_bit5_node.state != "connected"
+ assert unsupported_service_bit7_node.state != "connected"
- [conn.disconnect_node() for conn in connections]
+ self.nodes[0].disconnect_p2ps()
- # Wait until all connections are closed
+ # Wait until all connections are closed and the network thread has terminated
wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 0)
+ network_thread_join()
# Make sure no unexpected messages came in
assert(no_version_bannode.unexpected_msg == False)
@@ -152,15 +140,11 @@ class P2PLeakTest(BitcoinTestFramework):
self.log.info("Service bits 5 and 7 are allowed after August 1st 2018")
self.nodes[0].setmocktime(1533168000) # August 2nd 2018
- allowed_service_bit5_node = NodeConnCB()
- allowed_service_bit7_node = NodeConnCB()
-
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], allowed_service_bit5_node, services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_5))
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], allowed_service_bit7_node, services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_7))
- allowed_service_bit5_node.add_connection(connections[5])
- allowed_service_bit7_node.add_connection(connections[6])
+ allowed_service_bit5_node = self.nodes[0].add_p2p_connection(P2PInterface(), services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_5)
+ allowed_service_bit7_node = self.nodes[0].add_p2p_connection(P2PInterface(), services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_7)
- NetworkThread().start() # Network thread stopped when all previous NodeConnCBs disconnected. Restart it
+ # Network thread stopped when all previous P2PInterfaces disconnected. Restart it
+ network_thread_start()
wait_until(lambda: allowed_service_bit5_node.message_count["verack"], lock=mininode_lock)
wait_until(lambda: allowed_service_bit7_node.message_count["verack"], lock=mininode_lock)
diff --git a/test/functional/p2p-mempool.py b/test/functional/p2p-mempool.py
index 40fcde2605..485a8af3d0 100755
--- a/test/functional/p2p-mempool.py
+++ b/test/functional/p2p-mempool.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Copyright (c) 2015-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 p2p mempool message.
@@ -19,16 +19,14 @@ class P2PMempoolTests(BitcoinTestFramework):
self.extra_args = [["-peerbloomfilters=0"]]
def run_test(self):
- #connect a mininode
- aTestNode = NodeConnCB()
- node = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], aTestNode)
- aTestNode.add_connection(node)
- NetworkThread().start()
- aTestNode.wait_for_verack()
+ # Add a p2p connection
+ self.nodes[0].add_p2p_connection(P2PInterface())
+ network_thread_start()
+ self.nodes[0].p2p.wait_for_verack()
#request mempool
- aTestNode.send_message(msg_mempool())
- aTestNode.wait_for_disconnect()
+ self.nodes[0].p2p.send_message(msg_mempool())
+ self.nodes[0].p2p.wait_for_disconnect()
#mininode must be disconnected at this point
assert_equal(len(self.nodes[0].getpeerinfo()), 0)
diff --git a/test/functional/p2p-segwit.py b/test/functional/p2p-segwit.py
index a9ef47559b..20e4805df0 100755
--- a/test/functional/p2p-segwit.py
+++ b/test/functional/p2p-segwit.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016 The Bitcoin Core developers
+# Copyright (c) 2016-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 segwit transactions and blocks on P2P network."""
@@ -25,18 +25,46 @@ MAX_SIGOP_COST = 80000
# Calculate the virtual size of a witness block:
# (base + witness/4)
def get_virtual_size(witness_block):
- base_size = len(witness_block.serialize())
+ base_size = len(witness_block.serialize(with_witness=False))
total_size = len(witness_block.serialize(with_witness=True))
# the "+3" is so we round up
vsize = int((3*base_size + total_size + 3)/4)
return vsize
-class TestNode(NodeConnCB):
+def test_transaction_acceptance(rpc, p2p, tx, with_witness, accepted, reason=None):
+ """Send a transaction to the node and check that it's accepted to the mempool
+
+ - Submit the transaction over the p2p interface
+ - use the getrawmempool rpc to check for acceptance."""
+ tx_message = msg_tx(tx)
+ if with_witness:
+ tx_message = msg_witness_tx(tx)
+ p2p.send_message(tx_message)
+ p2p.sync_with_ping()
+ assert_equal(tx.hash in rpc.getrawmempool(), accepted)
+ if (reason != None and not accepted):
+ # Check the rejection reason as well.
+ with mininode_lock:
+ assert_equal(p2p.last_message["reject"].reason, reason)
+
+def test_witness_block(rpc, p2p, block, accepted, with_witness=True):
+ """Send a block to the node and check that it's accepted
+
+ - Submit the block over the p2p interface
+ - use the getbestblockhash rpc to check for acceptance."""
+ if with_witness:
+ p2p.send_message(msg_witness_block(block))
+ else:
+ p2p.send_message(msg_block(block))
+ p2p.sync_with_ping()
+ assert_equal(rpc.getbestblockhash() == block.hash, accepted)
+
+class TestNode(P2PInterface):
def __init__(self):
super().__init__()
self.getdataset = set()
- def on_getdata(self, conn, message):
+ def on_getdata(self, message):
for inv in message.inv:
self.getdataset.add(inv.hash)
@@ -67,29 +95,8 @@ class TestNode(NodeConnCB):
self.wait_for_block(blockhash, timeout)
return self.last_message["block"].block
- def test_transaction_acceptance(self, tx, with_witness, accepted, reason=None):
- tx_message = msg_tx(tx)
- if with_witness:
- tx_message = msg_witness_tx(tx)
- self.send_message(tx_message)
- self.sync_with_ping()
- assert_equal(tx.hash in self.connection.rpc.getrawmempool(), accepted)
- if (reason != None and not accepted):
- # Check the rejection reason as well.
- with mininode_lock:
- assert_equal(self.last_message["reject"].reason, reason)
-
- # Test whether a witness block had the correct effect on the tip
- def test_witness_block(self, block, accepted, with_witness=True):
- if with_witness:
- self.send_message(msg_witness_block(block))
- else:
- self.send_message(msg_block(block))
- self.sync_with_ping()
- assert_equal(self.connection.rpc.getbestblockhash() == block.hash, accepted)
-
# Used to keep track of anyone-can-spend outputs that we can use in the tests
-class UTXO(object):
+class UTXO():
def __init__(self, sha256, n, nValue):
self.sha256 = sha256
self.n = n
@@ -111,7 +118,8 @@ class SegWitTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 3
- self.extra_args = [["-whitelist=127.0.0.1"], ["-whitelist=127.0.0.1", "-acceptnonstdtxn=0"], ["-whitelist=127.0.0.1", "-vbparams=segwit:0:0"]]
+ # This test tests SegWit both pre and post-activation, so use the normal BIP9 activation.
+ self.extra_args = [["-whitelist=127.0.0.1", "-vbparams=segwit:0:999999999999"], ["-whitelist=127.0.0.1", "-acceptnonstdtxn=0", "-vbparams=segwit:0:999999999999"], ["-whitelist=127.0.0.1", "-vbparams=segwit:0:0"]]
def setup_network(self):
self.setup_nodes()
@@ -140,7 +148,7 @@ class SegWitTest(BitcoinTestFramework):
''' Individual tests '''
def test_witness_services(self):
self.log.info("Verifying NODE_WITNESS service bit")
- assert((self.test_node.connection.nServices & NODE_WITNESS) != 0)
+ assert((self.test_node.nServices & NODE_WITNESS) != 0)
# See if sending a regular transaction works, and create a utxo
@@ -199,7 +207,7 @@ class SegWitTest(BitcoinTestFramework):
self.update_witness_block_with_transactions(block, [tx])
# Sending witness data before activation is not allowed (anti-spam
# rule).
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# TODO: fix synchronization so we can test reject reason
# Right now, bitcoind delays sending reject messages for blocks
# until the future, making synchronization here difficult.
@@ -226,7 +234,7 @@ class SegWitTest(BitcoinTestFramework):
tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b""))
tx2.vout.append(CTxOut(tx.vout[0].nValue-1000, scriptPubKey))
tx2.rehash()
- self.test_node.test_transaction_acceptance(tx2, False, True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx2, False, True)
self.nodes[0].generate(1)
sync_blocks(self.nodes)
@@ -243,18 +251,18 @@ class SegWitTest(BitcoinTestFramework):
tx3.rehash()
# Note that this should be rejected for the premature witness reason,
# rather than a policy check, since segwit hasn't activated yet.
- self.std_node.test_transaction_acceptance(tx3, True, False, b'no-witness-yet')
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx3, True, False, b'no-witness-yet')
# If we send without witness, it should be accepted.
- self.std_node.test_transaction_acceptance(tx3, False, True)
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx3, False, True)
# Now create a new anyone-can-spend utxo for the next test.
tx4 = CTransaction()
tx4.vin.append(CTxIn(COutPoint(tx3.sha256, 0), CScript([p2sh_program])))
tx4.vout.append(CTxOut(tx3.vout[0].nValue-1000, CScript([OP_TRUE])))
tx4.rehash()
- self.test_node.test_transaction_acceptance(tx3, False, True)
- self.test_node.test_transaction_acceptance(tx4, False, True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, False, True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx4, False, True)
self.nodes[0].generate(1)
sync_blocks(self.nodes)
@@ -315,7 +323,7 @@ class SegWitTest(BitcoinTestFramework):
assert(msg_witness_block(block).serialize() != msg_block(block).serialize())
# This empty block should be valid.
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
# Try to tweak the nonce
block_2 = self.build_next_block()
@@ -326,7 +334,7 @@ class SegWitTest(BitcoinTestFramework):
assert(block_2.vtx[0].vout[-1] != block.vtx[0].vout[-1])
# This should also be valid.
- self.test_node.test_witness_block(block_2, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block_2, accepted=True)
# Now test commitments with actual transactions
assert (len(self.utxo) > 0)
@@ -359,7 +367,7 @@ class SegWitTest(BitcoinTestFramework):
block_3.rehash()
block_3.solve()
- self.test_node.test_witness_block(block_3, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block_3, accepted=False)
# Add a different commitment with different nonce, but in the
# right location, and with some funds burned(!).
@@ -373,7 +381,7 @@ class SegWitTest(BitcoinTestFramework):
block_3.rehash()
assert(len(block_3.vtx[0].vout) == 4) # 3 OP_returns
block_3.solve()
- self.test_node.test_witness_block(block_3, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block_3, accepted=True)
# Finally test that a block with no witness transactions can
# omit the commitment.
@@ -385,7 +393,7 @@ class SegWitTest(BitcoinTestFramework):
block_4.vtx.append(tx3)
block_4.hashMerkleRoot = block_4.calc_merkle_root()
block_4.solve()
- self.test_node.test_witness_block(block_4, with_witness=False, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block_4, with_witness=False, accepted=True)
# Update available utxo's for use in later test.
self.utxo.pop(0)
@@ -426,11 +434,11 @@ class SegWitTest(BitcoinTestFramework):
# Change the nonce -- should not cause the block to be permanently
# failed
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ ser_uint256(1) ]
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# Changing the witness nonce doesn't change the block hash
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ ser_uint256(0) ]
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
def test_witness_block_size(self):
@@ -495,7 +503,7 @@ class SegWitTest(BitcoinTestFramework):
# limit
assert(len(block.serialize(True)) > 2*1024*1024)
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# Now resize the second transaction to make the block fit.
cur_length = len(block.vtx[-1].wit.vtxinwit[0].scriptWitness.stack[0])
@@ -505,7 +513,7 @@ class SegWitTest(BitcoinTestFramework):
block.solve()
assert(get_virtual_size(block) == MAX_BLOCK_BASE_SIZE)
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
# Update available utxo's
self.utxo.pop(0)
@@ -572,7 +580,7 @@ class SegWitTest(BitcoinTestFramework):
self.update_witness_block_with_transactions(block, [tx])
# Extra witness data should not be allowed.
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# Try extra signature data. Ok if we're not spending a witness output.
block.vtx[1].wit.vtxinwit = []
@@ -581,7 +589,7 @@ class SegWitTest(BitcoinTestFramework):
add_witness_commitment(block)
block.solve()
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
# Now try extra witness/signature data on an input that DOES require a
# witness
@@ -597,7 +605,7 @@ class SegWitTest(BitcoinTestFramework):
self.update_witness_block_with_transactions(block, [tx2])
# This has extra witness data, so it should fail.
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# Now get rid of the extra witness, but add extra scriptSig data
tx2.vin[0].scriptSig = CScript([OP_TRUE])
@@ -609,7 +617,7 @@ class SegWitTest(BitcoinTestFramework):
block.solve()
# This has extra signature data for a witness input, so it should fail.
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# Now get rid of the extra scriptsig on the witness input, and verify
# success (even with extra scriptsig data in the non-witness input)
@@ -618,7 +626,7 @@ class SegWitTest(BitcoinTestFramework):
add_witness_commitment(block)
block.solve()
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
# Update utxo for later tests
self.utxo.pop(0)
@@ -651,14 +659,14 @@ class SegWitTest(BitcoinTestFramework):
tx2.rehash()
self.update_witness_block_with_transactions(block, [tx, tx2])
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# Now reduce the length of the stack element
tx2.wit.vtxinwit[0].scriptWitness.stack[0] = b'a'*(MAX_SCRIPT_ELEMENT_SIZE)
add_witness_commitment(block)
block.solve()
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
# Update the utxo for later tests
self.utxo.pop()
@@ -693,7 +701,7 @@ class SegWitTest(BitcoinTestFramework):
self.update_witness_block_with_transactions(block, [tx, tx2])
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# Try again with one less byte in the witness program
witness_program = CScript([b'a'*520]*19 + [OP_DROP]*62 + [OP_TRUE])
@@ -708,7 +716,7 @@ class SegWitTest(BitcoinTestFramework):
tx2.rehash()
block.vtx = [block.vtx[0]]
self.update_witness_block_with_transactions(block, [tx, tx2])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
self.utxo.pop()
self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue))
@@ -734,7 +742,7 @@ class SegWitTest(BitcoinTestFramework):
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
# Try various ways to spend tx that should all break.
# This "broken" transaction serializer will not normalize
@@ -769,7 +777,7 @@ class SegWitTest(BitcoinTestFramework):
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx2])
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# Now try using a too short vtxinwit
tx2.wit.vtxinwit.pop()
@@ -777,7 +785,7 @@ class SegWitTest(BitcoinTestFramework):
block.vtx = [block.vtx[0]]
self.update_witness_block_with_transactions(block, [tx2])
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# Now make one of the intermediate witnesses be incorrect
tx2.wit.vtxinwit.append(CTxInWitness())
@@ -786,13 +794,13 @@ class SegWitTest(BitcoinTestFramework):
block.vtx = [block.vtx[0]]
self.update_witness_block_with_transactions(block, [tx2])
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# Fix the broken witness and the block should be accepted.
tx2.wit.vtxinwit[5].scriptWitness.stack = [b'a', witness_program]
block.vtx = [block.vtx[0]]
self.update_witness_block_with_transactions(block, [tx2])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
self.utxo.pop()
self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue))
@@ -832,11 +840,11 @@ class SegWitTest(BitcoinTestFramework):
# its from)
assert_equal(len(self.nodes[0].getrawmempool()), 0)
assert_equal(len(self.nodes[1].getrawmempool()), 0)
- self.old_node.test_transaction_acceptance(tx, with_witness=True, accepted=False)
- self.test_node.test_transaction_acceptance(tx, with_witness=True, accepted=False)
+ test_transaction_acceptance(self.nodes[0].rpc, self.old_node, tx, with_witness=True, accepted=False)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=True, accepted=False)
# But eliminating the witness should fix it
- self.test_node.test_transaction_acceptance(tx, with_witness=False, accepted=True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=False, accepted=True)
# Cleanup: mine the first transaction and update utxo
self.nodes[0].generate(1)
@@ -868,11 +876,11 @@ class SegWitTest(BitcoinTestFramework):
# Verify that unnecessary witnesses are rejected.
self.test_node.announce_tx_and_wait_for_getdata(tx)
assert_equal(len(self.nodes[0].getrawmempool()), 0)
- self.test_node.test_transaction_acceptance(tx, with_witness=True, accepted=False)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=True, accepted=False)
# Verify that removing the witness succeeds.
self.test_node.announce_tx_and_wait_for_getdata(tx)
- self.test_node.test_transaction_acceptance(tx, with_witness=False, accepted=True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=False, accepted=True)
# Now try to add extra witness data to a valid witness tx.
witness_program = CScript([OP_TRUE])
@@ -897,24 +905,24 @@ class SegWitTest(BitcoinTestFramework):
# Node will not be blinded to the transaction
self.std_node.announce_tx_and_wait_for_getdata(tx3)
- self.std_node.test_transaction_acceptance(tx3, True, False, b'tx-size')
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx3, True, False, b'tx-size')
self.std_node.announce_tx_and_wait_for_getdata(tx3)
- self.std_node.test_transaction_acceptance(tx3, True, False, b'tx-size')
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx3, True, False, b'tx-size')
# Remove witness stuffing, instead add extra witness push on stack
tx3.vout[0] = CTxOut(tx2.vout[0].nValue-1000, CScript([OP_TRUE]))
tx3.wit.vtxinwit[0].scriptWitness.stack = [CScript([CScriptNum(1)]), witness_program ]
tx3.rehash()
- self.test_node.test_transaction_acceptance(tx2, with_witness=True, accepted=True)
- self.test_node.test_transaction_acceptance(tx3, with_witness=True, accepted=False)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx2, with_witness=True, accepted=True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=False)
# Get rid of the extra witness, and verify acceptance.
tx3.wit.vtxinwit[0].scriptWitness.stack = [ witness_program ]
# Also check that old_node gets a tx announcement, even though this is
# a witness transaction.
self.old_node.wait_for_inv([CInv(1, tx2.sha256)]) # wait until tx2 was inv'ed
- self.test_node.test_transaction_acceptance(tx3, with_witness=True, accepted=True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=True)
self.old_node.wait_for_inv([CInv(1, tx3.sha256)])
# Test that getrawtransaction returns correct witness information
@@ -953,20 +961,20 @@ class SegWitTest(BitcoinTestFramework):
self.test_node.announce_block_and_wait_for_getdata(block1, use_header=False)
assert(self.test_node.last_message["getdata"].inv[0].type == blocktype)
- self.test_node.test_witness_block(block1, True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block1, True)
block2 = self.build_next_block(nVersion=4)
block2.solve()
self.test_node.announce_block_and_wait_for_getdata(block2, use_header=True)
assert(self.test_node.last_message["getdata"].inv[0].type == blocktype)
- self.test_node.test_witness_block(block2, True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block2, True)
block3 = self.build_next_block(nVersion=(VB_TOP_BITS | (1<<15)))
block3.solve()
self.test_node.announce_block_and_wait_for_getdata(block3, use_header=True)
assert(self.test_node.last_message["getdata"].inv[0].type == blocktype)
- self.test_node.test_witness_block(block3, True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block3, True)
# Check that we can getdata for witness blocks or regular blocks,
# and the right thing happens.
@@ -996,7 +1004,7 @@ class SegWitTest(BitcoinTestFramework):
# This gives us a witness commitment.
assert(len(block.vtx[0].wit.vtxinwit) == 1)
assert(len(block.vtx[0].wit.vtxinwit[0].scriptWitness.stack) == 1)
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
# Now try to retrieve it...
rpc_block = self.nodes[0].getblock(block.hash, False)
non_wit_block = self.test_node.request_block(block.sha256, 2)
@@ -1050,7 +1058,7 @@ class SegWitTest(BitcoinTestFramework):
p2sh_tx.rehash()
# Mine it on test_node to create the confirmed output.
- self.test_node.test_transaction_acceptance(p2sh_tx, with_witness=True, accepted=True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2sh_tx, with_witness=True, accepted=True)
self.nodes[0].generate(1)
sync_blocks(self.nodes)
@@ -1062,7 +1070,7 @@ class SegWitTest(BitcoinTestFramework):
tx.vout.append(CTxOut(8000, scriptPubKey)) # Might burn this later
tx.rehash()
- self.std_node.test_transaction_acceptance(tx, with_witness=True, accepted=segwit_activated)
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx, with_witness=True, accepted=segwit_activated)
# Now create something that looks like a P2PKH output. This won't be spendable.
scriptPubKey = CScript([OP_0, hash160(witness_hash)])
@@ -1079,7 +1087,7 @@ class SegWitTest(BitcoinTestFramework):
tx2.vout = [CTxOut(p2sh_tx.vout[0].nValue-1000, scriptPubKey)]
tx2.rehash()
- self.std_node.test_transaction_acceptance(tx2, with_witness=True, accepted=segwit_activated)
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx2, with_witness=True, accepted=segwit_activated)
# Now update self.utxo for later tests.
tx3 = CTransaction()
@@ -1092,13 +1100,13 @@ class SegWitTest(BitcoinTestFramework):
tx3.wit.vtxinwit.append(CTxInWitness())
tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_program]
tx3.rehash()
- self.test_node.test_transaction_acceptance(tx3, with_witness=True, accepted=True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=True)
else:
# tx and tx2 didn't go anywhere; just clean up the p2sh_tx output.
tx3.vin = [CTxIn(COutPoint(p2sh_tx.sha256, 0), CScript([witness_program]))]
tx3.vout = [CTxOut(p2sh_tx.vout[0].nValue-1000, witness_program)]
tx3.rehash()
- self.test_node.test_transaction_acceptance(tx3, with_witness=True, accepted=True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=True)
self.nodes[0].generate(1)
sync_blocks(self.nodes)
@@ -1122,7 +1130,7 @@ class SegWitTest(BitcoinTestFramework):
tx.rehash()
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
self.utxo.pop(0)
for i in range(NUM_TESTS):
self.utxo.append(UTXO(tx.sha256, i, split_value))
@@ -1141,8 +1149,8 @@ class SegWitTest(BitcoinTestFramework):
tx.vin = [CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")]
tx.vout = [CTxOut(self.utxo[0].nValue-1000, scriptPubKey)]
tx.rehash()
- self.std_node.test_transaction_acceptance(tx, with_witness=True, accepted=False)
- self.test_node.test_transaction_acceptance(tx, with_witness=True, accepted=True)
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx, with_witness=True, accepted=False)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=True, accepted=True)
self.utxo.pop(0)
temp_utxo.append(UTXO(tx.sha256, 0, tx.vout[0].nValue))
@@ -1161,8 +1169,8 @@ class SegWitTest(BitcoinTestFramework):
tx2.rehash()
# Gets accepted to test_node, because standardness of outputs isn't
# checked with fRequireStandard
- self.test_node.test_transaction_acceptance(tx2, with_witness=True, accepted=True)
- self.std_node.test_transaction_acceptance(tx2, with_witness=True, accepted=False)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx2, with_witness=True, accepted=True)
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx2, with_witness=True, accepted=False)
temp_utxo.pop() # last entry in temp_utxo was the output we just spent
temp_utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue))
@@ -1178,7 +1186,7 @@ class SegWitTest(BitcoinTestFramework):
tx3.rehash()
# Spending a higher version witness output is not allowed by policy,
# even with fRequireStandard=false.
- self.test_node.test_transaction_acceptance(tx3, with_witness=True, accepted=False)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=False)
self.test_node.sync_with_ping()
with mininode_lock:
assert(b"reserved for soft-fork upgrades" in self.test_node.last_message["reject"].reason)
@@ -1186,7 +1194,7 @@ class SegWitTest(BitcoinTestFramework):
# Building a block with the transaction must be valid, however.
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx2, tx3])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
sync_blocks(self.nodes)
# Add utxo to our list
@@ -1204,7 +1212,7 @@ class SegWitTest(BitcoinTestFramework):
# This next line will rehash the coinbase and update the merkle
# root, and solve.
self.update_witness_block_with_transactions(block, [])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
spend_tx = CTransaction()
spend_tx.vin = [CTxIn(COutPoint(block.vtx[0].sha256, 0), b"")]
@@ -1218,13 +1226,13 @@ class SegWitTest(BitcoinTestFramework):
sync_blocks(self.nodes)
block2 = self.build_next_block()
self.update_witness_block_with_transactions(block2, [spend_tx])
- self.test_node.test_witness_block(block2, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block2, accepted=False)
# Advancing one more block should allow the spend.
self.nodes[0].generate(1)
block2 = self.build_next_block()
self.update_witness_block_with_transactions(block2, [spend_tx])
- self.test_node.test_witness_block(block2, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block2, accepted=True)
sync_blocks(self.nodes)
@@ -1245,11 +1253,11 @@ class SegWitTest(BitcoinTestFramework):
tx.vout.append(CTxOut(self.utxo[0].nValue-1000, scriptPubKey))
tx.rehash()
- self.test_node.test_transaction_acceptance(tx, with_witness=True, accepted=True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=True, accepted=True)
# Mine this transaction in preparation for following tests.
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
sync_blocks(self.nodes)
self.utxo.pop(0)
@@ -1266,19 +1274,19 @@ class SegWitTest(BitcoinTestFramework):
# Too-large input value
sign_P2PK_witness_input(witness_program, tx, 0, hashtype, prev_utxo.nValue+1, key)
self.update_witness_block_with_transactions(block, [tx])
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# Too-small input value
sign_P2PK_witness_input(witness_program, tx, 0, hashtype, prev_utxo.nValue-1, key)
block.vtx.pop() # remove last tx
self.update_witness_block_with_transactions(block, [tx])
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# Now try correct value
sign_P2PK_witness_input(witness_program, tx, 0, hashtype, prev_utxo.nValue, key)
block.vtx.pop()
self.update_witness_block_with_transactions(block, [tx])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
prev_utxo = UTXO(tx.sha256, 0, tx.vout[0].nValue)
@@ -1302,7 +1310,7 @@ class SegWitTest(BitcoinTestFramework):
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
block = self.build_next_block()
used_sighash_single_out_of_bounds = False
@@ -1344,7 +1352,7 @@ class SegWitTest(BitcoinTestFramework):
# Test the block periodically, if we're close to maxblocksize
if (get_virtual_size(block) > MAX_BLOCK_BASE_SIZE - 1000):
self.update_witness_block_with_transactions(block, [])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
block = self.build_next_block()
if (not used_sighash_single_out_of_bounds):
@@ -1352,7 +1360,7 @@ class SegWitTest(BitcoinTestFramework):
# Test the transactions we've added to the block
if (len(block.vtx) > 1):
self.update_witness_block_with_transactions(block, [])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
# Now test witness version 0 P2PKH transactions
pubkeyhash = hash160(pubkey)
@@ -1374,7 +1382,7 @@ class SegWitTest(BitcoinTestFramework):
tx2.vin[0].scriptSig = CScript([signature, pubkey])
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx, tx2])
- self.test_node.test_witness_block(block, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
# Move the signature to the witness.
block.vtx.pop()
@@ -1384,26 +1392,28 @@ class SegWitTest(BitcoinTestFramework):
tx2.rehash()
self.update_witness_block_with_transactions(block, [tx2])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
temp_utxos.pop(0)
- # Update self.utxos for later tests. Just spend everything in
- # temp_utxos to a corresponding entry in self.utxos
+ # Update self.utxos for later tests by creating two outputs
+ # that consolidate all the coins in temp_utxos.
+ output_value = sum(i.nValue for i in temp_utxos) // 2
+
tx = CTransaction()
index = 0
+ # Just spend to our usual anyone-can-spend output
+ tx.vout = [CTxOut(output_value, CScript([OP_TRUE]))] * 2
for i in temp_utxos:
- # Just spend to our usual anyone-can-spend output
- # Use SIGHASH_SINGLE|SIGHASH_ANYONECANPAY so we can build up
+ # Use SIGHASH_ALL|SIGHASH_ANYONECANPAY so we can build up
# the signatures as we go.
tx.vin.append(CTxIn(COutPoint(i.sha256, i.n), b""))
- tx.vout.append(CTxOut(i.nValue, CScript([OP_TRUE])))
tx.wit.vtxinwit.append(CTxInWitness())
- sign_P2PK_witness_input(witness_program, tx, index, SIGHASH_SINGLE|SIGHASH_ANYONECANPAY, i.nValue, key)
+ sign_P2PK_witness_input(witness_program, tx, index, SIGHASH_ALL|SIGHASH_ANYONECANPAY, i.nValue, key)
index += 1
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
for i in range(len(tx.vout)):
self.utxo.append(UTXO(tx.sha256, i, tx.vout[i].nValue))
@@ -1430,10 +1440,10 @@ class SegWitTest(BitcoinTestFramework):
tx.rehash()
# Verify mempool acceptance and block validity
- self.test_node.test_transaction_acceptance(tx, with_witness=False, accepted=True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=False, accepted=True)
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx])
- self.test_node.test_witness_block(block, accepted=True, with_witness=segwit_activated)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True, with_witness=segwit_activated)
sync_blocks(self.nodes)
# Now test attempts to spend the output.
@@ -1447,12 +1457,12 @@ class SegWitTest(BitcoinTestFramework):
# will require a witness to spend a witness program regardless of
# segwit activation. Note that older bitcoind's that are not
# segwit-aware would also reject this for failing CLEANSTACK.
- self.test_node.test_transaction_acceptance(spend_tx, with_witness=False, accepted=False)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, spend_tx, with_witness=False, accepted=False)
# Try to put the witness script in the scriptSig, should also fail.
spend_tx.vin[0].scriptSig = CScript([p2wsh_pubkey, b'a'])
spend_tx.rehash()
- self.test_node.test_transaction_acceptance(spend_tx, with_witness=False, accepted=False)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, spend_tx, with_witness=False, accepted=False)
# Now put the witness script in the witness, should succeed after
# segwit activates.
@@ -1462,7 +1472,7 @@ class SegWitTest(BitcoinTestFramework):
spend_tx.wit.vtxinwit[0].scriptWitness.stack = [ b'a', witness_program ]
# Verify mempool acceptance
- self.test_node.test_transaction_acceptance(spend_tx, with_witness=True, accepted=segwit_activated)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, spend_tx, with_witness=True, accepted=segwit_activated)
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [spend_tx])
@@ -1470,9 +1480,9 @@ class SegWitTest(BitcoinTestFramework):
# should be valid. If we're after activation, then sending this with
# witnesses should be valid.
if segwit_activated:
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
else:
- self.test_node.test_witness_block(block, accepted=True, with_witness=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True, with_witness=False)
# Update self.utxo
self.utxo.pop(0)
@@ -1493,7 +1503,7 @@ class SegWitTest(BitcoinTestFramework):
# Restart with the new binary
self.stop_node(node_id)
- self.start_node(node_id, extra_args=[])
+ self.start_node(node_id, extra_args=["-vbparams=segwit:0:999999999999"])
connect_nodes(self.nodes[0], node_id)
sync_blocks(self.nodes)
@@ -1556,7 +1566,7 @@ class SegWitTest(BitcoinTestFramework):
block_1 = self.build_next_block()
self.update_witness_block_with_transactions(block_1, [tx])
- self.test_node.test_witness_block(block_1, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block_1, accepted=True)
tx2 = CTransaction()
# If we try to spend the first n-1 outputs from tx, that should be
@@ -1573,7 +1583,7 @@ class SegWitTest(BitcoinTestFramework):
block_2 = self.build_next_block()
self.update_witness_block_with_transactions(block_2, [tx2])
- self.test_node.test_witness_block(block_2, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block_2, accepted=False)
# Try dropping the last input in tx2, and add an output that has
# too many sigops (contributing to legacy sigop count).
@@ -1586,14 +1596,14 @@ class SegWitTest(BitcoinTestFramework):
tx2.rehash()
block_3 = self.build_next_block()
self.update_witness_block_with_transactions(block_3, [tx2])
- self.test_node.test_witness_block(block_3, accepted=False)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block_3, accepted=False)
# If we drop the last checksig in this output, the tx should succeed.
block_4 = self.build_next_block()
tx2.vout[-1].scriptPubKey = CScript([OP_CHECKSIG]*(checksig_count-1))
tx2.rehash()
self.update_witness_block_with_transactions(block_4, [tx2])
- self.test_node.test_witness_block(block_4, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block_4, accepted=True)
# Reset the tip back down for the next test
sync_blocks(self.nodes)
@@ -1609,7 +1619,7 @@ class SegWitTest(BitcoinTestFramework):
tx2.wit.vtxinwit[-1].scriptWitness.stack = [ witness_program_justright ]
tx2.rehash()
self.update_witness_block_with_transactions(block_5, [tx2])
- self.test_node.test_witness_block(block_5, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block_5, accepted=True)
# TODO: test p2sh sigop counting
@@ -1687,7 +1697,7 @@ class SegWitTest(BitcoinTestFramework):
# Confirm it in a block.
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
# Now try to spend it. Send it to a P2WSH output, which we'll
# use in the next test.
@@ -1706,11 +1716,11 @@ class SegWitTest(BitcoinTestFramework):
tx2.rehash()
# Should fail policy test.
- self.test_node.test_transaction_acceptance(tx2, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)')
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx2, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)')
# But passes consensus.
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx2])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
# Test 2: P2WSH
# Try to spend the P2WSH output created in last test.
@@ -1726,11 +1736,11 @@ class SegWitTest(BitcoinTestFramework):
sign_P2PK_witness_input(witness_program, tx3, 0, SIGHASH_ALL, tx2.vout[0].nValue, key)
# Should fail policy test.
- self.test_node.test_transaction_acceptance(tx3, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)')
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)')
# But passes consensus.
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx3])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
# Test 3: P2SH(P2WSH)
# Try to spend the P2SH output created in the last test.
@@ -1743,10 +1753,10 @@ class SegWitTest(BitcoinTestFramework):
sign_P2PK_witness_input(witness_program, tx4, 0, SIGHASH_ALL, tx3.vout[0].nValue, key)
# Should fail policy test.
- self.test_node.test_transaction_acceptance(tx4, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)')
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx4, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)')
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx4])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
# Test 4: Uncompressed pubkeys should still be valid in non-segwit
# transactions.
@@ -1758,10 +1768,10 @@ class SegWitTest(BitcoinTestFramework):
tx5.vin[0].scriptSig = CScript([signature, pubkey])
tx5.rehash()
# Should pass policy and consensus.
- self.test_node.test_transaction_acceptance(tx5, True, True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx5, True, True)
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx5])
- self.test_node.test_witness_block(block, accepted=True)
+ test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
self.utxo.append(UTXO(tx5.sha256, 0, tx5.vout[0].nValue))
def test_non_standard_witness(self):
@@ -1791,7 +1801,7 @@ class SegWitTest(BitcoinTestFramework):
tx.vout.append(CTxOut(outputvalue, CScript([OP_HASH160, p2sh, OP_EQUAL])))
tx.rehash()
txid = tx.sha256
- self.test_node.test_transaction_acceptance(tx, with_witness=False, accepted=True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=False, accepted=True)
self.nodes[0].generate(1)
sync_blocks(self.nodes)
@@ -1816,45 +1826,45 @@ class SegWitTest(BitcoinTestFramework):
# Testing native P2WSH
# Witness stack size, excluding witnessScript, over 100 is non-standard
p2wsh_txs[0].wit.vtxinwit[0].scriptWitness.stack = [pad] * 101 + [scripts[0]]
- self.std_node.test_transaction_acceptance(p2wsh_txs[0], True, False, b'bad-witness-nonstandard')
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2wsh_txs[0], True, False, b'bad-witness-nonstandard')
# Non-standard nodes should accept
- self.test_node.test_transaction_acceptance(p2wsh_txs[0], True, True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2wsh_txs[0], True, True)
# Stack element size over 80 bytes is non-standard
p2wsh_txs[1].wit.vtxinwit[0].scriptWitness.stack = [pad * 81] * 100 + [scripts[1]]
- self.std_node.test_transaction_acceptance(p2wsh_txs[1], True, False, b'bad-witness-nonstandard')
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2wsh_txs[1], True, False, b'bad-witness-nonstandard')
# Non-standard nodes should accept
- self.test_node.test_transaction_acceptance(p2wsh_txs[1], True, True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2wsh_txs[1], True, True)
# Standard nodes should accept if element size is not over 80 bytes
p2wsh_txs[1].wit.vtxinwit[0].scriptWitness.stack = [pad * 80] * 100 + [scripts[1]]
- self.std_node.test_transaction_acceptance(p2wsh_txs[1], True, True)
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2wsh_txs[1], True, True)
# witnessScript size at 3600 bytes is standard
p2wsh_txs[2].wit.vtxinwit[0].scriptWitness.stack = [pad, pad, scripts[2]]
- self.test_node.test_transaction_acceptance(p2wsh_txs[2], True, True)
- self.std_node.test_transaction_acceptance(p2wsh_txs[2], True, True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2wsh_txs[2], True, True)
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2wsh_txs[2], True, True)
# witnessScript size at 3601 bytes is non-standard
p2wsh_txs[3].wit.vtxinwit[0].scriptWitness.stack = [pad, pad, pad, scripts[3]]
- self.std_node.test_transaction_acceptance(p2wsh_txs[3], True, False, b'bad-witness-nonstandard')
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2wsh_txs[3], True, False, b'bad-witness-nonstandard')
# Non-standard nodes should accept
- self.test_node.test_transaction_acceptance(p2wsh_txs[3], True, True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2wsh_txs[3], True, True)
# Repeating the same tests with P2SH-P2WSH
p2sh_txs[0].wit.vtxinwit[0].scriptWitness.stack = [pad] * 101 + [scripts[0]]
- self.std_node.test_transaction_acceptance(p2sh_txs[0], True, False, b'bad-witness-nonstandard')
- self.test_node.test_transaction_acceptance(p2sh_txs[0], True, True)
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2sh_txs[0], True, False, b'bad-witness-nonstandard')
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2sh_txs[0], True, True)
p2sh_txs[1].wit.vtxinwit[0].scriptWitness.stack = [pad * 81] * 100 + [scripts[1]]
- self.std_node.test_transaction_acceptance(p2sh_txs[1], True, False, b'bad-witness-nonstandard')
- self.test_node.test_transaction_acceptance(p2sh_txs[1], True, True)
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2sh_txs[1], True, False, b'bad-witness-nonstandard')
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2sh_txs[1], True, True)
p2sh_txs[1].wit.vtxinwit[0].scriptWitness.stack = [pad * 80] * 100 + [scripts[1]]
- self.std_node.test_transaction_acceptance(p2sh_txs[1], True, True)
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2sh_txs[1], True, True)
p2sh_txs[2].wit.vtxinwit[0].scriptWitness.stack = [pad, pad, scripts[2]]
- self.test_node.test_transaction_acceptance(p2sh_txs[2], True, True)
- self.std_node.test_transaction_acceptance(p2sh_txs[2], True, True)
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2sh_txs[2], True, True)
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2sh_txs[2], True, True)
p2sh_txs[3].wit.vtxinwit[0].scriptWitness.stack = [pad, pad, pad, scripts[3]]
- self.std_node.test_transaction_acceptance(p2sh_txs[3], True, False, b'bad-witness-nonstandard')
- self.test_node.test_transaction_acceptance(p2sh_txs[3], True, True)
+ test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2sh_txs[3], True, False, b'bad-witness-nonstandard')
+ test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2sh_txs[3], True, True)
self.nodes[0].generate(1) # Mine and clean up the mempool of non-standard node
# Valid but non-standard transactions in a block should be accepted by standard node
@@ -1867,21 +1877,14 @@ class SegWitTest(BitcoinTestFramework):
def run_test(self):
# Setup the p2p connections and start up the network thread.
- self.test_node = TestNode() # sets NODE_WITNESS|NODE_NETWORK
- self.old_node = TestNode() # only NODE_NETWORK
- self.std_node = TestNode() # for testing node1 (fRequireStandard=true)
-
- self.p2p_connections = [self.test_node, self.old_node]
-
- self.connections = []
- self.connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.test_node, services=NODE_NETWORK|NODE_WITNESS))
- self.connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.old_node, services=NODE_NETWORK))
- self.connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1], self.std_node, services=NODE_NETWORK|NODE_WITNESS))
- self.test_node.add_connection(self.connections[0])
- self.old_node.add_connection(self.connections[1])
- self.std_node.add_connection(self.connections[2])
-
- NetworkThread().start() # Start up network handling in another thread
+ # self.test_node sets NODE_WITNESS|NODE_NETWORK
+ self.test_node = self.nodes[0].add_p2p_connection(TestNode(), services=NODE_NETWORK|NODE_WITNESS)
+ # self.old_node sets only NODE_NETWORK
+ self.old_node = self.nodes[0].add_p2p_connection(TestNode(), services=NODE_NETWORK)
+ # self.std_node is for testing node1 (fRequireStandard=true)
+ self.std_node = self.nodes[1].add_p2p_connection(TestNode(), services=NODE_NETWORK|NODE_WITNESS)
+
+ network_thread_start()
# Keep a place to store utxo's that can be used in later tests
self.utxo = []
diff --git a/test/functional/p2p-timeouts.py b/test/functional/p2p-timeouts.py
index 51d4769efc..6d21095cc6 100755
--- a/test/functional/p2p-timeouts.py
+++ b/test/functional/p2p-timeouts.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016 The Bitcoin Core developers
+# Copyright (c) 2016-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 various net timeouts.
@@ -27,8 +27,8 @@ from test_framework.mininode import *
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
-class TestNode(NodeConnCB):
- def on_version(self, conn, message):
+class TestNode(P2PInterface):
+ def on_version(self, message):
# Don't send a verack in response
pass
@@ -39,46 +39,37 @@ class TimeoutsTest(BitcoinTestFramework):
def run_test(self):
# Setup the p2p connections and start up the network thread.
- self.no_verack_node = TestNode() # never send verack
- self.no_version_node = TestNode() # never send version (just ping)
- self.no_send_node = TestNode() # never send anything
+ no_verack_node = self.nodes[0].add_p2p_connection(TestNode())
+ no_version_node = self.nodes[0].add_p2p_connection(TestNode(), send_version=False)
+ no_send_node = self.nodes[0].add_p2p_connection(TestNode(), send_version=False)
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.no_verack_node))
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.no_version_node, send_version=False))
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.no_send_node, send_version=False))
- self.no_verack_node.add_connection(connections[0])
- self.no_version_node.add_connection(connections[1])
- self.no_send_node.add_connection(connections[2])
-
- NetworkThread().start() # Start up network handling in another thread
+ network_thread_start()
sleep(1)
- assert(self.no_verack_node.connected)
- assert(self.no_version_node.connected)
- assert(self.no_send_node.connected)
+ assert no_verack_node.connected
+ assert no_version_node.connected
+ assert no_send_node.connected
- ping_msg = msg_ping()
- connections[0].send_message(ping_msg)
- connections[1].send_message(ping_msg)
+ no_verack_node.send_message(msg_ping())
+ no_version_node.send_message(msg_ping())
sleep(30)
- assert "version" in self.no_verack_node.last_message
+ assert "version" in no_verack_node.last_message
- assert(self.no_verack_node.connected)
- assert(self.no_version_node.connected)
- assert(self.no_send_node.connected)
+ assert no_verack_node.connected
+ assert no_version_node.connected
+ assert no_send_node.connected
- connections[0].send_message(ping_msg)
- connections[1].send_message(ping_msg)
+ no_verack_node.send_message(msg_ping())
+ no_version_node.send_message(msg_ping())
sleep(31)
- assert(not self.no_verack_node.connected)
- assert(not self.no_version_node.connected)
- assert(not self.no_send_node.connected)
+ assert not no_verack_node.connected
+ assert not no_version_node.connected
+ assert not no_send_node.connected
if __name__ == '__main__':
TimeoutsTest().main()
diff --git a/test/functional/p2p-versionbits-warning.py b/test/functional/p2p-versionbits-warning.py
index f9bef2580a..0bfe94622f 100755
--- a/test/functional/p2p-versionbits-warning.py
+++ b/test/functional/p2p-versionbits-warning.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016 The Bitcoin Core developers
+# Copyright (c) 2016-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 version bits warning system.
@@ -23,8 +23,8 @@ WARN_UNKNOWN_RULES_MINED = "Unknown block versions being mined! It's possible un
WARN_UNKNOWN_RULES_ACTIVE = "unknown new rules activated (versionbit {})".format(VB_UNKNOWN_BIT)
VB_PATTERN = re.compile("^Warning.*versionbit")
-class TestNode(NodeConnCB):
- def on_inv(self, conn, message):
+class TestNode(P2PInterface):
+ def on_inv(self, message):
pass
class VersionBitsWarningTest(BitcoinTestFramework):
@@ -64,16 +64,12 @@ class VersionBitsWarningTest(BitcoinTestFramework):
def run_test(self):
# Setup the p2p connection and start up the network thread.
- test_node = TestNode()
+ self.nodes[0].add_p2p_connection(TestNode())
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node))
- test_node.add_connection(connections[0])
-
- NetworkThread().start() # Start up network handling in another thread
+ network_thread_start()
# Test logic begins here
- test_node.wait_for_verack()
+ self.nodes[0].p2p.wait_for_verack()
# 1. Have the node mine one period worth of blocks
self.nodes[0].generate(VB_PERIOD)
@@ -81,7 +77,7 @@ class VersionBitsWarningTest(BitcoinTestFramework):
# 2. Now build one period of blocks on the tip, with < VB_THRESHOLD
# blocks signaling some unknown bit.
nVersion = VB_TOP_BITS | (1<<VB_UNKNOWN_BIT)
- self.send_blocks_with_version(test_node, VB_THRESHOLD-1, nVersion)
+ self.send_blocks_with_version(self.nodes[0].p2p, VB_THRESHOLD-1, nVersion)
# Fill rest of period with regular version blocks
self.nodes[0].generate(VB_PERIOD - VB_THRESHOLD + 1)
@@ -92,7 +88,7 @@ class VersionBitsWarningTest(BitcoinTestFramework):
# 3. Now build one period of blocks with >= VB_THRESHOLD blocks signaling
# some unknown bit
- self.send_blocks_with_version(test_node, VB_THRESHOLD, nVersion)
+ self.send_blocks_with_version(self.nodes[0].p2p, VB_THRESHOLD, nVersion)
self.nodes[0].generate(VB_PERIOD - VB_THRESHOLD)
# Might not get a versionbits-related alert yet, as we should
# have gotten a different alert due to more than 51/100 blocks
diff --git a/test/functional/preciousblock.py b/test/functional/preciousblock.py
index 1466f901c0..960cd0ad12 100755
--- a/test/functional/preciousblock.py
+++ b/test/functional/preciousblock.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Copyright (c) 2015-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 the preciousblock RPC."""
diff --git a/test/functional/prioritise_transaction.py b/test/functional/prioritise_transaction.py
index bb56db9b40..57954ce321 100755
--- a/test/functional/prioritise_transaction.py
+++ b/test/functional/prioritise_transaction.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Copyright (c) 2015-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 the prioritisetransaction mining RPC."""
@@ -15,6 +15,25 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
self.extra_args = [["-printpriority=1"], ["-printpriority=1"]]
def run_test(self):
+ # Test `prioritisetransaction` required parameters
+ assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction)
+ assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction, '')
+ assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction, '', 0)
+
+ # Test `prioritisetransaction` invalid extra parameters
+ assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction, '', 0, 0, 0)
+
+ # Test `prioritisetransaction` invalid `txid`
+ assert_raises_rpc_error(-1, "txid must be hexadecimal string", self.nodes[0].prioritisetransaction, txid='foo', fee_delta=0)
+
+ # Test `prioritisetransaction` invalid `dummy`
+ txid = '1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000'
+ assert_raises_rpc_error(-1, "JSON value is not a number as expected", self.nodes[0].prioritisetransaction, txid, 'foo', 0)
+ assert_raises_rpc_error(-8, "Priority is no longer supported, dummy argument to prioritisetransaction must be 0.", self.nodes[0].prioritisetransaction, txid, 1, 0)
+
+ # Test `prioritisetransaction` invalid `fee_delta`
+ assert_raises_rpc_error(-1, "JSON value is not an integer as expected", self.nodes[0].prioritisetransaction, txid=txid, fee_delta='foo')
+
self.txouts = gen_return_txouts()
self.relayfee = self.nodes[0].getnetworkinfo()['relayfee']
diff --git a/test/functional/proxy_test.py b/test/functional/proxy_test.py
index 81b99d1bf4..2eb1be47a5 100755
--- a/test/functional/proxy_test.py
+++ b/test/functional/proxy_test.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Copyright (c) 2015-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 bitcoind with different proxy configuration.
diff --git a/test/functional/pruning.py b/test/functional/pruning.py
index 0101f61185..49ad7f838c 100755
--- a/test/functional/pruning.py
+++ b/test/functional/pruning.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 the pruning code.
diff --git a/test/functional/rawtransactions.py b/test/functional/rawtransactions.py
index 2777cb9693..fe749adb49 100755
--- a/test/functional/rawtransactions.py
+++ b/test/functional/rawtransactions.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 the rawtransaction RPCs.
@@ -15,11 +15,31 @@ Test the following RPCs:
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
+
+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 = [["-addresstype=legacy"], ["-addresstype=legacy"], ["-addresstype=legacy"]]
def setup_network(self, split=False):
super().setup_network()
@@ -39,6 +59,41 @@ class RawTransactionsTest(BitcoinTestFramework):
self.nodes[0].generate(5)
self.sync_all()
+ # Test `createrawtransaction` required parameters
+ assert_raises_rpc_error(-1, "createrawtransaction", self.nodes[0].createrawtransaction)
+ assert_raises_rpc_error(-1, "createrawtransaction", self.nodes[0].createrawtransaction, [])
+
+ # Test `createrawtransaction` invalid extra parameters
+ assert_raises_rpc_error(-1, "createrawtransaction", self.nodes[0].createrawtransaction, [], {}, 0, False, '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(-8, "txid must be hexadecimal string", self.nodes[0].createrawtransaction, [{}], {})
+ assert_raises_rpc_error(-8, "txid must be hexadecimal string", self.nodes[0].createrawtransaction, [{'txid': 'foo'}], {})
+ assert_raises_rpc_error(-8, "Invalid parameter, missing vout key", self.nodes[0].createrawtransaction, [{'txid': txid}], {})
+ assert_raises_rpc_error(-8, "Invalid parameter, missing vout key", self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': 'foo'}], {})
+ assert_raises_rpc_error(-8, "Invalid parameter, vout must be positive", 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()
+ assert_raises_rpc_error(-3, "Expected type object", self.nodes[0].createrawtransaction, [], 'foo')
+ 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, "Invalid parameter, duplicated address: %s" % address, self.nodes[0].createrawtransaction, [], multidict([(address, 1), (address, 1)]))
+
+ # 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)
+
+ # Test `createrawtransaction` invalid `replaceable`
+ assert_raises_rpc_error(-3, "Expected type bool", self.nodes[0].createrawtransaction, [], {}, 0, 'foo')
+
#########################################
# sendrawtransaction with missing input #
#########################################
@@ -50,6 +105,36 @@ class RawTransactionsTest(BitcoinTestFramework):
# This will raise an exception since there are missing inputs
assert_raises_rpc_error(-25, "Missing inputs", 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(), 1)
+ block1, block2 = self.nodes[2].generate(2)
+ self.sync_all()
+ # We should be able to get the raw transaction by providing the correct block
+ gottx = self.nodes[0].getrawtransaction(tx, True, block1)
+ assert_equal(gottx['txid'], tx)
+ assert_equal(gottx['in_active_chain'], True)
+ # We should not have the 'in_active_chain' flag when we don't provide a block
+ gottx = self.nodes[0].getrawtransaction(tx, True)
+ assert_equal(gottx['txid'], tx)
+ assert 'in_active_chain' not in gottx
+ # We should not get the tx if we provide an unrelated block
+ assert_raises_rpc_error(-5, "No such transaction found", self.nodes[0].getrawtransaction, tx, True, block2)
+ # An invalid block hash should raise the correct errors
+ assert_raises_rpc_error(-8, "parameter 3 must be hexadecimal", self.nodes[0].getrawtransaction, tx, True, True)
+ assert_raises_rpc_error(-8, "parameter 3 must be hexadecimal", self.nodes[0].getrawtransaction, tx, True, "foobar")
+ assert_raises_rpc_error(-8, "parameter 3 must be of length 64", self.nodes[0].getrawtransaction, tx, True, "abcd1234")
+ assert_raises_rpc_error(-5, "Block hash not found", self.nodes[0].getrawtransaction, tx, True, "0000000000000000000000000000000000000000000000000000000000000000")
+ # Undo the blocks and check in_active_chain
+ self.nodes[0].invalidateblock(block1)
+ gottx = self.nodes[0].getrawtransaction(txid=tx, verbose=True, blockhash=block1)
+ assert_equal(gottx['in_active_chain'], False)
+ self.nodes[0].reconsiderblock(block1)
+ assert_equal(self.nodes[0].getbestblockhash(), block2)
+
#########################
# RAW TX MULTISIG TESTS #
#########################
@@ -105,7 +190,7 @@ class RawTransactionsTest(BitcoinTestFramework):
break
bal = self.nodes[0].getbalance()
- inputs = [{ "txid" : txId, "vout" : vout['n'], "scriptPubKey" : vout['scriptPubKey']['hex']}]
+ inputs = [{ "txid" : txId, "vout" : vout['n'], "scriptPubKey" : vout['scriptPubKey']['hex'], "amount" : vout['value']}]
outputs = { self.nodes[0].getnewaddress() : 2.19 }
rawTx = self.nodes[2].createrawtransaction(inputs, outputs)
rawTxPartialSigned = self.nodes[1].signrawtransaction(rawTx, inputs)
@@ -150,7 +235,7 @@ class RawTransactionsTest(BitcoinTestFramework):
break
bal = self.nodes[0].getbalance()
- inputs = [{ "txid" : txId, "vout" : vout['n'], "scriptPubKey" : vout['scriptPubKey']['hex'], "redeemScript" : mSigObjValid['hex']}]
+ inputs = [{ "txid" : txId, "vout" : vout['n'], "scriptPubKey" : vout['scriptPubKey']['hex'], "redeemScript" : mSigObjValid['hex'], "amount" : vout['value']}]
outputs = { self.nodes[0].getnewaddress() : 2.19 }
rawTx2 = self.nodes[2].createrawtransaction(inputs, outputs)
rawTxPartialSigned1 = self.nodes[1].signrawtransaction(rawTx2, inputs)
@@ -169,6 +254,17 @@ class RawTransactionsTest(BitcoinTestFramework):
self.sync_all()
assert_equal(self.nodes[0].getbalance(), bal+Decimal('50.00000000')+Decimal('2.19000000')) #block reward + tx
+ # decoderawtransaction tests
+ # witness transaction
+ encrawtx = "010000000001010000000000000072c1a6a246ae63f74f931e8365e15a089c68d61900000000000000000000ffffffff0100e1f50500000000000000000000"
+ decrawtx = self.nodes[0].decoderawtransaction(encrawtx, True) # decode as witness transaction
+ assert_equal(decrawtx['vout'][0]['value'], Decimal('1.00000000'))
+ assert_raises_rpc_error(-22, 'TX decode failed', self.nodes[0].decoderawtransaction, encrawtx, False) # force decode as non-witness transaction
+ # non-witness transaction
+ encrawtx = "01000000010000000000000072c1a6a246ae63f74f931e8365e15a089c68d61900000000000000000000ffffffff0100e1f505000000000000000000"
+ decrawtx = self.nodes[0].decoderawtransaction(encrawtx, False) # decode as non-witness transaction
+ assert_equal(decrawtx['vout'][0]['value'], Decimal('1.00000000'))
+
# getrawtransaction tests
# 1. valid parameters - only supply txid
txHash = rawTx["hash"]
@@ -188,13 +284,13 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(self.nodes[0].getrawtransaction(txHash, True)["hex"], rawTxSigned['hex'])
# 6. invalid parameters - supply txid and string "Flase"
- assert_raises_rpc_error(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, "Flase")
+ assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txHash, "Flase")
# 7. invalid parameters - supply txid and empty array
- assert_raises_rpc_error(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, [])
+ assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txHash, [])
# 8. invalid parameters - supply txid and empty dict
- assert_raises_rpc_error(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, {})
+ assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txHash, {})
inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : 1000}]
outputs = { self.nodes[0].getnewaddress() : 1 }
diff --git a/test/functional/receivedby.py b/test/functional/receivedby.py
index db6fc86b82..1f2b3c8aa7 100755
--- a/test/functional/receivedby.py
+++ b/test/functional/receivedby.py
@@ -1,99 +1,85 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 the listreceivedbyaddress RPC."""
+from decimal import Decimal
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import *
-
-def get_sub_array_from_array(object_array, to_match):
- '''
- Finds and returns a sub array from an array of arrays.
- to_match should be a unique idetifier of a sub array
- '''
- for item in object_array:
- all_match = True
- for key,value in to_match.items():
- if item[key] != value:
- all_match = False
- if not all_match:
- continue
- return item
- return []
+from test_framework.util import (assert_array_result,
+ assert_equal,
+ assert_raises_rpc_error,
+ )
class ReceivedByTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
- self.enable_mocktime()
def run_test(self):
- '''
- listreceivedbyaddress Test
- '''
+ # Generate block to get out of IBD
+ self.nodes[0].generate(1)
+
+ self.log.info("listreceivedbyaddress Test")
+
# Send from node 0 to 1
addr = self.nodes[1].getnewaddress()
txid = self.nodes[0].sendtoaddress(addr, 0.1)
self.sync_all()
- #Check not listed in listreceivedbyaddress because has 0 confirmations
+ # Check not listed in listreceivedbyaddress because has 0 confirmations
assert_array_result(self.nodes[1].listreceivedbyaddress(),
- {"address":addr},
- { },
- True)
- #Bury Tx under 10 block so it will be returned by listreceivedbyaddress
+ {"address": addr},
+ {},
+ True)
+ # Bury Tx under 10 block so it will be returned by listreceivedbyaddress
self.nodes[1].generate(10)
self.sync_all()
assert_array_result(self.nodes[1].listreceivedbyaddress(),
- {"address":addr},
- {"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]})
- #With min confidence < 10
+ {"address": addr},
+ {"address": addr, "account": "", "amount": Decimal("0.1"), "confirmations": 10, "txids": [txid, ]})
+ # With min confidence < 10
assert_array_result(self.nodes[1].listreceivedbyaddress(5),
- {"address":addr},
- {"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]})
- #With min confidence > 10, should not find Tx
- assert_array_result(self.nodes[1].listreceivedbyaddress(11),{"address":addr},{ },True)
+ {"address": addr},
+ {"address": addr, "account": "", "amount": Decimal("0.1"), "confirmations": 10, "txids": [txid, ]})
+ # With min confidence > 10, should not find Tx
+ assert_array_result(self.nodes[1].listreceivedbyaddress(11), {"address": addr}, {}, True)
- #Empty Tx
+ # Empty Tx
addr = self.nodes[1].getnewaddress()
- assert_array_result(self.nodes[1].listreceivedbyaddress(0,True),
- {"address":addr},
- {"address":addr, "account":"", "amount":0, "confirmations":0, "txids":[]})
+ assert_array_result(self.nodes[1].listreceivedbyaddress(0, True),
+ {"address": addr},
+ {"address": addr, "account": "", "amount": 0, "confirmations": 0, "txids": []})
+
+ self.log.info("getreceivedbyaddress Test")
- '''
- getreceivedbyaddress Test
- '''
# Send from node 0 to 1
addr = self.nodes[1].getnewaddress()
txid = self.nodes[0].sendtoaddress(addr, 0.1)
self.sync_all()
- #Check balance is 0 because of 0 confirmations
+ # Check balance is 0 because of 0 confirmations
balance = self.nodes[1].getreceivedbyaddress(addr)
- if balance != Decimal("0.0"):
- raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance))
+ assert_equal(balance, Decimal("0.0"))
- #Check balance is 0.1
- balance = self.nodes[1].getreceivedbyaddress(addr,0)
- if balance != Decimal("0.1"):
- raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance))
+ # Check balance is 0.1
+ balance = self.nodes[1].getreceivedbyaddress(addr, 0)
+ assert_equal(balance, Decimal("0.1"))
- #Bury Tx under 10 block so it will be returned by the default getreceivedbyaddress
+ # Bury Tx under 10 block so it will be returned by the default getreceivedbyaddress
self.nodes[1].generate(10)
self.sync_all()
balance = self.nodes[1].getreceivedbyaddress(addr)
- if balance != Decimal("0.1"):
- raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance))
+ assert_equal(balance, Decimal("0.1"))
+
+ # Trying to getreceivedby for an address the wallet doesn't own should return an error
+ assert_raises_rpc_error(-4, "Address not found in wallet", self.nodes[0].getreceivedbyaddress, addr)
+
+ self.log.info("listreceivedbyaccount + getreceivedbyaccount Test")
- '''
- listreceivedbyaccount + getreceivedbyaccount Test
- '''
- #set pre-state
+ # set pre-state
addrArr = self.nodes[1].getnewaddress()
account = self.nodes[1].getaccount(addrArr)
- received_by_account_json = get_sub_array_from_array(self.nodes[1].listreceivedbyaccount(),{"account":account})
- if len(received_by_account_json) == 0:
- raise AssertionError("No accounts found in node")
+ received_by_account_json = [r for r in self.nodes[1].listreceivedbyaccount() if r["account"] == account][0]
balance_by_account = self.nodes[1].getreceivedbyaccount(account)
txid = self.nodes[0].sendtoaddress(addr, 0.1)
@@ -101,40 +87,34 @@ class ReceivedByTest(BitcoinTestFramework):
# listreceivedbyaccount should return received_by_account_json because of 0 confirmations
assert_array_result(self.nodes[1].listreceivedbyaccount(),
- {"account":account},
- received_by_account_json)
+ {"account": account},
+ received_by_account_json)
# getreceivedbyaddress should return same balance because of 0 confirmations
balance = self.nodes[1].getreceivedbyaccount(account)
- if balance != balance_by_account:
- raise AssertionError("Wrong balance returned by getreceivedbyaccount, %0.2f"%(balance))
+ assert_equal(balance, balance_by_account)
self.nodes[1].generate(10)
self.sync_all()
# listreceivedbyaccount should return updated account balance
assert_array_result(self.nodes[1].listreceivedbyaccount(),
- {"account":account},
- {"account":received_by_account_json["account"], "amount":(received_by_account_json["amount"] + Decimal("0.1"))})
+ {"account": account},
+ {"account": received_by_account_json["account"], "amount": (received_by_account_json["amount"] + Decimal("0.1"))})
# getreceivedbyaddress should return updates balance
balance = self.nodes[1].getreceivedbyaccount(account)
- if balance != balance_by_account + Decimal("0.1"):
- raise AssertionError("Wrong balance returned by getreceivedbyaccount, %0.2f"%(balance))
+ assert_equal(balance, balance_by_account + Decimal("0.1"))
- #Create a new account named "mynewaccount" that has a 0 balance
+ # Create a new account named "mynewaccount" that has a 0 balance
self.nodes[1].getaccountaddress("mynewaccount")
- received_by_account_json = get_sub_array_from_array(self.nodes[1].listreceivedbyaccount(0,True),{"account":"mynewaccount"})
- if len(received_by_account_json) == 0:
- raise AssertionError("No accounts found in node")
+ received_by_account_json = [r for r in self.nodes[1].listreceivedbyaccount(0, True) if r["account"] == "mynewaccount"][0]
# Test includeempty of listreceivedbyaccount
- if received_by_account_json["amount"] != Decimal("0.0"):
- raise AssertionError("Wrong balance returned by listreceivedbyaccount, %0.2f"%(received_by_account_json["amount"]))
+ assert_equal(received_by_account_json["amount"], Decimal("0.0"))
# Test getreceivedbyaccount for 0 amount accounts
balance = self.nodes[1].getreceivedbyaccount("mynewaccount")
- if balance != Decimal("0.0"):
- raise AssertionError("Wrong balance returned by getreceivedbyaccount, %0.2f"%(balance))
+ assert_equal(balance, Decimal("0.0"))
if __name__ == '__main__':
ReceivedByTest().main()
diff --git a/test/functional/reindex.py b/test/functional/reindex.py
index 1f684a1afe..ac67e6e9ba 100755
--- a/test/functional/reindex.py
+++ b/test/functional/reindex.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 running bitcoind with -reindex and -reindex-chainstate options.
diff --git a/test/functional/replace-by-fee.py b/test/functional/replace-by-fee.py
index 269d57775c..6b7ab0f43e 100755
--- a/test/functional/replace-by-fee.py
+++ b/test/functional/replace-by-fee.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 the RBF code."""
@@ -72,8 +72,14 @@ class ReplaceByFeeTest(BitcoinTestFramework):
["-mempoolreplacement=0"]]
def run_test(self):
+ # Leave IBD
+ self.nodes[0].generate(1)
+
make_utxo(self.nodes[0], 1*COIN)
+ # Ensure nodes are synced
+ self.sync_all()
+
self.log.info("Running test simple doublespend...")
self.test_simple_doublespend()
@@ -110,13 +116,18 @@ class ReplaceByFeeTest(BitcoinTestFramework):
"""Simple doublespend"""
tx0_outpoint = make_utxo(self.nodes[0], int(1.1*COIN))
+ # make_utxo may have generated a bunch of blocks, so we need to sync
+ # before we can spend the coins generated, or else the resulting
+ # transactions might not be accepted by our peers.
+ self.sync_all()
+
tx1a = CTransaction()
tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)]
tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))]
tx1a_hex = txToHex(tx1a)
tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True)
- self.sync_all([self.nodes])
+ self.sync_all()
# Should fail because we haven't changed the fee
tx1b = CTransaction()
diff --git a/test/functional/rest.py b/test/functional/rest.py
index 437111a4d7..9006e27cbe 100755
--- a/test/functional/rest.py
+++ b/test/functional/rest.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 the REST API."""
diff --git a/test/functional/rpcbind_test.py b/test/functional/rpcbind_test.py
index 0e8c3fa209..05433c7e24 100755
--- a/test/functional/rpcbind_test.py
+++ b/test/functional/rpcbind_test.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 running bitcoind with the -rpcbind and -rpcallowip options."""
diff --git a/test/functional/rpcnamedargs.py b/test/functional/rpcnamedargs.py
index c47212bddb..97bee39614 100755
--- a/test/functional/rpcnamedargs.py
+++ b/test/functional/rpcnamedargs.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016 The Bitcoin Core developers
+# Copyright (c) 2016-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 using named arguments for RPCs."""
diff --git a/test/functional/segwit.py b/test/functional/segwit.py
index f03eda5bb2..7d5c760ad9 100755
--- a/test/functional/segwit.py
+++ b/test/functional/segwit.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016 The Bitcoin Core developers
+# Copyright (c) 2016-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 the SegWit changeover logic."""
@@ -39,9 +39,10 @@ class SegWitTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 3
- self.extra_args = [["-walletprematurewitness", "-rpcserialversion=0"],
- ["-blockversion=4", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness", "-rpcserialversion=1"],
- ["-blockversion=536870915", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness"]]
+ # This test tests SegWit both pre and post-activation, so use the normal BIP9 activation.
+ self.extra_args = [["-walletprematurewitness", "-rpcserialversion=0", "-vbparams=segwit:0:999999999999", "-addresstype=legacy"],
+ ["-blockversion=4", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness", "-rpcserialversion=1", "-vbparams=segwit:0:999999999999", "-addresstype=legacy"],
+ ["-blockversion=536870915", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness", "-vbparams=segwit:0:999999999999", "-addresstype=legacy"]]
def setup_network(self):
super().setup_network()
@@ -96,9 +97,9 @@ class SegWitTest(BitcoinTestFramework):
self.pubkey.append(self.nodes[i].validateaddress(newaddress)["pubkey"])
multiaddress = self.nodes[i].addmultisigaddress(1, [self.pubkey[-1]])
multiscript = CScript([OP_1, hex_str_to_bytes(self.pubkey[-1]), OP_1, OP_CHECKMULTISIG])
- p2sh_addr = self.nodes[i].addwitnessaddress(newaddress, True)
+ p2sh_addr = self.nodes[i].addwitnessaddress(newaddress)
bip173_addr = self.nodes[i].addwitnessaddress(newaddress, False)
- p2sh_ms_addr = self.nodes[i].addwitnessaddress(multiaddress, True)
+ p2sh_ms_addr = self.nodes[i].addwitnessaddress(multiaddress)
bip173_ms_addr = self.nodes[i].addwitnessaddress(multiaddress, False)
assert_equal(p2sh_addr, key_to_p2sh_p2wpkh(self.pubkey[-1]))
assert_equal(bip173_addr, key_to_p2wpkh(self.pubkey[-1]))
@@ -317,8 +318,10 @@ class SegWitTest(BitcoinTestFramework):
[p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
# normal P2PKH and P2PK with compressed keys should always be spendable
spendable_anytime.extend([p2pkh, p2pk])
- # P2SH_P2PK, P2SH_P2PKH, and witness with compressed keys are spendable after direct importaddress
- spendable_after_importaddress.extend([p2wpkh, p2sh_p2wpkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
+ # P2SH_P2PK, P2SH_P2PKH with compressed keys are spendable after direct importaddress
+ spendable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
+ # P2WPKH and P2SH_P2WPKH with compressed keys should always be spendable
+ spendable_anytime.extend([p2wpkh, p2sh_p2wpkh])
for i in uncompressed_spendable_address:
v = self.nodes[0].validateaddress(i)
@@ -334,7 +337,7 @@ class SegWitTest(BitcoinTestFramework):
spendable_anytime.extend([p2pkh, p2pk])
# P2SH_P2PK and P2SH_P2PKH are spendable after direct importaddress
spendable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh])
- # witness with uncompressed keys are never seen
+ # Witness output types with uncompressed keys are never seen
unseen_anytime.extend([p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
for i in compressed_solvable_address:
@@ -345,10 +348,10 @@ class SegWitTest(BitcoinTestFramework):
solvable_after_importaddress.extend([bare, p2sh, p2wsh, p2sh_p2wsh])
else:
[p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
- # normal P2PKH and P2PK with compressed keys should always be seen
- solvable_anytime.extend([p2pkh, p2pk])
- # P2SH_P2PK, P2SH_P2PKH, and witness with compressed keys are seen after direct importaddress
- solvable_after_importaddress.extend([p2wpkh, p2sh_p2wpkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
+ # normal P2PKH, P2PK, P2WPKH and P2SH_P2WPKH with compressed keys should always be seen
+ solvable_anytime.extend([p2pkh, p2pk, p2wpkh, p2sh_p2wpkh])
+ # P2SH_P2PK, P2SH_P2PKH with compressed keys are seen after direct importaddress
+ solvable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
for i in uncompressed_solvable_address:
v = self.nodes[0].validateaddress(i)
@@ -364,7 +367,7 @@ class SegWitTest(BitcoinTestFramework):
solvable_anytime.extend([p2pkh, p2pk])
# P2SH_P2PK, P2SH_P2PKH with uncompressed keys are seen after direct importaddress
solvable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh])
- # witness with uncompressed keys are never seen
+ # Witness output types with uncompressed keys are never seen
unseen_anytime.extend([p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
op1 = CScript([OP_1])
@@ -457,6 +460,8 @@ class SegWitTest(BitcoinTestFramework):
spendable_after_addwitnessaddress = [] # These outputs should be seen after importaddress
solvable_after_addwitnessaddress=[] # These outputs should be seen after importaddress but not spendable
unseen_anytime = [] # These outputs should never be seen
+ solvable_anytime = [] # These outputs should be solvable after importpubkey
+ unseen_anytime = [] # These outputs should never be seen
uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], compressed_spendable_address[0]]))
uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], uncompressed_spendable_address[0]]))
@@ -475,9 +480,8 @@ class SegWitTest(BitcoinTestFramework):
premature_witaddress.append(script_to_p2sh(p2wsh))
else:
[p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
- # P2WPKH, P2SH_P2WPKH are spendable after addwitnessaddress
- spendable_after_addwitnessaddress.extend([p2wpkh, p2sh_p2wpkh])
- premature_witaddress.append(script_to_p2sh(p2wpkh))
+ # P2WPKH, P2SH_P2WPKH are always spendable
+ spendable_anytime.extend([p2wpkh, p2sh_p2wpkh])
for i in uncompressed_spendable_address + uncompressed_solvable_address:
v = self.nodes[0].validateaddress(i)
@@ -499,10 +503,11 @@ class SegWitTest(BitcoinTestFramework):
premature_witaddress.append(script_to_p2sh(p2wsh))
else:
[p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
- # P2SH_P2PK, P2SH_P2PKH with compressed keys are seen after addwitnessaddress
- solvable_after_addwitnessaddress.extend([p2wpkh, p2sh_p2wpkh])
- premature_witaddress.append(script_to_p2sh(p2wpkh))
+ # P2SH_P2PK, P2SH_P2PKH with compressed keys are always solvable
+ solvable_anytime.extend([p2wpkh, p2sh_p2wpkh])
+ self.mine_and_test_listunspent(spendable_anytime, 2)
+ self.mine_and_test_listunspent(solvable_anytime, 1)
self.mine_and_test_listunspent(spendable_after_addwitnessaddress + solvable_after_addwitnessaddress + unseen_anytime, 0)
# addwitnessaddress should refuse to return a witness address if an uncompressed key is used
@@ -519,8 +524,8 @@ class SegWitTest(BitcoinTestFramework):
witaddress = self.nodes[0].addwitnessaddress(i)
assert_equal(witaddress, self.nodes[0].addwitnessaddress(witaddress))
- spendable_txid.append(self.mine_and_test_listunspent(spendable_after_addwitnessaddress, 2))
- solvable_txid.append(self.mine_and_test_listunspent(solvable_after_addwitnessaddress, 1))
+ spendable_txid.append(self.mine_and_test_listunspent(spendable_after_addwitnessaddress + spendable_anytime, 2))
+ solvable_txid.append(self.mine_and_test_listunspent(solvable_after_addwitnessaddress + solvable_anytime, 1))
self.mine_and_test_listunspent(unseen_anytime, 0)
# Check that createrawtransaction/decoderawtransaction with non-v0 Bech32 works
diff --git a/test/functional/sendheaders.py b/test/functional/sendheaders.py
index fe577dc20a..8869aeaaea 100755
--- a/test/functional/sendheaders.py
+++ b/test/functional/sendheaders.py
@@ -1,14 +1,27 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 behavior of headers messages to announce blocks.
-Setup:
+Setup:
-- Two nodes, two p2p connections to node0. One p2p connection should only ever
- receive inv's (omitted from testing description below, this is our control).
- Second node is used for creating reorgs.
+- Two nodes:
+ - node0 is the node-under-test. We create two p2p connections to it. The
+ first p2p connection is a control and should only ever receive inv's. The
+ second p2p connection tests the headers sending logic.
+ - node1 is used to create reorgs.
+
+test_null_locators
+==================
+
+Sends two getheaders requests with null locator values. First request's hashstop
+value refers to validated block, while second request's hashstop value refers to
+a block which hasn't been validated. Verifies only the first request returns
+headers.
+
+test_nonnull_locators
+=====================
Part 1: No headers announcements before "sendheaders"
a. node mines a block [expect: inv]
@@ -72,159 +85,197 @@ d. Announce 49 headers that don't connect.
e. Announce one more that doesn't connect.
Expect: disconnect.
"""
-
-from test_framework.mininode import *
-from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import *
from test_framework.blocktools import create_block, create_coinbase
+from test_framework.mininode import (
+ CBlockHeader,
+ CInv,
+ NODE_WITNESS,
+ network_thread_start,
+ P2PInterface,
+ mininode_lock,
+ msg_block,
+ msg_getblocks,
+ msg_getdata,
+ msg_getheaders,
+ msg_headers,
+ msg_inv,
+ msg_sendheaders,
+)
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ sync_blocks,
+ wait_until,
+)
+DIRECT_FETCH_RESPONSE_TIME = 0.05
-direct_fetch_response_time = 0.05
-
-class TestNode(NodeConnCB):
+class BaseNode(P2PInterface):
def __init__(self):
super().__init__()
+
self.block_announced = False
self.last_blockhash_announced = None
- def clear_last_announcement(self):
- with mininode_lock:
- self.block_announced = False
- self.last_message.pop("inv", None)
- self.last_message.pop("headers", None)
-
- # Request data for a list of block hashes
- def get_data(self, block_hashes):
+ def send_get_data(self, block_hashes):
+ """Request data for a list of block hashes."""
msg = msg_getdata()
for x in block_hashes:
msg.inv.append(CInv(2, x))
- self.connection.send_message(msg)
+ self.send_message(msg)
- def get_headers(self, locator, hashstop):
+ def send_get_headers(self, locator, hashstop):
msg = msg_getheaders()
msg.locator.vHave = locator
msg.hashstop = hashstop
- self.connection.send_message(msg)
+ self.send_message(msg)
def send_block_inv(self, blockhash):
msg = msg_inv()
msg.inv = [CInv(2, blockhash)]
- self.connection.send_message(msg)
+ self.send_message(msg)
+
+ def send_header_for_blocks(self, new_blocks):
+ headers_message = msg_headers()
+ headers_message.headers = [CBlockHeader(b) for b in new_blocks]
+ self.send_message(headers_message)
- def on_inv(self, conn, message):
+ def send_getblocks(self, locator):
+ getblocks_message = msg_getblocks()
+ getblocks_message.locator.vHave = locator
+ self.send_message(getblocks_message)
+
+ def wait_for_getdata(self, hash_list, timeout=60):
+ if hash_list == []:
+ return
+
+ test_function = lambda: "getdata" in self.last_message and [x.hash for x in self.last_message["getdata"].inv] == hash_list
+ wait_until(test_function, timeout=timeout, lock=mininode_lock)
+
+ def wait_for_block_announcement(self, block_hash, timeout=60):
+ test_function = lambda: self.last_blockhash_announced == block_hash
+ wait_until(test_function, timeout=timeout, lock=mininode_lock)
+
+ def on_inv(self, message):
self.block_announced = True
self.last_blockhash_announced = message.inv[-1].hash
- def on_headers(self, conn, message):
+ def on_headers(self, message):
if len(message.headers):
self.block_announced = True
message.headers[-1].calc_sha256()
self.last_blockhash_announced = message.headers[-1].sha256
- # Test whether the last announcement we received had the
- # right header or the right inv
- # inv and headers should be lists of block hashes
+ def clear_last_announcement(self):
+ with mininode_lock:
+ self.block_announced = False
+ self.last_message.pop("inv", None)
+ self.last_message.pop("headers", None)
+
def check_last_announcement(self, headers=None, inv=None):
- expect_headers = headers if headers != None else []
- expect_inv = inv if inv != None else []
+ """Test whether the last announcement received had the right header or the right inv.
+
+ inv and headers should be lists of block hashes."""
+
test_function = lambda: self.block_announced
wait_until(test_function, timeout=60, lock=mininode_lock)
+
with mininode_lock:
self.block_announced = False
- success = True
compare_inv = []
if "inv" in self.last_message:
compare_inv = [x.hash for x in self.last_message["inv"].inv]
- if compare_inv != expect_inv:
- success = False
+ if inv is not None:
+ assert_equal(compare_inv, inv)
- hash_headers = []
+ compare_headers = []
if "headers" in self.last_message:
- # treat headers as a list of block hashes
- hash_headers = [ x.sha256 for x in self.last_message["headers"].headers ]
- if hash_headers != expect_headers:
- success = False
+ compare_headers = [x.sha256 for x in self.last_message["headers"].headers]
+ if headers is not None:
+ assert_equal(compare_headers, headers)
self.last_message.pop("inv", None)
self.last_message.pop("headers", None)
- return success
-
- def wait_for_getdata(self, hash_list, timeout=60):
- if hash_list == []:
- return
-
- test_function = lambda: "getdata" in self.last_message and [x.hash for x in self.last_message["getdata"].inv] == hash_list
- wait_until(test_function, timeout=timeout, lock=mininode_lock)
- return
-
- def wait_for_block_announcement(self, block_hash, timeout=60):
- test_function = lambda: self.last_blockhash_announced == block_hash
- wait_until(test_function, timeout=timeout, lock=mininode_lock)
- return
-
- def send_header_for_blocks(self, new_blocks):
- headers_message = msg_headers()
- headers_message.headers = [ CBlockHeader(b) for b in new_blocks ]
- self.send_message(headers_message)
-
- def send_getblocks(self, locator):
- getblocks_message = msg_getblocks()
- getblocks_message.locator.vHave = locator
- self.send_message(getblocks_message)
class SendHeadersTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 2
- # mine count blocks and return the new tip
def mine_blocks(self, count):
+ """Mine count blocks and return the new tip."""
+
# Clear out last block announcement from each p2p listener
- [ x.clear_last_announcement() for x in self.p2p_connections ]
+ [x.clear_last_announcement() for x in self.nodes[0].p2ps]
self.nodes[0].generate(count)
return int(self.nodes[0].getbestblockhash(), 16)
- # mine a reorg that invalidates length blocks (replacing them with
- # length+1 blocks).
- # Note: we clear the state of our p2p connections after the
- # to-be-reorged-out blocks are mined, so that we don't break later tests.
- # return the list of block hashes newly mined
def mine_reorg(self, length):
- self.nodes[0].generate(length) # make sure all invalidated blocks are node0's
+ """Mine a reorg that invalidates length blocks (replacing them with # length+1 blocks).
+
+ Note: we clear the state of our p2p connections after the
+ to-be-reorged-out blocks are mined, so that we don't break later tests.
+ return the list of block hashes newly mined."""
+
+ self.nodes[0].generate(length) # make sure all invalidated blocks are node0's
sync_blocks(self.nodes, wait=0.1)
- for x in self.p2p_connections:
+ for x in self.nodes[0].p2ps:
x.wait_for_block_announcement(int(self.nodes[0].getbestblockhash(), 16))
x.clear_last_announcement()
tip_height = self.nodes[1].getblockcount()
- hash_to_invalidate = self.nodes[1].getblockhash(tip_height-(length-1))
+ hash_to_invalidate = self.nodes[1].getblockhash(tip_height - (length - 1))
self.nodes[1].invalidateblock(hash_to_invalidate)
- all_hashes = self.nodes[1].generate(length+1) # Must be longer than the orig chain
+ all_hashes = self.nodes[1].generate(length + 1) # Must be longer than the orig chain
sync_blocks(self.nodes, wait=0.1)
return [int(x, 16) for x in all_hashes]
def run_test(self):
# Setup the p2p connections and start up the network thread.
- inv_node = TestNode()
- test_node = TestNode()
-
- self.p2p_connections = [inv_node, test_node]
-
- connections = []
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], inv_node))
- # Set nServices to 0 for test_node, so no block download will occur outside of
- # direct fetching
- connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node, services=0))
- inv_node.add_connection(connections[0])
- test_node.add_connection(connections[1])
+ inv_node = self.nodes[0].add_p2p_connection(BaseNode())
+ # Make sure NODE_NETWORK is not set for test_node, so no block download
+ # will occur outside of direct fetching
+ test_node = self.nodes[0].add_p2p_connection(BaseNode(), services=NODE_WITNESS)
- NetworkThread().start() # Start up network handling in another thread
+ network_thread_start()
# Test logic begins here
inv_node.wait_for_verack()
test_node.wait_for_verack()
+ # Ensure verack's have been processed by our peer
+ inv_node.sync_with_ping()
+ test_node.sync_with_ping()
+
+ self.test_null_locators(test_node, inv_node)
+ self.test_nonnull_locators(test_node, inv_node)
+
+ def test_null_locators(self, test_node, inv_node):
+ tip = self.nodes[0].getblockheader(self.nodes[0].generate(1)[0])
+ tip_hash = int(tip["hash"], 16)
+
+ inv_node.check_last_announcement(inv=[tip_hash], headers=[])
+ test_node.check_last_announcement(inv=[tip_hash], headers=[])
+
+ self.log.info("Verify getheaders with null locator and valid hashstop returns headers.")
+ test_node.clear_last_announcement()
+ test_node.send_get_headers(locator=[], hashstop=tip_hash)
+ test_node.check_last_announcement(headers=[tip_hash])
+
+ self.log.info("Verify getheaders with null locator and invalid hashstop does not return headers.")
+ block = create_block(int(tip["hash"], 16), create_coinbase(tip["height"] + 1), tip["mediantime"] + 1)
+ block.solve()
+ test_node.send_header_for_blocks([block])
+ test_node.clear_last_announcement()
+ test_node.send_get_headers(locator=[], hashstop=int(block.hash, 16))
+ test_node.sync_with_ping()
+ assert_equal(test_node.block_announced, False)
+ inv_node.clear_last_announcement()
+ test_node.send_message(msg_block(block))
+ inv_node.check_last_announcement(inv=[int(block.hash, 16)], headers=[])
+
+ def test_nonnull_locators(self, test_node, inv_node):
tip = int(self.nodes[0].getbestblockhash(), 16)
# PART 1
@@ -233,30 +284,30 @@ class SendHeadersTest(BitcoinTestFramework):
for i in range(4):
old_tip = tip
tip = self.mine_blocks(1)
- assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
- assert_equal(test_node.check_last_announcement(inv=[tip]), True)
+ inv_node.check_last_announcement(inv=[tip], headers=[])
+ test_node.check_last_announcement(inv=[tip], headers=[])
# Try a few different responses; none should affect next announcement
if i == 0:
# first request the block
- test_node.get_data([tip])
+ test_node.send_get_data([tip])
test_node.wait_for_block(tip)
elif i == 1:
# next try requesting header and block
- test_node.get_headers(locator=[old_tip], hashstop=tip)
- test_node.get_data([tip])
+ test_node.send_get_headers(locator=[old_tip], hashstop=tip)
+ test_node.send_get_data([tip])
test_node.wait_for_block(tip)
- test_node.clear_last_announcement() # since we requested headers...
+ test_node.clear_last_announcement() # since we requested headers...
elif i == 2:
# this time announce own block via headers
height = self.nodes[0].getblockcount()
last_time = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['time']
block_time = last_time + 1
- new_block = create_block(tip, create_coinbase(height+1), block_time)
+ new_block = create_block(tip, create_coinbase(height + 1), block_time)
new_block.solve()
test_node.send_header_for_blocks([new_block])
test_node.wait_for_getdata([new_block.sha256])
test_node.send_message(msg_block(new_block))
- test_node.sync_with_ping() # make sure this block is processed
+ test_node.sync_with_ping() # make sure this block is processed
inv_node.clear_last_announcement()
test_node.clear_last_announcement()
@@ -267,15 +318,15 @@ class SendHeadersTest(BitcoinTestFramework):
# commence and keep working.
test_node.send_message(msg_sendheaders())
prev_tip = int(self.nodes[0].getbestblockhash(), 16)
- test_node.get_headers(locator=[prev_tip], hashstop=0)
+ test_node.send_get_headers(locator=[prev_tip], hashstop=0)
test_node.sync_with_ping()
# Now that we've synced headers, headers announcements should work
tip = self.mine_blocks(1)
- assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
- assert_equal(test_node.check_last_announcement(headers=[tip]), True)
+ inv_node.check_last_announcement(inv=[tip], headers=[])
+ test_node.check_last_announcement(headers=[tip])
- height = self.nodes[0].getblockcount()+1
+ height = self.nodes[0].getblockcount() + 1
block_time += 10 # Advance far enough ahead
for i in range(10):
# Mine i blocks, and alternate announcing either via
@@ -284,7 +335,7 @@ class SendHeadersTest(BitcoinTestFramework):
# with block header, even though the blocks are never requested
for j in range(2):
blocks = []
- for b in range(i+1):
+ for b in range(i + 1):
blocks.append(create_block(tip, create_coinbase(height), block_time))
blocks[-1].solve()
tip = blocks[-1].sha256
@@ -298,7 +349,7 @@ class SendHeadersTest(BitcoinTestFramework):
test_node.send_header_for_blocks(blocks)
# Test that duplicate inv's won't result in duplicate
# getdata requests, or duplicate headers announcements
- [ inv_node.send_block_inv(x.sha256) for x in blocks ]
+ [inv_node.send_block_inv(x.sha256) for x in blocks]
test_node.wait_for_getdata([x.sha256 for x in blocks])
inv_node.sync_with_ping()
else:
@@ -309,7 +360,7 @@ class SendHeadersTest(BitcoinTestFramework):
# getdata requests (the check is further down)
inv_node.send_header_for_blocks(blocks)
inv_node.sync_with_ping()
- [ test_node.send_message(msg_block(x)) for x in blocks ]
+ [test_node.send_message(msg_block(x)) for x in blocks]
test_node.sync_with_ping()
inv_node.sync_with_ping()
# This block should not be announced to the inv node (since it also
@@ -317,8 +368,8 @@ class SendHeadersTest(BitcoinTestFramework):
assert "inv" not in inv_node.last_message
assert "headers" not in inv_node.last_message
tip = self.mine_blocks(1)
- assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
- assert_equal(test_node.check_last_announcement(headers=[tip]), True)
+ inv_node.check_last_announcement(inv=[tip], headers=[])
+ test_node.check_last_announcement(headers=[tip])
height += 1
block_time += 1
@@ -332,16 +383,16 @@ class SendHeadersTest(BitcoinTestFramework):
# First try mining a reorg that can propagate with header announcement
new_block_hashes = self.mine_reorg(length=7)
tip = new_block_hashes[-1]
- assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
- assert_equal(test_node.check_last_announcement(headers=new_block_hashes), True)
+ inv_node.check_last_announcement(inv=[tip], headers=[])
+ test_node.check_last_announcement(headers=new_block_hashes)
- block_time += 8
+ block_time += 8
# Mine a too-large reorg, which should be announced with a single inv
new_block_hashes = self.mine_reorg(length=8)
tip = new_block_hashes[-1]
- assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
- assert_equal(test_node.check_last_announcement(inv=[tip]), True)
+ inv_node.check_last_announcement(inv=[tip], headers=[])
+ test_node.check_last_announcement(inv=[tip], headers=[])
block_time += 9
@@ -349,42 +400,42 @@ class SendHeadersTest(BitcoinTestFramework):
fork_point = int(fork_point, 16)
# Use getblocks/getdata
- test_node.send_getblocks(locator = [fork_point])
- assert_equal(test_node.check_last_announcement(inv=new_block_hashes), True)
- test_node.get_data(new_block_hashes)
+ test_node.send_getblocks(locator=[fork_point])
+ test_node.check_last_announcement(inv=new_block_hashes, headers=[])
+ test_node.send_get_data(new_block_hashes)
test_node.wait_for_block(new_block_hashes[-1])
for i in range(3):
# Mine another block, still should get only an inv
tip = self.mine_blocks(1)
- assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
- assert_equal(test_node.check_last_announcement(inv=[tip]), True)
+ inv_node.check_last_announcement(inv=[tip], headers=[])
+ test_node.check_last_announcement(inv=[tip], headers=[])
if i == 0:
# Just get the data -- shouldn't cause headers announcements to resume
- test_node.get_data([tip])
+ test_node.send_get_data([tip])
test_node.wait_for_block(tip)
elif i == 1:
# Send a getheaders message that shouldn't trigger headers announcements
# to resume (best header sent will be too old)
- test_node.get_headers(locator=[fork_point], hashstop=new_block_hashes[1])
- test_node.get_data([tip])
+ test_node.send_get_headers(locator=[fork_point], hashstop=new_block_hashes[1])
+ test_node.send_get_data([tip])
test_node.wait_for_block(tip)
elif i == 2:
- test_node.get_data([tip])
+ test_node.send_get_data([tip])
test_node.wait_for_block(tip)
# This time, try sending either a getheaders to trigger resumption
- # of headers announcements, or mine a new block and inv it, also
+ # of headers announcements, or mine a new block and inv it, also
# triggering resumption of headers announcements.
if j == 0:
- test_node.get_headers(locator=[tip], hashstop=0)
+ test_node.send_get_headers(locator=[tip], hashstop=0)
test_node.sync_with_ping()
else:
test_node.send_block_inv(tip)
test_node.sync_with_ping()
# New blocks should now be announced with header
tip = self.mine_blocks(1)
- assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
- assert_equal(test_node.check_last_announcement(headers=[tip]), True)
+ inv_node.check_last_announcement(inv=[tip], headers=[])
+ test_node.check_last_announcement(headers=[tip])
self.log.info("Part 3: success!")
@@ -404,7 +455,7 @@ class SendHeadersTest(BitcoinTestFramework):
height += 1
inv_node.send_message(msg_block(blocks[-1]))
- inv_node.sync_with_ping() # Make sure blocks are processed
+ inv_node.sync_with_ping() # Make sure blocks are processed
test_node.last_message.pop("getdata", None)
test_node.send_header_for_blocks(blocks)
test_node.sync_with_ping()
@@ -423,9 +474,9 @@ class SendHeadersTest(BitcoinTestFramework):
test_node.send_header_for_blocks(blocks)
test_node.sync_with_ping()
- test_node.wait_for_getdata([x.sha256 for x in blocks], timeout=direct_fetch_response_time)
+ test_node.wait_for_getdata([x.sha256 for x in blocks], timeout=DIRECT_FETCH_RESPONSE_TIME)
- [ test_node.send_message(msg_block(x)) for x in blocks ]
+ [test_node.send_message(msg_block(x)) for x in blocks]
test_node.sync_with_ping()
@@ -454,13 +505,13 @@ class SendHeadersTest(BitcoinTestFramework):
# both blocks (same work as tip)
test_node.send_header_for_blocks(blocks[1:2])
test_node.sync_with_ping()
- test_node.wait_for_getdata([x.sha256 for x in blocks[0:2]], timeout=direct_fetch_response_time)
+ test_node.wait_for_getdata([x.sha256 for x in blocks[0:2]], timeout=DIRECT_FETCH_RESPONSE_TIME)
# Announcing 16 more headers should trigger direct fetch for 14 more
# blocks
test_node.send_header_for_blocks(blocks[2:18])
test_node.sync_with_ping()
- test_node.wait_for_getdata([x.sha256 for x in blocks[2:16]], timeout=direct_fetch_response_time)
+ test_node.wait_for_getdata([x.sha256 for x in blocks[2:16]], timeout=DIRECT_FETCH_RESPONSE_TIME)
# Announcing 1 more header should not trigger any response
test_node.last_message.pop("getdata", None)
@@ -472,7 +523,7 @@ class SendHeadersTest(BitcoinTestFramework):
self.log.info("Part 4: success!")
# Now deliver all those blocks we announced.
- [ test_node.send_message(msg_block(x)) for x in blocks ]
+ [test_node.send_message(msg_block(x)) for x in blocks]
self.log.info("Part 5: Testing handling of unconnecting headers")
# First we test that receipt of an unconnecting header doesn't prevent
@@ -494,7 +545,7 @@ class SendHeadersTest(BitcoinTestFramework):
test_node.wait_for_getheaders()
test_node.send_header_for_blocks(blocks)
test_node.wait_for_getdata([x.sha256 for x in blocks])
- [ test_node.send_message(msg_block(x)) for x in blocks ]
+ [test_node.send_message(msg_block(x)) for x in blocks]
test_node.sync_with_ping()
assert_equal(int(self.nodes[0].getbestblockhash(), 16), blocks[1].sha256)
@@ -502,7 +553,7 @@ class SendHeadersTest(BitcoinTestFramework):
# Now we test that if we repeatedly don't send connecting headers, we
# don't go into an infinite loop trying to get them to connect.
MAX_UNCONNECTING_HEADERS = 10
- for j in range(MAX_UNCONNECTING_HEADERS+1):
+ for j in range(MAX_UNCONNECTING_HEADERS + 1):
blocks.append(create_block(tip, create_coinbase(height), block_time))
blocks[-1].solve()
tip = blocks[-1].sha256
@@ -524,11 +575,11 @@ class SendHeadersTest(BitcoinTestFramework):
# Now try to see how many unconnecting headers we can send
# before we get disconnected. Should be 5*MAX_UNCONNECTING_HEADERS
- for i in range(5*MAX_UNCONNECTING_HEADERS - 1):
+ for i in range(5 * MAX_UNCONNECTING_HEADERS - 1):
# Send a header that doesn't connect, check that we get a getheaders.
with mininode_lock:
test_node.last_message.pop("getheaders", None)
- test_node.send_header_for_blocks([blocks[i%len(blocks)]])
+ test_node.send_header_for_blocks([blocks[i % len(blocks)]])
test_node.wait_for_getheaders()
# Eventually this stops working.
diff --git a/test/functional/signmessages.py b/test/functional/signmessages.py
index 52ba6a5ad7..5b6935ceea 100755
--- a/test/functional/signmessages.py
+++ b/test/functional/signmessages.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016 The Bitcoin Core developers
+# Copyright (c) 2016-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 commands for signing and verifying messages."""
@@ -11,6 +11,7 @@ class SignMessagesTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
+ self.extra_args = [["-addresstype=legacy"]]
def run_test(self):
message = 'This is just a test message'
diff --git a/test/functional/signrawtransactions.py b/test/functional/signrawtransactions.py
index 9a45d53cb8..dd0fa6c02c 100755
--- a/test/functional/signrawtransactions.py
+++ b/test/functional/signrawtransactions.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Copyright (c) 2015-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 transaction signing using the signrawtransaction RPC."""
diff --git a/test/functional/smartfees.py b/test/functional/smartfees.py
index 986f4546a8..68453e50f4 100755
--- a/test/functional/smartfees.py
+++ b/test/functional/smartfees.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 fee estimation code."""
diff --git a/test/functional/test_framework/address.py b/test/functional/test_framework/address.py
index 2e2db5ffb2..b076c9dd43 100644
--- a/test/functional/test_framework/address.py
+++ b/test/functional/test_framework/address.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016 The Bitcoin Core developers
+# Copyright (c) 2016-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.
"""Encode and decode BASE58, P2PKH and P2SH addresses."""
diff --git a/test/functional/test_framework/authproxy.py b/test/functional/test_framework/authproxy.py
index b3671cbdc5..bd3a3b3fab 100644
--- a/test/functional/test_framework/authproxy.py
+++ b/test/functional/test_framework/authproxy.py
@@ -33,24 +33,17 @@ ServiceProxy class:
- uses standard Python json lib
"""
-try:
- import http.client as httplib
-except ImportError:
- import httplib
import base64
import decimal
+import http.client
import json
import logging
import socket
import time
-try:
- import urllib.parse as urlparse
-except ImportError:
- import urlparse
-
-USER_AGENT = "AuthServiceProxy/0.1"
+import urllib.parse
HTTP_TIMEOUT = 30
+USER_AGENT = "AuthServiceProxy/0.1"
log = logging.getLogger("BitcoinRPC")
@@ -60,7 +53,7 @@ class JSONRPCException(Exception):
errmsg = '%(message)s (%(code)i)' % rpc_error
except (KeyError, TypeError):
errmsg = ''
- Exception.__init__(self, errmsg)
+ super().__init__(errmsg)
self.error = rpc_error
@@ -69,28 +62,18 @@ def EncodeDecimal(o):
return str(o)
raise TypeError(repr(o) + " is not JSON serializable")
-class AuthServiceProxy(object):
+class AuthServiceProxy():
__id_count = 0
# ensure_ascii: escape unicode as \uXXXX, passed to json.dumps
def __init__(self, service_url, service_name=None, timeout=HTTP_TIMEOUT, connection=None, ensure_ascii=True):
self.__service_url = service_url
self._service_name = service_name
- self.ensure_ascii = ensure_ascii # can be toggled on the fly by tests
- self.__url = urlparse.urlparse(service_url)
- if self.__url.port is None:
- port = 80
- else:
- port = self.__url.port
- (user, passwd) = (self.__url.username, self.__url.password)
- try:
- user = user.encode('utf8')
- except AttributeError:
- pass
- try:
- passwd = passwd.encode('utf8')
- except AttributeError:
- pass
+ self.ensure_ascii = ensure_ascii # can be toggled on the fly by tests
+ self.__url = urllib.parse.urlparse(service_url)
+ port = 80 if self.__url.port is None else self.__url.port
+ user = None if self.__url.username is None else self.__url.username.encode('utf8')
+ passwd = None if self.__url.password is None else self.__url.password.encode('utf8')
authpair = user + b':' + passwd
self.__auth_header = b'Basic ' + base64.b64encode(authpair)
@@ -98,11 +81,9 @@ class AuthServiceProxy(object):
# Callables re-use the connection of the original proxy
self.__conn = connection
elif self.__url.scheme == 'https':
- self.__conn = httplib.HTTPSConnection(self.__url.hostname, port,
- timeout=timeout)
+ self.__conn = http.client.HTTPSConnection(self.__url.hostname, port, timeout=timeout)
else:
- self.__conn = httplib.HTTPConnection(self.__url.hostname, port,
- timeout=timeout)
+ self.__conn = http.client.HTTPConnection(self.__url.hostname, port, timeout=timeout)
def __getattr__(self, name):
if name.startswith('__') and name.endswith('__'):
@@ -124,31 +105,34 @@ class AuthServiceProxy(object):
try:
self.__conn.request(method, path, postdata, headers)
return self._get_response()
- except httplib.BadStatusLine as e:
- if e.line == "''": # if connection was closed, try again
+ except http.client.BadStatusLine as e:
+ if e.line == "''": # if connection was closed, try again
self.__conn.close()
self.__conn.request(method, path, postdata, headers)
return self._get_response()
else:
raise
- except (BrokenPipeError,ConnectionResetError):
+ except (BrokenPipeError, ConnectionResetError):
# Python 3.5+ raises BrokenPipeError instead of BadStatusLine when the connection was reset
# ConnectionResetError happens on FreeBSD with Python 3.4
self.__conn.close()
self.__conn.request(method, path, postdata, headers)
return self._get_response()
- def __call__(self, *args, **argsn):
+ def get_request(self, *args, **argsn):
AuthServiceProxy.__id_count += 1
- log.debug("-%s-> %s %s"%(AuthServiceProxy.__id_count, self._service_name,
- json.dumps(args, default=EncodeDecimal, ensure_ascii=self.ensure_ascii)))
+ log.debug("-%s-> %s %s" % (AuthServiceProxy.__id_count, self._service_name,
+ json.dumps(args, default=EncodeDecimal, ensure_ascii=self.ensure_ascii)))
if args and argsn:
raise ValueError('Cannot handle both named and positional arguments')
- postdata = json.dumps({'version': '1.1',
- 'method': self._service_name,
- 'params': args or argsn,
- 'id': AuthServiceProxy.__id_count}, default=EncodeDecimal, ensure_ascii=self.ensure_ascii)
+ return {'version': '1.1',
+ 'method': self._service_name,
+ 'params': args or argsn,
+ 'id': AuthServiceProxy.__id_count}
+
+ def __call__(self, *args, **argsn):
+ postdata = json.dumps(self.get_request(*args, **argsn), default=EncodeDecimal, ensure_ascii=self.ensure_ascii)
response = self._request('POST', self.__url.path, postdata.encode('utf-8'))
if response['error'] is not None:
raise JSONRPCException(response['error'])
@@ -158,9 +142,9 @@ class AuthServiceProxy(object):
else:
return response['result']
- def _batch(self, rpc_call_list):
+ def batch(self, rpc_call_list):
postdata = json.dumps(list(rpc_call_list), default=EncodeDecimal, ensure_ascii=self.ensure_ascii)
- log.debug("--> "+postdata)
+ log.debug("--> " + postdata)
return self._request('POST', self.__url.path, postdata.encode('utf-8'))
def _get_response(self):
@@ -187,9 +171,9 @@ class AuthServiceProxy(object):
response = json.loads(responsedata, parse_float=decimal.Decimal)
elapsed = time.time() - req_start_time
if "error" in response and response["error"] is None:
- log.debug("<-%s- [%.6f] %s"%(response["id"], elapsed, json.dumps(response["result"], default=EncodeDecimal, ensure_ascii=self.ensure_ascii)))
+ log.debug("<-%s- [%.6f] %s" % (response["id"], elapsed, json.dumps(response["result"], default=EncodeDecimal, ensure_ascii=self.ensure_ascii)))
else:
- log.debug("<-- [%.6f] %s"%(elapsed,responsedata))
+ log.debug("<-- [%.6f] %s" % (elapsed, responsedata))
return response
def __truediv__(self, relative_uri):
diff --git a/test/functional/test_framework/blockstore.py b/test/functional/test_framework/blockstore.py
index 4b2170a03f..6067a407cc 100644
--- a/test/functional/test_framework/blockstore.py
+++ b/test/functional/test_framework/blockstore.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Copyright (c) 2015-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.
"""BlockStore and TxStore helper classes."""
@@ -10,7 +10,7 @@ import dbm.dumb as dbmd
logger = logging.getLogger("TestFramework.blockstore")
-class BlockStore(object):
+class BlockStore():
"""BlockStore helper class.
BlockStore keeps a map of blocks and implements helper functions for
@@ -100,7 +100,7 @@ class BlockStore(object):
def get_blocks(self, inv):
responses = []
for i in inv:
- if (i.type == 2): # MSG_BLOCK
+ if (i.type == 2 or i.type == (2 | (1 << 30))): # MSG_BLOCK or MSG_WITNESS_BLOCK
data = self.get(i.hash)
if data is not None:
# Use msg_generic to avoid re-serialization
@@ -127,7 +127,7 @@ class BlockStore(object):
locator.vHave = r
return locator
-class TxStore(object):
+class TxStore():
def __init__(self, datadir):
self.txDB = dbmd.open(datadir + "/transactions", 'c')
@@ -153,7 +153,7 @@ class TxStore(object):
def get_transactions(self, inv):
responses = []
for i in inv:
- if (i.type == 1): # MSG_TX
+ if (i.type == 1 or i.type == (1 | (1 << 30))): # MSG_TX or MSG_WITNESS_TX
tx = self.get(i.hash)
if tx is not None:
responses.append(msg_generic(b"tx", tx))
diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py
index 55f398719a..642ef98a27 100644
--- a/test/functional/test_framework/blocktools.py
+++ b/test/functional/test_framework/blocktools.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Copyright (c) 2015-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.
"""Utilities for manipulating blocks and transactions."""
diff --git a/test/functional/test_framework/comptool.py b/test/functional/test_framework/comptool.py
index bfbc0c3b03..61ea2280e2 100755
--- a/test/functional/test_framework/comptool.py
+++ b/test/functional/test_framework/comptool.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Copyright (c) 2015-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.
"""Compare two or more bitcoinds to each other.
@@ -27,7 +27,7 @@ logger=logging.getLogger("TestFramework.comptool")
global mininode_lock
-class RejectResult(object):
+class RejectResult():
"""Outcome that expects rejection of a transaction or block."""
def __init__(self, code, reason=b''):
self.code = code
@@ -39,11 +39,10 @@ class RejectResult(object):
def __repr__(self):
return '%i:%s' % (self.code,self.reason or '*')
-class TestNode(NodeConnCB):
+class TestNode(P2PInterface):
def __init__(self, block_store, tx_store):
super().__init__()
- self.conn = None
self.bestblockhash = None
self.block_store = block_store
self.block_request_map = {}
@@ -58,43 +57,40 @@ class TestNode(NodeConnCB):
self.lastInv = []
self.closed = False
- def on_close(self, conn):
+ def on_close(self):
self.closed = True
- def add_connection(self, conn):
- self.conn = conn
-
- def on_headers(self, conn, message):
+ def on_headers(self, message):
if len(message.headers) > 0:
best_header = message.headers[-1]
best_header.calc_sha256()
self.bestblockhash = best_header.sha256
- def on_getheaders(self, conn, message):
+ def on_getheaders(self, message):
response = self.block_store.headers_for(message.locator, message.hashstop)
if response is not None:
- conn.send_message(response)
+ self.send_message(response)
- def on_getdata(self, conn, message):
- [conn.send_message(r) for r in self.block_store.get_blocks(message.inv)]
- [conn.send_message(r) for r in self.tx_store.get_transactions(message.inv)]
+ def on_getdata(self, message):
+ [self.send_message(r) for r in self.block_store.get_blocks(message.inv)]
+ [self.send_message(r) for r in self.tx_store.get_transactions(message.inv)]
for i in message.inv:
- if i.type == 1:
+ if i.type == 1 or i.type == 1 | (1 << 30): # MSG_TX or MSG_WITNESS_TX
self.tx_request_map[i.hash] = True
- elif i.type == 2:
+ elif i.type == 2 or i.type == 2 | (1 << 30): # MSG_BLOCK or MSG_WITNESS_BLOCK
self.block_request_map[i.hash] = True
- def on_inv(self, conn, message):
+ def on_inv(self, message):
self.lastInv = [x.hash for x in message.inv]
- def on_pong(self, conn, message):
+ def on_pong(self, message):
try:
del self.pingMap[message.nonce]
except KeyError:
raise AssertionError("Got pong for unknown ping [%s]" % repr(message))
- def on_reject(self, conn, message):
+ def on_reject(self, message):
if message.message == b'tx':
self.tx_reject_map[message.data] = RejectResult(message.code, message.reason)
if message.message == b'block':
@@ -102,30 +98,30 @@ class TestNode(NodeConnCB):
def send_inv(self, obj):
mtype = 2 if isinstance(obj, CBlock) else 1
- self.conn.send_message(msg_inv([CInv(mtype, obj.sha256)]))
+ self.send_message(msg_inv([CInv(mtype, obj.sha256)]))
def send_getheaders(self):
# We ask for headers from their last tip.
m = msg_getheaders()
m.locator = self.block_store.get_locator(self.bestblockhash)
- self.conn.send_message(m)
+ self.send_message(m)
def send_header(self, header):
m = msg_headers()
m.headers.append(header)
- self.conn.send_message(m)
+ self.send_message(m)
# This assumes BIP31
def send_ping(self, nonce):
self.pingMap[nonce] = True
- self.conn.send_message(msg_ping(nonce))
+ self.send_message(msg_ping(nonce))
def received_ping_response(self, nonce):
return nonce not in self.pingMap
def send_mempool(self):
self.lastInv = []
- self.conn.send_message(msg_mempool())
+ self.send_message(msg_mempool())
# TestInstance:
#
@@ -156,18 +152,17 @@ class TestNode(NodeConnCB):
# across all connections. (If outcome of final tx is specified as true
# or false, then only the last tx is tested against outcome.)
-class TestInstance(object):
+class TestInstance():
def __init__(self, objects=None, sync_every_block=True, sync_every_tx=False):
self.blocks_and_transactions = objects if objects else []
self.sync_every_block = sync_every_block
self.sync_every_tx = sync_every_tx
-class TestManager(object):
+class TestManager():
def __init__(self, testgen, datadir):
self.test_generator = testgen
- self.connections = []
- self.test_nodes = []
+ self.p2p_connections= []
self.block_store = BlockStore(datadir)
self.tx_store = TxStore(datadir)
self.ping_counter = 1
@@ -175,28 +170,24 @@ class TestManager(object):
def add_all_connections(self, nodes):
for i in range(len(nodes)):
# Create a p2p connection to each node
- test_node = TestNode(self.block_store, self.tx_store)
- self.test_nodes.append(test_node)
- self.connections.append(NodeConn('127.0.0.1', p2p_port(i), nodes[i], test_node))
- # Make sure the TestNode (callback class) has a reference to its
- # associated NodeConn
- test_node.add_connection(self.connections[-1])
+ node = TestNode(self.block_store, self.tx_store)
+ node.peer_connect('127.0.0.1', p2p_port(i))
+ self.p2p_connections.append(node)
def clear_all_connections(self):
- self.connections = []
- self.test_nodes = []
+ self.p2p_connections = []
def wait_for_disconnections(self):
def disconnected():
- return all(node.closed for node in self.test_nodes)
+ return all(node.closed for node in self.p2p_connections)
wait_until(disconnected, timeout=10, lock=mininode_lock)
def wait_for_verack(self):
- return all(node.wait_for_verack() for node in self.test_nodes)
+ return all(node.wait_for_verack() for node in self.p2p_connections)
def wait_for_pings(self, counter):
def received_pongs():
- return all(node.received_ping_response(counter) for node in self.test_nodes)
+ return all(node.received_ping_response(counter) for node in self.p2p_connections)
wait_until(received_pongs, lock=mininode_lock)
# sync_blocks: Wait for all connections to request the blockhash given
@@ -206,17 +197,17 @@ class TestManager(object):
def blocks_requested():
return all(
blockhash in node.block_request_map and node.block_request_map[blockhash]
- for node in self.test_nodes
+ for node in self.p2p_connections
)
# --> error if not requested
wait_until(blocks_requested, attempts=20*num_blocks, lock=mininode_lock)
# Send getheaders message
- [ c.cb.send_getheaders() for c in self.connections ]
+ [ c.send_getheaders() for c in self.p2p_connections ]
# Send ping and wait for response -- synchronization hack
- [ c.cb.send_ping(self.ping_counter) for c in self.connections ]
+ [ c.send_ping(self.ping_counter) for c in self.p2p_connections ]
self.wait_for_pings(self.ping_counter)
self.ping_counter += 1
@@ -226,42 +217,42 @@ class TestManager(object):
def transaction_requested():
return all(
txhash in node.tx_request_map and node.tx_request_map[txhash]
- for node in self.test_nodes
+ for node in self.p2p_connections
)
# --> error if not requested
wait_until(transaction_requested, attempts=20*num_events, lock=mininode_lock)
# Get the mempool
- [ c.cb.send_mempool() for c in self.connections ]
+ [ c.send_mempool() for c in self.p2p_connections ]
# Send ping and wait for response -- synchronization hack
- [ c.cb.send_ping(self.ping_counter) for c in self.connections ]
+ [ c.send_ping(self.ping_counter) for c in self.p2p_connections ]
self.wait_for_pings(self.ping_counter)
self.ping_counter += 1
# Sort inv responses from each node
with mininode_lock:
- [ c.cb.lastInv.sort() for c in self.connections ]
+ [ c.lastInv.sort() for c in self.p2p_connections ]
# Verify that the tip of each connection all agree with each other, and
# with the expected outcome (if given)
def check_results(self, blockhash, outcome):
with mininode_lock:
- for c in self.connections:
+ for c in self.p2p_connections:
if outcome is None:
- if c.cb.bestblockhash != self.connections[0].cb.bestblockhash:
+ if c.bestblockhash != self.p2p_connections[0].bestblockhash:
return False
elif isinstance(outcome, RejectResult): # Check that block was rejected w/ code
- if c.cb.bestblockhash == blockhash:
+ if c.bestblockhash == blockhash:
return False
- if blockhash not in c.cb.block_reject_map:
+ if blockhash not in c.block_reject_map:
logger.error('Block not in reject map: %064x' % (blockhash))
return False
- if not outcome.match(c.cb.block_reject_map[blockhash]):
- logger.error('Block rejected with %s instead of expected %s: %064x' % (c.cb.block_reject_map[blockhash], outcome, blockhash))
+ if not outcome.match(c.block_reject_map[blockhash]):
+ logger.error('Block rejected with %s instead of expected %s: %064x' % (c.block_reject_map[blockhash], outcome, blockhash))
return False
- elif ((c.cb.bestblockhash == blockhash) != outcome):
+ elif ((c.bestblockhash == blockhash) != outcome):
return False
return True
@@ -273,21 +264,21 @@ class TestManager(object):
# a particular tx's existence in the mempool is the same across all nodes.
def check_mempool(self, txhash, outcome):
with mininode_lock:
- for c in self.connections:
+ for c in self.p2p_connections:
if outcome is None:
# Make sure the mempools agree with each other
- if c.cb.lastInv != self.connections[0].cb.lastInv:
+ if c.lastInv != self.p2p_connections[0].lastInv:
return False
elif isinstance(outcome, RejectResult): # Check that tx was rejected w/ code
- if txhash in c.cb.lastInv:
+ if txhash in c.lastInv:
return False
- if txhash not in c.cb.tx_reject_map:
+ if txhash not in c.tx_reject_map:
logger.error('Tx not in reject map: %064x' % (txhash))
return False
- if not outcome.match(c.cb.tx_reject_map[txhash]):
- logger.error('Tx rejected with %s instead of expected %s: %064x' % (c.cb.tx_reject_map[txhash], outcome, txhash))
+ if not outcome.match(c.tx_reject_map[txhash]):
+ logger.error('Tx rejected with %s instead of expected %s: %064x' % (c.tx_reject_map[txhash], outcome, txhash))
return False
- elif ((txhash in c.cb.lastInv) != outcome):
+ elif ((txhash in c.lastInv) != outcome):
return False
return True
@@ -295,8 +286,11 @@ class TestManager(object):
# Wait until verack is received
self.wait_for_verack()
- test_number = 1
- for test_instance in self.test_generator.get_tests():
+ test_number = 0
+ tests = self.test_generator.get_tests()
+ for test_instance in tests:
+ test_number += 1
+ logger.info("Running test %d: %s line %s" % (test_number, tests.gi_code.co_filename, tests.gi_frame.f_lineno))
# We use these variables to keep track of the last block
# and last transaction in the tests, which are used
# if we're not syncing on every block or every tx.
@@ -329,25 +323,25 @@ class TestManager(object):
first_block_with_hash = False
with mininode_lock:
self.block_store.add_block(block)
- for c in self.connections:
- if first_block_with_hash and block.sha256 in c.cb.block_request_map and c.cb.block_request_map[block.sha256] == True:
+ for c in self.p2p_connections:
+ if first_block_with_hash and block.sha256 in c.block_request_map and c.block_request_map[block.sha256] == True:
# There was a previous request for this block hash
# Most likely, we delivered a header for this block
# but never had the block to respond to the getdata
c.send_message(msg_block(block))
else:
- c.cb.block_request_map[block.sha256] = False
+ c.block_request_map[block.sha256] = False
# Either send inv's to each node and sync, or add
# to invqueue for later inv'ing.
if (test_instance.sync_every_block):
# if we expect success, send inv and sync every block
# if we expect failure, just push the block and see what happens.
if outcome == True:
- [ c.cb.send_inv(block) for c in self.connections ]
+ [ c.send_inv(block) for c in self.p2p_connections ]
self.sync_blocks(block.sha256, 1)
else:
- [ c.send_message(msg_block(block)) for c in self.connections ]
- [ c.cb.send_ping(self.ping_counter) for c in self.connections ]
+ [ c.send_message(msg_block(block)) for c in self.p2p_connections ]
+ [ c.send_ping(self.ping_counter) for c in self.p2p_connections ]
self.wait_for_pings(self.ping_counter)
self.ping_counter += 1
if (not self.check_results(tip, outcome)):
@@ -357,7 +351,7 @@ class TestManager(object):
elif isinstance(b_or_t, CBlockHeader):
block_header = b_or_t
self.block_store.add_header(block_header)
- [ c.cb.send_header(block_header) for c in self.connections ]
+ [ c.send_header(block_header) for c in self.p2p_connections ]
else: # Tx test runner
assert(isinstance(b_or_t, CTransaction))
@@ -366,11 +360,11 @@ class TestManager(object):
# Add to shared tx store and clear map entry
with mininode_lock:
self.tx_store.add_transaction(tx)
- for c in self.connections:
- c.cb.tx_request_map[tx.sha256] = False
+ for c in self.p2p_connections:
+ c.tx_request_map[tx.sha256] = False
# Again, either inv to all nodes or save for later
if (test_instance.sync_every_tx):
- [ c.cb.send_inv(tx) for c in self.connections ]
+ [ c.send_inv(tx) for c in self.p2p_connections ]
self.sync_transaction(tx.sha256, 1)
if (not self.check_mempool(tx.sha256, outcome)):
raise AssertionError("Test failed at test %d" % test_number)
@@ -378,29 +372,26 @@ class TestManager(object):
invqueue.append(CInv(1, tx.sha256))
# Ensure we're not overflowing the inv queue
if len(invqueue) == MAX_INV_SZ:
- [ c.send_message(msg_inv(invqueue)) for c in self.connections ]
+ [ c.send_message(msg_inv(invqueue)) for c in self.p2p_connections ]
invqueue = []
# Do final sync if we weren't syncing on every block or every tx.
if (not test_instance.sync_every_block and block is not None):
if len(invqueue) > 0:
- [ c.send_message(msg_inv(invqueue)) for c in self.connections ]
+ [ c.send_message(msg_inv(invqueue)) for c in self.p2p_connections ]
invqueue = []
self.sync_blocks(block.sha256, len(test_instance.blocks_and_transactions))
if (not self.check_results(tip, block_outcome)):
raise AssertionError("Block test failed at test %d" % test_number)
if (not test_instance.sync_every_tx and tx is not None):
if len(invqueue) > 0:
- [ c.send_message(msg_inv(invqueue)) for c in self.connections ]
+ [ c.send_message(msg_inv(invqueue)) for c in self.p2p_connections ]
invqueue = []
self.sync_transaction(tx.sha256, len(test_instance.blocks_and_transactions))
if (not self.check_mempool(tx.sha256, tx_outcome)):
raise AssertionError("Mempool test failed at test %d" % test_number)
- logger.info("Test %d: PASS" % test_number)
- test_number += 1
-
- [ c.disconnect_node() for c in self.connections ]
+ [ c.disconnect_node() for c in self.p2p_connections ]
self.wait_for_disconnections()
self.block_store.close()
self.tx_store.close()
diff --git a/test/functional/test_framework/coverage.py b/test/functional/test_framework/coverage.py
index 227b1a17af..f8761f2bb3 100644
--- a/test/functional/test_framework/coverage.py
+++ b/test/functional/test_framework/coverage.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Copyright (c) 2015-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.
"""Utilities for doing coverage analysis on the RPC interface.
@@ -14,7 +14,7 @@ import os
REFERENCE_FILENAME = 'rpc_interface.txt'
-class AuthServiceProxyWrapper(object):
+class AuthServiceProxyWrapper():
"""
An object that wraps AuthServiceProxy to record specific RPC calls.
@@ -31,10 +31,11 @@ class AuthServiceProxyWrapper(object):
self.auth_service_proxy_instance = auth_service_proxy_instance
self.coverage_logfile = coverage_logfile
- def __getattr__(self, *args, **kwargs):
- return_val = self.auth_service_proxy_instance.__getattr__(
- *args, **kwargs)
-
+ def __getattr__(self, name):
+ return_val = getattr(self.auth_service_proxy_instance, name)
+ if not isinstance(return_val, type(self.auth_service_proxy_instance)):
+ # If proxy getattr returned an unwrapped value, do the same here.
+ return return_val
return AuthServiceProxyWrapper(return_val, self.coverage_logfile)
def __call__(self, *args, **kwargs):
@@ -44,20 +45,23 @@ class AuthServiceProxyWrapper(object):
"""
return_val = self.auth_service_proxy_instance.__call__(*args, **kwargs)
+ self._log_call()
+ return return_val
+
+ def _log_call(self):
rpc_method = self.auth_service_proxy_instance._service_name
if self.coverage_logfile:
with open(self.coverage_logfile, 'a+', encoding='utf8') as f:
f.write("%s\n" % rpc_method)
- return return_val
-
- @property
- def url(self):
- return self.auth_service_proxy_instance.url
-
def __truediv__(self, relative_uri):
- return AuthServiceProxyWrapper(self.auth_service_proxy_instance / relative_uri)
+ return AuthServiceProxyWrapper(self.auth_service_proxy_instance / relative_uri,
+ self.coverage_logfile)
+
+ def get_request(self, *args, **kwargs):
+ self._log_call()
+ return self.auth_service_proxy_instance.get_request(*args, **kwargs)
def get_filename(dirname, n_node):
"""
diff --git a/test/functional/test_framework/key.py b/test/functional/test_framework/key.py
index 85a6158a2f..aa91fb5b0d 100644
--- a/test/functional/test_framework/key.py
+++ b/test/functional/test_framework/key.py
@@ -84,7 +84,7 @@ def _check_result(val, func, args):
ssl.EC_KEY_new_by_curve_name.restype = ctypes.c_void_p
ssl.EC_KEY_new_by_curve_name.errcheck = _check_result
-class CECKey(object):
+class CECKey():
"""Wrapper around OpenSSL's EC_KEY"""
POINT_CONVERSION_COMPRESSED = 2
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py
new file mode 100644
index 0000000000..a54a0299c7
--- /dev/null
+++ b/test/functional/test_framework/messages.py
@@ -0,0 +1,1308 @@
+#!/usr/bin/env python3
+# Copyright (c) 2010 ArtForz -- public domain half-a-node
+# Copyright (c) 2012 Jeff Garzik
+# Copyright (c) 2010-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.
+"""Bitcoin test framework primitive and message strcutures
+
+CBlock, CTransaction, CBlockHeader, CTxIn, CTxOut, etc....:
+ data structures that should map to corresponding structures in
+ bitcoin/primitives
+
+msg_block, msg_tx, msg_headers, etc.:
+ data structures that represent network messages
+
+ser_*, deser_*: functions that handle serialization/deserialization."""
+from codecs import encode
+import copy
+import hashlib
+from io import BytesIO
+import random
+import socket
+import struct
+import time
+
+from test_framework.siphash import siphash256
+from test_framework.util import hex_str_to_bytes, bytes_to_hex_str
+
+MIN_VERSION_SUPPORTED = 60001
+MY_VERSION = 70014 # past bip-31 for ping/pong
+MY_SUBVERSION = b"/python-mininode-tester:0.0.3/"
+MY_RELAY = 1 # from version 70001 onwards, fRelay should be appended to version messages (BIP37)
+
+MAX_INV_SZ = 50000
+MAX_BLOCK_BASE_SIZE = 1000000
+
+COIN = 100000000 # 1 btc in satoshis
+
+NODE_NETWORK = (1 << 0)
+# NODE_GETUTXO = (1 << 1)
+NODE_BLOOM = (1 << 2)
+NODE_WITNESS = (1 << 3)
+NODE_UNSUPPORTED_SERVICE_BIT_5 = (1 << 5)
+NODE_UNSUPPORTED_SERVICE_BIT_7 = (1 << 7)
+NODE_NETWORK_LIMITED = (1 << 10)
+
+# Serialization/deserialization tools
+def sha256(s):
+ return hashlib.new('sha256', s).digest()
+
+def ripemd160(s):
+ return hashlib.new('ripemd160', s).digest()
+
+def hash256(s):
+ return sha256(sha256(s))
+
+def ser_compact_size(l):
+ r = b""
+ if l < 253:
+ r = struct.pack("B", l)
+ elif l < 0x10000:
+ r = struct.pack("<BH", 253, l)
+ elif l < 0x100000000:
+ r = struct.pack("<BI", 254, l)
+ else:
+ r = struct.pack("<BQ", 255, l)
+ return r
+
+def deser_compact_size(f):
+ nit = struct.unpack("<B", f.read(1))[0]
+ if nit == 253:
+ nit = struct.unpack("<H", f.read(2))[0]
+ elif nit == 254:
+ nit = struct.unpack("<I", f.read(4))[0]
+ elif nit == 255:
+ nit = struct.unpack("<Q", f.read(8))[0]
+ return nit
+
+def deser_string(f):
+ nit = deser_compact_size(f)
+ return f.read(nit)
+
+def ser_string(s):
+ return ser_compact_size(len(s)) + s
+
+def deser_uint256(f):
+ r = 0
+ for i in range(8):
+ t = struct.unpack("<I", f.read(4))[0]
+ r += t << (i * 32)
+ return r
+
+
+def ser_uint256(u):
+ rs = b""
+ for i in range(8):
+ rs += struct.pack("<I", u & 0xFFFFFFFF)
+ u >>= 32
+ return rs
+
+
+def uint256_from_str(s):
+ r = 0
+ t = struct.unpack("<IIIIIIII", s[:32])
+ for i in range(8):
+ r += t[i] << (i * 32)
+ return r
+
+
+def uint256_from_compact(c):
+ nbytes = (c >> 24) & 0xFF
+ v = (c & 0xFFFFFF) << (8 * (nbytes - 3))
+ return v
+
+
+def deser_vector(f, c):
+ nit = deser_compact_size(f)
+ r = []
+ for i in range(nit):
+ t = c()
+ t.deserialize(f)
+ r.append(t)
+ return r
+
+
+# ser_function_name: Allow for an alternate serialization function on the
+# entries in the vector (we use this for serializing the vector of transactions
+# for a witness block).
+def ser_vector(l, ser_function_name=None):
+ r = ser_compact_size(len(l))
+ for i in l:
+ if ser_function_name:
+ r += getattr(i, ser_function_name)()
+ else:
+ r += i.serialize()
+ return r
+
+
+def deser_uint256_vector(f):
+ nit = deser_compact_size(f)
+ r = []
+ for i in range(nit):
+ t = deser_uint256(f)
+ r.append(t)
+ return r
+
+
+def ser_uint256_vector(l):
+ r = ser_compact_size(len(l))
+ for i in l:
+ r += ser_uint256(i)
+ return r
+
+
+def deser_string_vector(f):
+ nit = deser_compact_size(f)
+ r = []
+ for i in range(nit):
+ t = deser_string(f)
+ r.append(t)
+ return r
+
+
+def ser_string_vector(l):
+ r = ser_compact_size(len(l))
+ for sv in l:
+ r += ser_string(sv)
+ return r
+
+
+# Deserialize from a hex string representation (eg from RPC)
+def FromHex(obj, hex_string):
+ obj.deserialize(BytesIO(hex_str_to_bytes(hex_string)))
+ return obj
+
+# Convert a binary-serializable object to hex (eg for submission via RPC)
+def ToHex(obj):
+ return bytes_to_hex_str(obj.serialize())
+
+# Objects that map to bitcoind objects, which can be serialized/deserialized
+
+class CAddress():
+ def __init__(self):
+ self.nServices = 1
+ self.pchReserved = b"\x00" * 10 + b"\xff" * 2
+ self.ip = "0.0.0.0"
+ self.port = 0
+
+ def deserialize(self, f):
+ self.nServices = struct.unpack("<Q", f.read(8))[0]
+ self.pchReserved = f.read(12)
+ self.ip = socket.inet_ntoa(f.read(4))
+ self.port = struct.unpack(">H", f.read(2))[0]
+
+ def serialize(self):
+ r = b""
+ r += struct.pack("<Q", self.nServices)
+ r += self.pchReserved
+ r += socket.inet_aton(self.ip)
+ r += struct.pack(">H", self.port)
+ return r
+
+ def __repr__(self):
+ return "CAddress(nServices=%i ip=%s port=%i)" % (self.nServices,
+ self.ip, self.port)
+
+MSG_WITNESS_FLAG = 1<<30
+
+class CInv():
+ typemap = {
+ 0: "Error",
+ 1: "TX",
+ 2: "Block",
+ 1|MSG_WITNESS_FLAG: "WitnessTx",
+ 2|MSG_WITNESS_FLAG : "WitnessBlock",
+ 4: "CompactBlock"
+ }
+
+ def __init__(self, t=0, h=0):
+ self.type = t
+ self.hash = h
+
+ def deserialize(self, f):
+ self.type = struct.unpack("<i", f.read(4))[0]
+ self.hash = deser_uint256(f)
+
+ def serialize(self):
+ r = b""
+ r += struct.pack("<i", self.type)
+ r += ser_uint256(self.hash)
+ return r
+
+ def __repr__(self):
+ return "CInv(type=%s hash=%064x)" \
+ % (self.typemap[self.type], self.hash)
+
+
+class CBlockLocator():
+ def __init__(self):
+ self.nVersion = MY_VERSION
+ self.vHave = []
+
+ def deserialize(self, f):
+ self.nVersion = struct.unpack("<i", f.read(4))[0]
+ self.vHave = deser_uint256_vector(f)
+
+ def serialize(self):
+ r = b""
+ r += struct.pack("<i", self.nVersion)
+ r += ser_uint256_vector(self.vHave)
+ return r
+
+ def __repr__(self):
+ return "CBlockLocator(nVersion=%i vHave=%s)" \
+ % (self.nVersion, repr(self.vHave))
+
+
+class COutPoint():
+ def __init__(self, hash=0, n=0):
+ self.hash = hash
+ self.n = n
+
+ def deserialize(self, f):
+ self.hash = deser_uint256(f)
+ self.n = struct.unpack("<I", f.read(4))[0]
+
+ def serialize(self):
+ r = b""
+ r += ser_uint256(self.hash)
+ r += struct.pack("<I", self.n)
+ return r
+
+ def __repr__(self):
+ return "COutPoint(hash=%064x n=%i)" % (self.hash, self.n)
+
+
+class CTxIn():
+ def __init__(self, outpoint=None, scriptSig=b"", nSequence=0):
+ if outpoint is None:
+ self.prevout = COutPoint()
+ else:
+ self.prevout = outpoint
+ self.scriptSig = scriptSig
+ self.nSequence = nSequence
+
+ def deserialize(self, f):
+ self.prevout = COutPoint()
+ self.prevout.deserialize(f)
+ self.scriptSig = deser_string(f)
+ self.nSequence = struct.unpack("<I", f.read(4))[0]
+
+ def serialize(self):
+ r = b""
+ r += self.prevout.serialize()
+ r += ser_string(self.scriptSig)
+ r += struct.pack("<I", self.nSequence)
+ return r
+
+ def __repr__(self):
+ return "CTxIn(prevout=%s scriptSig=%s nSequence=%i)" \
+ % (repr(self.prevout), bytes_to_hex_str(self.scriptSig),
+ self.nSequence)
+
+
+class CTxOut():
+ def __init__(self, nValue=0, scriptPubKey=b""):
+ self.nValue = nValue
+ self.scriptPubKey = scriptPubKey
+
+ def deserialize(self, f):
+ self.nValue = struct.unpack("<q", f.read(8))[0]
+ self.scriptPubKey = deser_string(f)
+
+ def serialize(self):
+ r = b""
+ r += struct.pack("<q", self.nValue)
+ r += ser_string(self.scriptPubKey)
+ return r
+
+ def __repr__(self):
+ return "CTxOut(nValue=%i.%08i scriptPubKey=%s)" \
+ % (self.nValue // COIN, self.nValue % COIN,
+ bytes_to_hex_str(self.scriptPubKey))
+
+
+class CScriptWitness():
+ def __init__(self):
+ # stack is a vector of strings
+ self.stack = []
+
+ def __repr__(self):
+ return "CScriptWitness(%s)" % \
+ (",".join([bytes_to_hex_str(x) for x in self.stack]))
+
+ def is_null(self):
+ if self.stack:
+ return False
+ return True
+
+
+class CTxInWitness():
+ def __init__(self):
+ self.scriptWitness = CScriptWitness()
+
+ def deserialize(self, f):
+ self.scriptWitness.stack = deser_string_vector(f)
+
+ def serialize(self):
+ return ser_string_vector(self.scriptWitness.stack)
+
+ def __repr__(self):
+ return repr(self.scriptWitness)
+
+ def is_null(self):
+ return self.scriptWitness.is_null()
+
+
+class CTxWitness():
+ def __init__(self):
+ self.vtxinwit = []
+
+ def deserialize(self, f):
+ for i in range(len(self.vtxinwit)):
+ self.vtxinwit[i].deserialize(f)
+
+ def serialize(self):
+ r = b""
+ # This is different than the usual vector serialization --
+ # we omit the length of the vector, which is required to be
+ # the same length as the transaction's vin vector.
+ for x in self.vtxinwit:
+ r += x.serialize()
+ return r
+
+ def __repr__(self):
+ return "CTxWitness(%s)" % \
+ (';'.join([repr(x) for x in self.vtxinwit]))
+
+ def is_null(self):
+ for x in self.vtxinwit:
+ if not x.is_null():
+ return False
+ return True
+
+
+class CTransaction():
+ def __init__(self, tx=None):
+ if tx is None:
+ self.nVersion = 1
+ self.vin = []
+ self.vout = []
+ self.wit = CTxWitness()
+ self.nLockTime = 0
+ self.sha256 = None
+ self.hash = None
+ else:
+ self.nVersion = tx.nVersion
+ self.vin = copy.deepcopy(tx.vin)
+ self.vout = copy.deepcopy(tx.vout)
+ self.nLockTime = tx.nLockTime
+ self.sha256 = tx.sha256
+ self.hash = tx.hash
+ self.wit = copy.deepcopy(tx.wit)
+
+ def deserialize(self, f):
+ self.nVersion = struct.unpack("<i", f.read(4))[0]
+ self.vin = deser_vector(f, CTxIn)
+ flags = 0
+ if len(self.vin) == 0:
+ flags = struct.unpack("<B", f.read(1))[0]
+ # Not sure why flags can't be zero, but this
+ # matches the implementation in bitcoind
+ if (flags != 0):
+ self.vin = deser_vector(f, CTxIn)
+ self.vout = deser_vector(f, CTxOut)
+ else:
+ self.vout = deser_vector(f, CTxOut)
+ if flags != 0:
+ self.wit.vtxinwit = [CTxInWitness() for i in range(len(self.vin))]
+ self.wit.deserialize(f)
+ self.nLockTime = struct.unpack("<I", f.read(4))[0]
+ self.sha256 = None
+ self.hash = None
+
+ def serialize_without_witness(self):
+ r = b""
+ r += struct.pack("<i", self.nVersion)
+ r += ser_vector(self.vin)
+ r += ser_vector(self.vout)
+ r += struct.pack("<I", self.nLockTime)
+ return r
+
+ # Only serialize with witness when explicitly called for
+ def serialize_with_witness(self):
+ flags = 0
+ if not self.wit.is_null():
+ flags |= 1
+ r = b""
+ r += struct.pack("<i", self.nVersion)
+ if flags:
+ dummy = []
+ r += ser_vector(dummy)
+ r += struct.pack("<B", flags)
+ r += ser_vector(self.vin)
+ r += ser_vector(self.vout)
+ if flags & 1:
+ if (len(self.wit.vtxinwit) != len(self.vin)):
+ # vtxinwit must have the same length as vin
+ self.wit.vtxinwit = self.wit.vtxinwit[:len(self.vin)]
+ for i in range(len(self.wit.vtxinwit), len(self.vin)):
+ self.wit.vtxinwit.append(CTxInWitness())
+ r += self.wit.serialize()
+ r += struct.pack("<I", self.nLockTime)
+ return r
+
+ # Regular serialization is with witness -- must explicitly
+ # call serialize_without_witness to exclude witness data.
+ def serialize(self):
+ return self.serialize_with_witness()
+
+ # Recalculate the txid (transaction hash without witness)
+ def rehash(self):
+ self.sha256 = None
+ self.calc_sha256()
+
+ # We will only cache the serialization without witness in
+ # self.sha256 and self.hash -- those are expected to be the txid.
+ def calc_sha256(self, with_witness=False):
+ if with_witness:
+ # Don't cache the result, just return it
+ return uint256_from_str(hash256(self.serialize_with_witness()))
+
+ if self.sha256 is None:
+ self.sha256 = uint256_from_str(hash256(self.serialize_without_witness()))
+ self.hash = encode(hash256(self.serialize_without_witness())[::-1], 'hex_codec').decode('ascii')
+
+ def is_valid(self):
+ self.calc_sha256()
+ for tout in self.vout:
+ if tout.nValue < 0 or tout.nValue > 21000000 * COIN:
+ return False
+ return True
+
+ def __repr__(self):
+ return "CTransaction(nVersion=%i vin=%s vout=%s wit=%s nLockTime=%i)" \
+ % (self.nVersion, repr(self.vin), repr(self.vout), repr(self.wit), self.nLockTime)
+
+
+class CBlockHeader():
+ def __init__(self, header=None):
+ if header is None:
+ self.set_null()
+ else:
+ self.nVersion = header.nVersion
+ self.hashPrevBlock = header.hashPrevBlock
+ self.hashMerkleRoot = header.hashMerkleRoot
+ self.nTime = header.nTime
+ self.nBits = header.nBits
+ self.nNonce = header.nNonce
+ self.sha256 = header.sha256
+ self.hash = header.hash
+ self.calc_sha256()
+
+ def set_null(self):
+ self.nVersion = 1
+ self.hashPrevBlock = 0
+ self.hashMerkleRoot = 0
+ self.nTime = 0
+ self.nBits = 0
+ self.nNonce = 0
+ self.sha256 = None
+ self.hash = None
+
+ def deserialize(self, f):
+ self.nVersion = struct.unpack("<i", f.read(4))[0]
+ self.hashPrevBlock = deser_uint256(f)
+ self.hashMerkleRoot = deser_uint256(f)
+ self.nTime = struct.unpack("<I", f.read(4))[0]
+ self.nBits = struct.unpack("<I", f.read(4))[0]
+ self.nNonce = struct.unpack("<I", f.read(4))[0]
+ self.sha256 = None
+ self.hash = None
+
+ def serialize(self):
+ r = b""
+ r += struct.pack("<i", self.nVersion)
+ r += ser_uint256(self.hashPrevBlock)
+ r += ser_uint256(self.hashMerkleRoot)
+ r += struct.pack("<I", self.nTime)
+ r += struct.pack("<I", self.nBits)
+ r += struct.pack("<I", self.nNonce)
+ return r
+
+ def calc_sha256(self):
+ if self.sha256 is None:
+ r = b""
+ r += struct.pack("<i", self.nVersion)
+ r += ser_uint256(self.hashPrevBlock)
+ r += ser_uint256(self.hashMerkleRoot)
+ r += struct.pack("<I", self.nTime)
+ r += struct.pack("<I", self.nBits)
+ r += struct.pack("<I", self.nNonce)
+ self.sha256 = uint256_from_str(hash256(r))
+ self.hash = encode(hash256(r)[::-1], 'hex_codec').decode('ascii')
+
+ def rehash(self):
+ self.sha256 = None
+ self.calc_sha256()
+ return self.sha256
+
+ def __repr__(self):
+ return "CBlockHeader(nVersion=%i hashPrevBlock=%064x hashMerkleRoot=%064x nTime=%s nBits=%08x nNonce=%08x)" \
+ % (self.nVersion, self.hashPrevBlock, self.hashMerkleRoot,
+ time.ctime(self.nTime), self.nBits, self.nNonce)
+
+
+class CBlock(CBlockHeader):
+ def __init__(self, header=None):
+ super(CBlock, self).__init__(header)
+ self.vtx = []
+
+ def deserialize(self, f):
+ super(CBlock, self).deserialize(f)
+ self.vtx = deser_vector(f, CTransaction)
+
+ def serialize(self, with_witness=False):
+ r = b""
+ r += super(CBlock, self).serialize()
+ if with_witness:
+ r += ser_vector(self.vtx, "serialize_with_witness")
+ else:
+ r += ser_vector(self.vtx, "serialize_without_witness")
+ return r
+
+ # Calculate the merkle root given a vector of transaction hashes
+ @classmethod
+ def get_merkle_root(cls, hashes):
+ while len(hashes) > 1:
+ newhashes = []
+ for i in range(0, len(hashes), 2):
+ i2 = min(i+1, len(hashes)-1)
+ newhashes.append(hash256(hashes[i] + hashes[i2]))
+ hashes = newhashes
+ return uint256_from_str(hashes[0])
+
+ def calc_merkle_root(self):
+ hashes = []
+ for tx in self.vtx:
+ tx.calc_sha256()
+ hashes.append(ser_uint256(tx.sha256))
+ return self.get_merkle_root(hashes)
+
+ def calc_witness_merkle_root(self):
+ # For witness root purposes, the hash of the
+ # coinbase, with witness, is defined to be 0...0
+ hashes = [ser_uint256(0)]
+
+ for tx in self.vtx[1:]:
+ # Calculate the hashes with witness data
+ hashes.append(ser_uint256(tx.calc_sha256(True)))
+
+ return self.get_merkle_root(hashes)
+
+ def is_valid(self):
+ self.calc_sha256()
+ target = uint256_from_compact(self.nBits)
+ if self.sha256 > target:
+ return False
+ for tx in self.vtx:
+ if not tx.is_valid():
+ return False
+ if self.calc_merkle_root() != self.hashMerkleRoot:
+ return False
+ return True
+
+ def solve(self):
+ self.rehash()
+ target = uint256_from_compact(self.nBits)
+ while self.sha256 > target:
+ self.nNonce += 1
+ self.rehash()
+
+ def __repr__(self):
+ return "CBlock(nVersion=%i hashPrevBlock=%064x hashMerkleRoot=%064x nTime=%s nBits=%08x nNonce=%08x vtx=%s)" \
+ % (self.nVersion, self.hashPrevBlock, self.hashMerkleRoot,
+ time.ctime(self.nTime), self.nBits, self.nNonce, repr(self.vtx))
+
+
+class PrefilledTransaction():
+ def __init__(self, index=0, tx = None):
+ self.index = index
+ self.tx = tx
+
+ def deserialize(self, f):
+ self.index = deser_compact_size(f)
+ self.tx = CTransaction()
+ self.tx.deserialize(f)
+
+ def serialize(self, with_witness=True):
+ r = b""
+ r += ser_compact_size(self.index)
+ if with_witness:
+ r += self.tx.serialize_with_witness()
+ else:
+ r += self.tx.serialize_without_witness()
+ return r
+
+ def serialize_without_witness(self):
+ return self.serialize(with_witness=False)
+
+ def serialize_with_witness(self):
+ return self.serialize(with_witness=True)
+
+ def __repr__(self):
+ return "PrefilledTransaction(index=%d, tx=%s)" % (self.index, repr(self.tx))
+
+# This is what we send on the wire, in a cmpctblock message.
+class P2PHeaderAndShortIDs():
+ def __init__(self):
+ self.header = CBlockHeader()
+ self.nonce = 0
+ self.shortids_length = 0
+ self.shortids = []
+ self.prefilled_txn_length = 0
+ self.prefilled_txn = []
+
+ def deserialize(self, f):
+ self.header.deserialize(f)
+ self.nonce = struct.unpack("<Q", f.read(8))[0]
+ self.shortids_length = deser_compact_size(f)
+ for i in range(self.shortids_length):
+ # shortids are defined to be 6 bytes in the spec, so append
+ # two zero bytes and read it in as an 8-byte number
+ self.shortids.append(struct.unpack("<Q", f.read(6) + b'\x00\x00')[0])
+ self.prefilled_txn = deser_vector(f, PrefilledTransaction)
+ self.prefilled_txn_length = len(self.prefilled_txn)
+
+ # When using version 2 compact blocks, we must serialize with_witness.
+ def serialize(self, with_witness=False):
+ r = b""
+ r += self.header.serialize()
+ r += struct.pack("<Q", self.nonce)
+ r += ser_compact_size(self.shortids_length)
+ for x in self.shortids:
+ # We only want the first 6 bytes
+ r += struct.pack("<Q", x)[0:6]
+ if with_witness:
+ r += ser_vector(self.prefilled_txn, "serialize_with_witness")
+ else:
+ r += ser_vector(self.prefilled_txn, "serialize_without_witness")
+ return r
+
+ def __repr__(self):
+ return "P2PHeaderAndShortIDs(header=%s, nonce=%d, shortids_length=%d, shortids=%s, prefilled_txn_length=%d, prefilledtxn=%s" % (repr(self.header), self.nonce, self.shortids_length, repr(self.shortids), self.prefilled_txn_length, repr(self.prefilled_txn))
+
+# P2P version of the above that will use witness serialization (for compact
+# block version 2)
+class P2PHeaderAndShortWitnessIDs(P2PHeaderAndShortIDs):
+ def serialize(self):
+ return super(P2PHeaderAndShortWitnessIDs, self).serialize(with_witness=True)
+
+# Calculate the BIP 152-compact blocks shortid for a given transaction hash
+def calculate_shortid(k0, k1, tx_hash):
+ expected_shortid = siphash256(k0, k1, tx_hash)
+ expected_shortid &= 0x0000ffffffffffff
+ return expected_shortid
+
+# This version gets rid of the array lengths, and reinterprets the differential
+# encoding into indices that can be used for lookup.
+class HeaderAndShortIDs():
+ def __init__(self, p2pheaders_and_shortids = None):
+ self.header = CBlockHeader()
+ self.nonce = 0
+ self.shortids = []
+ self.prefilled_txn = []
+ self.use_witness = False
+
+ if p2pheaders_and_shortids != None:
+ self.header = p2pheaders_and_shortids.header
+ self.nonce = p2pheaders_and_shortids.nonce
+ self.shortids = p2pheaders_and_shortids.shortids
+ last_index = -1
+ for x in p2pheaders_and_shortids.prefilled_txn:
+ self.prefilled_txn.append(PrefilledTransaction(x.index + last_index + 1, x.tx))
+ last_index = self.prefilled_txn[-1].index
+
+ def to_p2p(self):
+ if self.use_witness:
+ ret = P2PHeaderAndShortWitnessIDs()
+ else:
+ ret = P2PHeaderAndShortIDs()
+ ret.header = self.header
+ ret.nonce = self.nonce
+ ret.shortids_length = len(self.shortids)
+ ret.shortids = self.shortids
+ ret.prefilled_txn_length = len(self.prefilled_txn)
+ ret.prefilled_txn = []
+ last_index = -1
+ for x in self.prefilled_txn:
+ ret.prefilled_txn.append(PrefilledTransaction(x.index - last_index - 1, x.tx))
+ last_index = x.index
+ return ret
+
+ def get_siphash_keys(self):
+ header_nonce = self.header.serialize()
+ header_nonce += struct.pack("<Q", self.nonce)
+ hash_header_nonce_as_str = sha256(header_nonce)
+ key0 = struct.unpack("<Q", hash_header_nonce_as_str[0:8])[0]
+ key1 = struct.unpack("<Q", hash_header_nonce_as_str[8:16])[0]
+ return [ key0, key1 ]
+
+ # Version 2 compact blocks use wtxid in shortids (rather than txid)
+ def initialize_from_block(self, block, nonce=0, prefill_list = [0], use_witness = False):
+ self.header = CBlockHeader(block)
+ self.nonce = nonce
+ self.prefilled_txn = [ PrefilledTransaction(i, block.vtx[i]) for i in prefill_list ]
+ self.shortids = []
+ self.use_witness = use_witness
+ [k0, k1] = self.get_siphash_keys()
+ for i in range(len(block.vtx)):
+ if i not in prefill_list:
+ tx_hash = block.vtx[i].sha256
+ if use_witness:
+ tx_hash = block.vtx[i].calc_sha256(with_witness=True)
+ self.shortids.append(calculate_shortid(k0, k1, tx_hash))
+
+ def __repr__(self):
+ return "HeaderAndShortIDs(header=%s, nonce=%d, shortids=%s, prefilledtxn=%s" % (repr(self.header), self.nonce, repr(self.shortids), repr(self.prefilled_txn))
+
+
+class BlockTransactionsRequest():
+
+ def __init__(self, blockhash=0, indexes = None):
+ self.blockhash = blockhash
+ self.indexes = indexes if indexes != None else []
+
+ def deserialize(self, f):
+ self.blockhash = deser_uint256(f)
+ indexes_length = deser_compact_size(f)
+ for i in range(indexes_length):
+ self.indexes.append(deser_compact_size(f))
+
+ def serialize(self):
+ r = b""
+ r += ser_uint256(self.blockhash)
+ r += ser_compact_size(len(self.indexes))
+ for x in self.indexes:
+ r += ser_compact_size(x)
+ return r
+
+ # helper to set the differentially encoded indexes from absolute ones
+ def from_absolute(self, absolute_indexes):
+ self.indexes = []
+ last_index = -1
+ for x in absolute_indexes:
+ self.indexes.append(x-last_index-1)
+ last_index = x
+
+ def to_absolute(self):
+ absolute_indexes = []
+ last_index = -1
+ for x in self.indexes:
+ absolute_indexes.append(x+last_index+1)
+ last_index = absolute_indexes[-1]
+ return absolute_indexes
+
+ def __repr__(self):
+ return "BlockTransactionsRequest(hash=%064x indexes=%s)" % (self.blockhash, repr(self.indexes))
+
+
+class BlockTransactions():
+
+ def __init__(self, blockhash=0, transactions = None):
+ self.blockhash = blockhash
+ self.transactions = transactions if transactions != None else []
+
+ def deserialize(self, f):
+ self.blockhash = deser_uint256(f)
+ self.transactions = deser_vector(f, CTransaction)
+
+ def serialize(self, with_witness=True):
+ r = b""
+ r += ser_uint256(self.blockhash)
+ if with_witness:
+ r += ser_vector(self.transactions, "serialize_with_witness")
+ else:
+ r += ser_vector(self.transactions, "serialize_without_witness")
+ return r
+
+ def __repr__(self):
+ return "BlockTransactions(hash=%064x transactions=%s)" % (self.blockhash, repr(self.transactions))
+
+
+# Objects that correspond to messages on the wire
+class msg_version():
+ command = b"version"
+
+ def __init__(self):
+ self.nVersion = MY_VERSION
+ self.nServices = NODE_NETWORK | NODE_WITNESS
+ self.nTime = int(time.time())
+ self.addrTo = CAddress()
+ self.addrFrom = CAddress()
+ self.nNonce = random.getrandbits(64)
+ self.strSubVer = MY_SUBVERSION
+ self.nStartingHeight = -1
+ self.nRelay = MY_RELAY
+
+ def deserialize(self, f):
+ self.nVersion = struct.unpack("<i", f.read(4))[0]
+ if self.nVersion == 10300:
+ self.nVersion = 300
+ self.nServices = struct.unpack("<Q", f.read(8))[0]
+ self.nTime = struct.unpack("<q", f.read(8))[0]
+ self.addrTo = CAddress()
+ self.addrTo.deserialize(f)
+
+ if self.nVersion >= 106:
+ self.addrFrom = CAddress()
+ self.addrFrom.deserialize(f)
+ self.nNonce = struct.unpack("<Q", f.read(8))[0]
+ self.strSubVer = deser_string(f)
+ else:
+ self.addrFrom = None
+ self.nNonce = None
+ self.strSubVer = None
+ self.nStartingHeight = None
+
+ if self.nVersion >= 209:
+ self.nStartingHeight = struct.unpack("<i", f.read(4))[0]
+ else:
+ self.nStartingHeight = None
+
+ if self.nVersion >= 70001:
+ # Relay field is optional for version 70001 onwards
+ try:
+ self.nRelay = struct.unpack("<b", f.read(1))[0]
+ except:
+ self.nRelay = 0
+ else:
+ self.nRelay = 0
+
+ def serialize(self):
+ r = b""
+ r += struct.pack("<i", self.nVersion)
+ r += struct.pack("<Q", self.nServices)
+ r += struct.pack("<q", self.nTime)
+ r += self.addrTo.serialize()
+ r += self.addrFrom.serialize()
+ r += struct.pack("<Q", self.nNonce)
+ r += ser_string(self.strSubVer)
+ r += struct.pack("<i", self.nStartingHeight)
+ r += struct.pack("<b", self.nRelay)
+ return r
+
+ def __repr__(self):
+ return 'msg_version(nVersion=%i nServices=%i nTime=%s addrTo=%s addrFrom=%s nNonce=0x%016X strSubVer=%s nStartingHeight=%i nRelay=%i)' \
+ % (self.nVersion, self.nServices, time.ctime(self.nTime),
+ repr(self.addrTo), repr(self.addrFrom), self.nNonce,
+ self.strSubVer, self.nStartingHeight, self.nRelay)
+
+
+class msg_verack():
+ command = b"verack"
+
+ def __init__(self):
+ pass
+
+ def deserialize(self, f):
+ pass
+
+ def serialize(self):
+ return b""
+
+ def __repr__(self):
+ return "msg_verack()"
+
+
+class msg_addr():
+ command = b"addr"
+
+ def __init__(self):
+ self.addrs = []
+
+ def deserialize(self, f):
+ self.addrs = deser_vector(f, CAddress)
+
+ def serialize(self):
+ return ser_vector(self.addrs)
+
+ def __repr__(self):
+ return "msg_addr(addrs=%s)" % (repr(self.addrs))
+
+
+class msg_inv():
+ command = b"inv"
+
+ def __init__(self, inv=None):
+ if inv is None:
+ self.inv = []
+ else:
+ self.inv = inv
+
+ def deserialize(self, f):
+ self.inv = deser_vector(f, CInv)
+
+ def serialize(self):
+ return ser_vector(self.inv)
+
+ def __repr__(self):
+ return "msg_inv(inv=%s)" % (repr(self.inv))
+
+
+class msg_getdata():
+ command = b"getdata"
+
+ def __init__(self, inv=None):
+ self.inv = inv if inv != None else []
+
+ def deserialize(self, f):
+ self.inv = deser_vector(f, CInv)
+
+ def serialize(self):
+ return ser_vector(self.inv)
+
+ def __repr__(self):
+ return "msg_getdata(inv=%s)" % (repr(self.inv))
+
+
+class msg_getblocks():
+ command = b"getblocks"
+
+ def __init__(self):
+ self.locator = CBlockLocator()
+ self.hashstop = 0
+
+ def deserialize(self, f):
+ self.locator = CBlockLocator()
+ self.locator.deserialize(f)
+ self.hashstop = deser_uint256(f)
+
+ def serialize(self):
+ r = b""
+ r += self.locator.serialize()
+ r += ser_uint256(self.hashstop)
+ return r
+
+ def __repr__(self):
+ return "msg_getblocks(locator=%s hashstop=%064x)" \
+ % (repr(self.locator), self.hashstop)
+
+
+class msg_tx():
+ command = b"tx"
+
+ def __init__(self, tx=CTransaction()):
+ self.tx = tx
+
+ def deserialize(self, f):
+ self.tx.deserialize(f)
+
+ def serialize(self):
+ return self.tx.serialize_without_witness()
+
+ def __repr__(self):
+ return "msg_tx(tx=%s)" % (repr(self.tx))
+
+class msg_witness_tx(msg_tx):
+
+ def serialize(self):
+ return self.tx.serialize_with_witness()
+
+
+class msg_block():
+ command = b"block"
+
+ def __init__(self, block=None):
+ if block is None:
+ self.block = CBlock()
+ else:
+ self.block = block
+
+ def deserialize(self, f):
+ self.block.deserialize(f)
+
+ def serialize(self):
+ return self.block.serialize(with_witness=False)
+
+ def __repr__(self):
+ return "msg_block(block=%s)" % (repr(self.block))
+
+# for cases where a user needs tighter control over what is sent over the wire
+# note that the user must supply the name of the command, and the data
+class msg_generic():
+ def __init__(self, command, data=None):
+ self.command = command
+ self.data = data
+
+ def serialize(self):
+ return self.data
+
+ def __repr__(self):
+ return "msg_generic()"
+
+class msg_witness_block(msg_block):
+
+ def serialize(self):
+ r = self.block.serialize(with_witness=True)
+ return r
+
+class msg_getaddr():
+ command = b"getaddr"
+
+ def __init__(self):
+ pass
+
+ def deserialize(self, f):
+ pass
+
+ def serialize(self):
+ return b""
+
+ def __repr__(self):
+ return "msg_getaddr()"
+
+
+class msg_ping():
+ command = b"ping"
+
+ def __init__(self, nonce=0):
+ self.nonce = nonce
+
+ def deserialize(self, f):
+ self.nonce = struct.unpack("<Q", f.read(8))[0]
+
+ def serialize(self):
+ r = b""
+ r += struct.pack("<Q", self.nonce)
+ return r
+
+ def __repr__(self):
+ return "msg_ping(nonce=%08x)" % self.nonce
+
+
+class msg_pong():
+ command = b"pong"
+
+ def __init__(self, nonce=0):
+ self.nonce = nonce
+
+ def deserialize(self, f):
+ self.nonce = struct.unpack("<Q", f.read(8))[0]
+
+ def serialize(self):
+ r = b""
+ r += struct.pack("<Q", self.nonce)
+ return r
+
+ def __repr__(self):
+ return "msg_pong(nonce=%08x)" % self.nonce
+
+
+class msg_mempool():
+ command = b"mempool"
+
+ def __init__(self):
+ pass
+
+ def deserialize(self, f):
+ pass
+
+ def serialize(self):
+ return b""
+
+ def __repr__(self):
+ return "msg_mempool()"
+
+class msg_sendheaders():
+ command = b"sendheaders"
+
+ def __init__(self):
+ pass
+
+ def deserialize(self, f):
+ pass
+
+ def serialize(self):
+ return b""
+
+ def __repr__(self):
+ return "msg_sendheaders()"
+
+
+# getheaders message has
+# number of entries
+# vector of hashes
+# hash_stop (hash of last desired block header, 0 to get as many as possible)
+class msg_getheaders():
+ command = b"getheaders"
+
+ def __init__(self):
+ self.locator = CBlockLocator()
+ self.hashstop = 0
+
+ def deserialize(self, f):
+ self.locator = CBlockLocator()
+ self.locator.deserialize(f)
+ self.hashstop = deser_uint256(f)
+
+ def serialize(self):
+ r = b""
+ r += self.locator.serialize()
+ r += ser_uint256(self.hashstop)
+ return r
+
+ def __repr__(self):
+ return "msg_getheaders(locator=%s, stop=%064x)" \
+ % (repr(self.locator), self.hashstop)
+
+
+# headers message has
+# <count> <vector of block headers>
+class msg_headers():
+ command = b"headers"
+
+ def __init__(self, headers=None):
+ self.headers = headers if headers is not None else []
+
+ def deserialize(self, f):
+ # comment in bitcoind indicates these should be deserialized as blocks
+ blocks = deser_vector(f, CBlock)
+ for x in blocks:
+ self.headers.append(CBlockHeader(x))
+
+ def serialize(self):
+ blocks = [CBlock(x) for x in self.headers]
+ return ser_vector(blocks)
+
+ def __repr__(self):
+ return "msg_headers(headers=%s)" % repr(self.headers)
+
+
+class msg_reject():
+ command = b"reject"
+ REJECT_MALFORMED = 1
+
+ def __init__(self):
+ self.message = b""
+ self.code = 0
+ self.reason = b""
+ self.data = 0
+
+ def deserialize(self, f):
+ self.message = deser_string(f)
+ self.code = struct.unpack("<B", f.read(1))[0]
+ self.reason = deser_string(f)
+ if (self.code != self.REJECT_MALFORMED and
+ (self.message == b"block" or self.message == b"tx")):
+ self.data = deser_uint256(f)
+
+ def serialize(self):
+ r = ser_string(self.message)
+ r += struct.pack("<B", self.code)
+ r += ser_string(self.reason)
+ if (self.code != self.REJECT_MALFORMED and
+ (self.message == b"block" or self.message == b"tx")):
+ r += ser_uint256(self.data)
+ return r
+
+ def __repr__(self):
+ return "msg_reject: %s %d %s [%064x]" \
+ % (self.message, self.code, self.reason, self.data)
+
+class msg_feefilter():
+ command = b"feefilter"
+
+ def __init__(self, feerate=0):
+ self.feerate = feerate
+
+ def deserialize(self, f):
+ self.feerate = struct.unpack("<Q", f.read(8))[0]
+
+ def serialize(self):
+ r = b""
+ r += struct.pack("<Q", self.feerate)
+ return r
+
+ def __repr__(self):
+ return "msg_feefilter(feerate=%08x)" % self.feerate
+
+class msg_sendcmpct():
+ command = b"sendcmpct"
+
+ def __init__(self):
+ self.announce = False
+ self.version = 1
+
+ def deserialize(self, f):
+ self.announce = struct.unpack("<?", f.read(1))[0]
+ self.version = struct.unpack("<Q", f.read(8))[0]
+
+ def serialize(self):
+ r = b""
+ r += struct.pack("<?", self.announce)
+ r += struct.pack("<Q", self.version)
+ return r
+
+ def __repr__(self):
+ return "msg_sendcmpct(announce=%s, version=%lu)" % (self.announce, self.version)
+
+class msg_cmpctblock():
+ command = b"cmpctblock"
+
+ def __init__(self, header_and_shortids = None):
+ self.header_and_shortids = header_and_shortids
+
+ def deserialize(self, f):
+ self.header_and_shortids = P2PHeaderAndShortIDs()
+ self.header_and_shortids.deserialize(f)
+
+ def serialize(self):
+ r = b""
+ r += self.header_and_shortids.serialize()
+ return r
+
+ def __repr__(self):
+ return "msg_cmpctblock(HeaderAndShortIDs=%s)" % repr(self.header_and_shortids)
+
+class msg_getblocktxn():
+ command = b"getblocktxn"
+
+ def __init__(self):
+ self.block_txn_request = None
+
+ def deserialize(self, f):
+ self.block_txn_request = BlockTransactionsRequest()
+ self.block_txn_request.deserialize(f)
+
+ def serialize(self):
+ r = b""
+ r += self.block_txn_request.serialize()
+ return r
+
+ def __repr__(self):
+ return "msg_getblocktxn(block_txn_request=%s)" % (repr(self.block_txn_request))
+
+class msg_blocktxn():
+ command = b"blocktxn"
+
+ def __init__(self):
+ self.block_transactions = BlockTransactions()
+
+ def deserialize(self, f):
+ self.block_transactions.deserialize(f)
+
+ def serialize(self):
+ r = b""
+ r += self.block_transactions.serialize(with_witness=False)
+ return r
+
+ def __repr__(self):
+ return "msg_blocktxn(block_transactions=%s)" % (repr(self.block_transactions))
+
+class msg_witness_blocktxn(msg_blocktxn):
+ def serialize(self):
+ r = b""
+ r += self.block_transactions.serialize(with_witness=True)
+ return r
diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py
index d072969d7f..fe14591139 100755
--- a/test/functional/test_framework/mininode.py
+++ b/test/functional/test_framework/mininode.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
# Copyright (c) 2010 ArtForz -- public domain half-a-node
# Copyright (c) 2012 Jeff Garzik
-# Copyright (c) 2010-2016 The Bitcoin Core developers
+# Copyright (c) 2010-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.
"""Bitcoin P2P network half-a-node.
@@ -9,1462 +9,252 @@
This python code was modified from ArtForz' public domain half-a-node, as
found in the mini-node branch of http://github.com/jgarzik/pynode.
-NodeConn: an object which manages p2p connectivity to a bitcoin node
-NodeConnCB: a base class that describes the interface for receiving
- callbacks with network messages from a NodeConn
-CBlock, CTransaction, CBlockHeader, CTxIn, CTxOut, etc....:
- data structures that should map to corresponding structures in
- bitcoin/primitives
-msg_block, msg_tx, msg_headers, etc.:
- data structures that represent network messages
-ser_*, deser_*: functions that handle serialization/deserialization
-"""
-
+P2PConnection: A low-level connection object to a node's P2P interface
+P2PInterface: A high-level interface object for communicating to a node over P2P"""
import asyncore
-from codecs import encode
from collections import defaultdict
-import copy
-import hashlib
from io import BytesIO
import logging
-import random
import socket
import struct
import sys
-import time
-from threading import RLock, Thread
-
-from test_framework.siphash import siphash256
-from test_framework.util import hex_str_to_bytes, bytes_to_hex_str, wait_until
-
-BIP0031_VERSION = 60000
-MY_VERSION = 70014 # past bip-31 for ping/pong
-MY_SUBVERSION = b"/python-mininode-tester:0.0.3/"
-MY_RELAY = 1 # from version 70001 onwards, fRelay should be appended to version messages (BIP37)
-
-MAX_INV_SZ = 50000
-MAX_BLOCK_BASE_SIZE = 1000000
+import threading
-COIN = 100000000 # 1 btc in satoshis
-
-NODE_NETWORK = (1 << 0)
-# NODE_GETUTXO = (1 << 1)
-# NODE_BLOOM = (1 << 2)
-NODE_WITNESS = (1 << 3)
-NODE_UNSUPPORTED_SERVICE_BIT_5 = (1 << 5)
-NODE_UNSUPPORTED_SERVICE_BIT_7 = (1 << 7)
+from test_framework.messages import *
+from test_framework.util import wait_until
logger = logging.getLogger("TestFramework.mininode")
-# Keep our own socket map for asyncore, so that we can track disconnects
-# ourselves (to workaround an issue with closing an asyncore socket when
-# using select)
-mininode_socket_map = dict()
-
-# One lock for synchronizing all data access between the networking thread (see
-# NetworkThread below) and the thread running the test logic. For simplicity,
-# NodeConn acquires this lock whenever delivering a message to a NodeConnCB,
-# and whenever adding anything to the send buffer (in send_message()). This
-# lock should be acquired in the thread running the test logic to synchronize
-# access to any data shared with the NodeConnCB or NodeConn.
-mininode_lock = RLock()
-
-# Serialization/deserialization tools
-def sha256(s):
- return hashlib.new('sha256', s).digest()
-
-def ripemd160(s):
- return hashlib.new('ripemd160', s).digest()
-
-def hash256(s):
- return sha256(sha256(s))
-
-def ser_compact_size(l):
- r = b""
- if l < 253:
- r = struct.pack("B", l)
- elif l < 0x10000:
- r = struct.pack("<BH", 253, l)
- elif l < 0x100000000:
- r = struct.pack("<BI", 254, l)
- else:
- r = struct.pack("<BQ", 255, l)
- return r
-
-def deser_compact_size(f):
- nit = struct.unpack("<B", f.read(1))[0]
- if nit == 253:
- nit = struct.unpack("<H", f.read(2))[0]
- elif nit == 254:
- nit = struct.unpack("<I", f.read(4))[0]
- elif nit == 255:
- nit = struct.unpack("<Q", f.read(8))[0]
- return nit
-
-def deser_string(f):
- nit = deser_compact_size(f)
- return f.read(nit)
-
-def ser_string(s):
- return ser_compact_size(len(s)) + s
-
-def deser_uint256(f):
- r = 0
- for i in range(8):
- t = struct.unpack("<I", f.read(4))[0]
- r += t << (i * 32)
- return r
-
-
-def ser_uint256(u):
- rs = b""
- for i in range(8):
- rs += struct.pack("<I", u & 0xFFFFFFFF)
- u >>= 32
- return rs
-
-
-def uint256_from_str(s):
- r = 0
- t = struct.unpack("<IIIIIIII", s[:32])
- for i in range(8):
- r += t[i] << (i * 32)
- return r
-
-
-def uint256_from_compact(c):
- nbytes = (c >> 24) & 0xFF
- v = (c & 0xFFFFFF) << (8 * (nbytes - 3))
- return v
-
-
-def deser_vector(f, c):
- nit = deser_compact_size(f)
- r = []
- for i in range(nit):
- t = c()
- t.deserialize(f)
- r.append(t)
- return r
-
-
-# ser_function_name: Allow for an alternate serialization function on the
-# entries in the vector (we use this for serializing the vector of transactions
-# for a witness block).
-def ser_vector(l, ser_function_name=None):
- r = ser_compact_size(len(l))
- for i in l:
- if ser_function_name:
- r += getattr(i, ser_function_name)()
- else:
- r += i.serialize()
- return r
-
-
-def deser_uint256_vector(f):
- nit = deser_compact_size(f)
- r = []
- for i in range(nit):
- t = deser_uint256(f)
- r.append(t)
- return r
-
-
-def ser_uint256_vector(l):
- r = ser_compact_size(len(l))
- for i in l:
- r += ser_uint256(i)
- return r
-
-
-def deser_string_vector(f):
- nit = deser_compact_size(f)
- r = []
- for i in range(nit):
- t = deser_string(f)
- r.append(t)
- return r
-
-
-def ser_string_vector(l):
- r = ser_compact_size(len(l))
- for sv in l:
- r += ser_string(sv)
- return r
-
-
-def deser_int_vector(f):
- nit = deser_compact_size(f)
- r = []
- for i in range(nit):
- t = struct.unpack("<i", f.read(4))[0]
- r.append(t)
- return r
-
-
-def ser_int_vector(l):
- r = ser_compact_size(len(l))
- for i in l:
- r += struct.pack("<i", i)
- return r
-
-# Deserialize from a hex string representation (eg from RPC)
-def FromHex(obj, hex_string):
- obj.deserialize(BytesIO(hex_str_to_bytes(hex_string)))
- return obj
-
-# Convert a binary-serializable object to hex (eg for submission via RPC)
-def ToHex(obj):
- return bytes_to_hex_str(obj.serialize())
-
-# Objects that map to bitcoind objects, which can be serialized/deserialized
-
-class CAddress(object):
- def __init__(self):
- self.nServices = 1
- self.pchReserved = b"\x00" * 10 + b"\xff" * 2
- self.ip = "0.0.0.0"
- self.port = 0
-
- def deserialize(self, f):
- self.nServices = struct.unpack("<Q", f.read(8))[0]
- self.pchReserved = f.read(12)
- self.ip = socket.inet_ntoa(f.read(4))
- self.port = struct.unpack(">H", f.read(2))[0]
-
- def serialize(self):
- r = b""
- r += struct.pack("<Q", self.nServices)
- r += self.pchReserved
- r += socket.inet_aton(self.ip)
- r += struct.pack(">H", self.port)
- return r
-
- def __repr__(self):
- return "CAddress(nServices=%i ip=%s port=%i)" % (self.nServices,
- self.ip, self.port)
-
-MSG_WITNESS_FLAG = 1<<30
-
-class CInv(object):
- typemap = {
- 0: "Error",
- 1: "TX",
- 2: "Block",
- 1|MSG_WITNESS_FLAG: "WitnessTx",
- 2|MSG_WITNESS_FLAG : "WitnessBlock",
- 4: "CompactBlock"
- }
-
- def __init__(self, t=0, h=0):
- self.type = t
- self.hash = h
-
- def deserialize(self, f):
- self.type = struct.unpack("<i", f.read(4))[0]
- self.hash = deser_uint256(f)
-
- def serialize(self):
- r = b""
- r += struct.pack("<i", self.type)
- r += ser_uint256(self.hash)
- return r
-
- def __repr__(self):
- return "CInv(type=%s hash=%064x)" \
- % (self.typemap[self.type], self.hash)
-
-
-class CBlockLocator(object):
- def __init__(self):
- self.nVersion = MY_VERSION
- self.vHave = []
-
- def deserialize(self, f):
- self.nVersion = struct.unpack("<i", f.read(4))[0]
- self.vHave = deser_uint256_vector(f)
-
- def serialize(self):
- r = b""
- r += struct.pack("<i", self.nVersion)
- r += ser_uint256_vector(self.vHave)
- return r
-
- def __repr__(self):
- return "CBlockLocator(nVersion=%i vHave=%s)" \
- % (self.nVersion, repr(self.vHave))
-
-
-class COutPoint(object):
- def __init__(self, hash=0, n=0):
- self.hash = hash
- self.n = n
-
- def deserialize(self, f):
- self.hash = deser_uint256(f)
- self.n = struct.unpack("<I", f.read(4))[0]
-
- def serialize(self):
- r = b""
- r += ser_uint256(self.hash)
- r += struct.pack("<I", self.n)
- return r
-
- def __repr__(self):
- return "COutPoint(hash=%064x n=%i)" % (self.hash, self.n)
-
-
-class CTxIn(object):
- def __init__(self, outpoint=None, scriptSig=b"", nSequence=0):
- if outpoint is None:
- self.prevout = COutPoint()
- else:
- self.prevout = outpoint
- self.scriptSig = scriptSig
- self.nSequence = nSequence
-
- def deserialize(self, f):
- self.prevout = COutPoint()
- self.prevout.deserialize(f)
- self.scriptSig = deser_string(f)
- self.nSequence = struct.unpack("<I", f.read(4))[0]
-
- def serialize(self):
- r = b""
- r += self.prevout.serialize()
- r += ser_string(self.scriptSig)
- r += struct.pack("<I", self.nSequence)
- return r
-
- def __repr__(self):
- return "CTxIn(prevout=%s scriptSig=%s nSequence=%i)" \
- % (repr(self.prevout), bytes_to_hex_str(self.scriptSig),
- self.nSequence)
-
-
-class CTxOut(object):
- def __init__(self, nValue=0, scriptPubKey=b""):
- self.nValue = nValue
- self.scriptPubKey = scriptPubKey
-
- def deserialize(self, f):
- self.nValue = struct.unpack("<q", f.read(8))[0]
- self.scriptPubKey = deser_string(f)
-
- def serialize(self):
- r = b""
- r += struct.pack("<q", self.nValue)
- r += ser_string(self.scriptPubKey)
- return r
-
- def __repr__(self):
- return "CTxOut(nValue=%i.%08i scriptPubKey=%s)" \
- % (self.nValue // COIN, self.nValue % COIN,
- bytes_to_hex_str(self.scriptPubKey))
-
-
-class CScriptWitness(object):
- def __init__(self):
- # stack is a vector of strings
- self.stack = []
-
- def __repr__(self):
- return "CScriptWitness(%s)" % \
- (",".join([bytes_to_hex_str(x) for x in self.stack]))
-
- def is_null(self):
- if self.stack:
- return False
- return True
-
-
-class CTxInWitness(object):
- def __init__(self):
- self.scriptWitness = CScriptWitness()
-
- def deserialize(self, f):
- self.scriptWitness.stack = deser_string_vector(f)
-
- def serialize(self):
- return ser_string_vector(self.scriptWitness.stack)
-
- def __repr__(self):
- return repr(self.scriptWitness)
-
- def is_null(self):
- return self.scriptWitness.is_null()
-
-
-class CTxWitness(object):
- def __init__(self):
- self.vtxinwit = []
-
- def deserialize(self, f):
- for i in range(len(self.vtxinwit)):
- self.vtxinwit[i].deserialize(f)
-
- def serialize(self):
- r = b""
- # This is different than the usual vector serialization --
- # we omit the length of the vector, which is required to be
- # the same length as the transaction's vin vector.
- for x in self.vtxinwit:
- r += x.serialize()
- return r
-
- def __repr__(self):
- return "CTxWitness(%s)" % \
- (';'.join([repr(x) for x in self.vtxinwit]))
-
- def is_null(self):
- for x in self.vtxinwit:
- if not x.is_null():
- return False
- return True
-
-
-class CTransaction(object):
- def __init__(self, tx=None):
- if tx is None:
- self.nVersion = 1
- self.vin = []
- self.vout = []
- self.wit = CTxWitness()
- self.nLockTime = 0
- self.sha256 = None
- self.hash = None
- else:
- self.nVersion = tx.nVersion
- self.vin = copy.deepcopy(tx.vin)
- self.vout = copy.deepcopy(tx.vout)
- self.nLockTime = tx.nLockTime
- self.sha256 = tx.sha256
- self.hash = tx.hash
- self.wit = copy.deepcopy(tx.wit)
-
- def deserialize(self, f):
- self.nVersion = struct.unpack("<i", f.read(4))[0]
- self.vin = deser_vector(f, CTxIn)
- flags = 0
- if len(self.vin) == 0:
- flags = struct.unpack("<B", f.read(1))[0]
- # Not sure why flags can't be zero, but this
- # matches the implementation in bitcoind
- if (flags != 0):
- self.vin = deser_vector(f, CTxIn)
- self.vout = deser_vector(f, CTxOut)
- else:
- self.vout = deser_vector(f, CTxOut)
- if flags != 0:
- self.wit.vtxinwit = [CTxInWitness() for i in range(len(self.vin))]
- self.wit.deserialize(f)
- self.nLockTime = struct.unpack("<I", f.read(4))[0]
- self.sha256 = None
- self.hash = None
-
- def serialize_without_witness(self):
- r = b""
- r += struct.pack("<i", self.nVersion)
- r += ser_vector(self.vin)
- r += ser_vector(self.vout)
- r += struct.pack("<I", self.nLockTime)
- return r
-
- # Only serialize with witness when explicitly called for
- def serialize_with_witness(self):
- flags = 0
- if not self.wit.is_null():
- flags |= 1
- r = b""
- r += struct.pack("<i", self.nVersion)
- if flags:
- dummy = []
- r += ser_vector(dummy)
- r += struct.pack("<B", flags)
- r += ser_vector(self.vin)
- r += ser_vector(self.vout)
- if flags & 1:
- if (len(self.wit.vtxinwit) != len(self.vin)):
- # vtxinwit must have the same length as vin
- self.wit.vtxinwit = self.wit.vtxinwit[:len(self.vin)]
- for i in range(len(self.wit.vtxinwit), len(self.vin)):
- self.wit.vtxinwit.append(CTxInWitness())
- r += self.wit.serialize()
- r += struct.pack("<I", self.nLockTime)
- return r
-
- # Regular serialization is without witness -- must explicitly
- # call serialize_with_witness to include witness data.
- def serialize(self):
- return self.serialize_without_witness()
-
- # Recalculate the txid (transaction hash without witness)
- def rehash(self):
- self.sha256 = None
- self.calc_sha256()
-
- # We will only cache the serialization without witness in
- # self.sha256 and self.hash -- those are expected to be the txid.
- def calc_sha256(self, with_witness=False):
- if with_witness:
- # Don't cache the result, just return it
- return uint256_from_str(hash256(self.serialize_with_witness()))
-
- if self.sha256 is None:
- self.sha256 = uint256_from_str(hash256(self.serialize_without_witness()))
- self.hash = encode(hash256(self.serialize())[::-1], 'hex_codec').decode('ascii')
-
- def is_valid(self):
- self.calc_sha256()
- for tout in self.vout:
- if tout.nValue < 0 or tout.nValue > 21000000 * COIN:
- return False
- return True
-
- def __repr__(self):
- return "CTransaction(nVersion=%i vin=%s vout=%s wit=%s nLockTime=%i)" \
- % (self.nVersion, repr(self.vin), repr(self.vout), repr(self.wit), self.nLockTime)
-
-
-class CBlockHeader(object):
- def __init__(self, header=None):
- if header is None:
- self.set_null()
- else:
- self.nVersion = header.nVersion
- self.hashPrevBlock = header.hashPrevBlock
- self.hashMerkleRoot = header.hashMerkleRoot
- self.nTime = header.nTime
- self.nBits = header.nBits
- self.nNonce = header.nNonce
- self.sha256 = header.sha256
- self.hash = header.hash
- self.calc_sha256()
-
- def set_null(self):
- self.nVersion = 1
- self.hashPrevBlock = 0
- self.hashMerkleRoot = 0
- self.nTime = 0
- self.nBits = 0
- self.nNonce = 0
- self.sha256 = None
- self.hash = None
-
- def deserialize(self, f):
- self.nVersion = struct.unpack("<i", f.read(4))[0]
- self.hashPrevBlock = deser_uint256(f)
- self.hashMerkleRoot = deser_uint256(f)
- self.nTime = struct.unpack("<I", f.read(4))[0]
- self.nBits = struct.unpack("<I", f.read(4))[0]
- self.nNonce = struct.unpack("<I", f.read(4))[0]
- self.sha256 = None
- self.hash = None
-
- def serialize(self):
- r = b""
- r += struct.pack("<i", self.nVersion)
- r += ser_uint256(self.hashPrevBlock)
- r += ser_uint256(self.hashMerkleRoot)
- r += struct.pack("<I", self.nTime)
- r += struct.pack("<I", self.nBits)
- r += struct.pack("<I", self.nNonce)
- return r
-
- def calc_sha256(self):
- if self.sha256 is None:
- r = b""
- r += struct.pack("<i", self.nVersion)
- r += ser_uint256(self.hashPrevBlock)
- r += ser_uint256(self.hashMerkleRoot)
- r += struct.pack("<I", self.nTime)
- r += struct.pack("<I", self.nBits)
- r += struct.pack("<I", self.nNonce)
- self.sha256 = uint256_from_str(hash256(r))
- self.hash = encode(hash256(r)[::-1], 'hex_codec').decode('ascii')
-
- def rehash(self):
- self.sha256 = None
- self.calc_sha256()
- return self.sha256
-
- def __repr__(self):
- return "CBlockHeader(nVersion=%i hashPrevBlock=%064x hashMerkleRoot=%064x nTime=%s nBits=%08x nNonce=%08x)" \
- % (self.nVersion, self.hashPrevBlock, self.hashMerkleRoot,
- time.ctime(self.nTime), self.nBits, self.nNonce)
-
-
-class CBlock(CBlockHeader):
- def __init__(self, header=None):
- super(CBlock, self).__init__(header)
- self.vtx = []
-
- def deserialize(self, f):
- super(CBlock, self).deserialize(f)
- self.vtx = deser_vector(f, CTransaction)
-
- def serialize(self, with_witness=False):
- r = b""
- r += super(CBlock, self).serialize()
- if with_witness:
- r += ser_vector(self.vtx, "serialize_with_witness")
- else:
- r += ser_vector(self.vtx)
- return r
-
- # Calculate the merkle root given a vector of transaction hashes
- @classmethod
- def get_merkle_root(cls, hashes):
- while len(hashes) > 1:
- newhashes = []
- for i in range(0, len(hashes), 2):
- i2 = min(i+1, len(hashes)-1)
- newhashes.append(hash256(hashes[i] + hashes[i2]))
- hashes = newhashes
- return uint256_from_str(hashes[0])
-
- def calc_merkle_root(self):
- hashes = []
- for tx in self.vtx:
- tx.calc_sha256()
- hashes.append(ser_uint256(tx.sha256))
- return self.get_merkle_root(hashes)
-
- def calc_witness_merkle_root(self):
- # For witness root purposes, the hash of the
- # coinbase, with witness, is defined to be 0...0
- hashes = [ser_uint256(0)]
-
- for tx in self.vtx[1:]:
- # Calculate the hashes with witness data
- hashes.append(ser_uint256(tx.calc_sha256(True)))
-
- return self.get_merkle_root(hashes)
-
- def is_valid(self):
- self.calc_sha256()
- target = uint256_from_compact(self.nBits)
- if self.sha256 > target:
- return False
- for tx in self.vtx:
- if not tx.is_valid():
- return False
- if self.calc_merkle_root() != self.hashMerkleRoot:
- return False
- return True
-
- def solve(self):
- self.rehash()
- target = uint256_from_compact(self.nBits)
- while self.sha256 > target:
- self.nNonce += 1
- self.rehash()
-
- def __repr__(self):
- return "CBlock(nVersion=%i hashPrevBlock=%064x hashMerkleRoot=%064x nTime=%s nBits=%08x nNonce=%08x vtx=%s)" \
- % (self.nVersion, self.hashPrevBlock, self.hashMerkleRoot,
- time.ctime(self.nTime), self.nBits, self.nNonce, repr(self.vtx))
-
-
-class CUnsignedAlert(object):
- def __init__(self):
- self.nVersion = 1
- self.nRelayUntil = 0
- self.nExpiration = 0
- self.nID = 0
- self.nCancel = 0
- self.setCancel = []
- self.nMinVer = 0
- self.nMaxVer = 0
- self.setSubVer = []
- self.nPriority = 0
- self.strComment = b""
- self.strStatusBar = b""
- self.strReserved = b""
-
- def deserialize(self, f):
- self.nVersion = struct.unpack("<i", f.read(4))[0]
- self.nRelayUntil = struct.unpack("<q", f.read(8))[0]
- self.nExpiration = struct.unpack("<q", f.read(8))[0]
- self.nID = struct.unpack("<i", f.read(4))[0]
- self.nCancel = struct.unpack("<i", f.read(4))[0]
- self.setCancel = deser_int_vector(f)
- self.nMinVer = struct.unpack("<i", f.read(4))[0]
- self.nMaxVer = struct.unpack("<i", f.read(4))[0]
- self.setSubVer = deser_string_vector(f)
- self.nPriority = struct.unpack("<i", f.read(4))[0]
- self.strComment = deser_string(f)
- self.strStatusBar = deser_string(f)
- self.strReserved = deser_string(f)
-
- def serialize(self):
- r = b""
- r += struct.pack("<i", self.nVersion)
- r += struct.pack("<q", self.nRelayUntil)
- r += struct.pack("<q", self.nExpiration)
- r += struct.pack("<i", self.nID)
- r += struct.pack("<i", self.nCancel)
- r += ser_int_vector(self.setCancel)
- r += struct.pack("<i", self.nMinVer)
- r += struct.pack("<i", self.nMaxVer)
- r += ser_string_vector(self.setSubVer)
- r += struct.pack("<i", self.nPriority)
- r += ser_string(self.strComment)
- r += ser_string(self.strStatusBar)
- r += ser_string(self.strReserved)
- return r
-
- def __repr__(self):
- return "CUnsignedAlert(nVersion %d, nRelayUntil %d, nExpiration %d, nID %d, nCancel %d, nMinVer %d, nMaxVer %d, nPriority %d, strComment %s, strStatusBar %s, strReserved %s)" \
- % (self.nVersion, self.nRelayUntil, self.nExpiration, self.nID,
- self.nCancel, self.nMinVer, self.nMaxVer, self.nPriority,
- self.strComment, self.strStatusBar, self.strReserved)
-
-
-class CAlert(object):
- def __init__(self):
- self.vchMsg = b""
- self.vchSig = b""
-
- def deserialize(self, f):
- self.vchMsg = deser_string(f)
- self.vchSig = deser_string(f)
-
- def serialize(self):
- r = b""
- r += ser_string(self.vchMsg)
- r += ser_string(self.vchSig)
- return r
-
- def __repr__(self):
- return "CAlert(vchMsg.sz %d, vchSig.sz %d)" \
- % (len(self.vchMsg), len(self.vchSig))
-
-
-class PrefilledTransaction(object):
- def __init__(self, index=0, tx = None):
- self.index = index
- self.tx = tx
-
- def deserialize(self, f):
- self.index = deser_compact_size(f)
- self.tx = CTransaction()
- self.tx.deserialize(f)
-
- def serialize(self, with_witness=False):
- r = b""
- r += ser_compact_size(self.index)
- if with_witness:
- r += self.tx.serialize_with_witness()
- else:
- r += self.tx.serialize_without_witness()
- return r
-
- def serialize_with_witness(self):
- return self.serialize(with_witness=True)
-
- def __repr__(self):
- return "PrefilledTransaction(index=%d, tx=%s)" % (self.index, repr(self.tx))
-
-# This is what we send on the wire, in a cmpctblock message.
-class P2PHeaderAndShortIDs(object):
- def __init__(self):
- self.header = CBlockHeader()
- self.nonce = 0
- self.shortids_length = 0
- self.shortids = []
- self.prefilled_txn_length = 0
- self.prefilled_txn = []
-
- def deserialize(self, f):
- self.header.deserialize(f)
- self.nonce = struct.unpack("<Q", f.read(8))[0]
- self.shortids_length = deser_compact_size(f)
- for i in range(self.shortids_length):
- # shortids are defined to be 6 bytes in the spec, so append
- # two zero bytes and read it in as an 8-byte number
- self.shortids.append(struct.unpack("<Q", f.read(6) + b'\x00\x00')[0])
- self.prefilled_txn = deser_vector(f, PrefilledTransaction)
- self.prefilled_txn_length = len(self.prefilled_txn)
-
- # When using version 2 compact blocks, we must serialize with_witness.
- def serialize(self, with_witness=False):
- r = b""
- r += self.header.serialize()
- r += struct.pack("<Q", self.nonce)
- r += ser_compact_size(self.shortids_length)
- for x in self.shortids:
- # We only want the first 6 bytes
- r += struct.pack("<Q", x)[0:6]
- if with_witness:
- r += ser_vector(self.prefilled_txn, "serialize_with_witness")
- else:
- r += ser_vector(self.prefilled_txn)
- return r
-
- def __repr__(self):
- return "P2PHeaderAndShortIDs(header=%s, nonce=%d, shortids_length=%d, shortids=%s, prefilled_txn_length=%d, prefilledtxn=%s" % (repr(self.header), self.nonce, self.shortids_length, repr(self.shortids), self.prefilled_txn_length, repr(self.prefilled_txn))
-
-# P2P version of the above that will use witness serialization (for compact
-# block version 2)
-class P2PHeaderAndShortWitnessIDs(P2PHeaderAndShortIDs):
- def serialize(self):
- return super(P2PHeaderAndShortWitnessIDs, self).serialize(with_witness=True)
-
-# Calculate the BIP 152-compact blocks shortid for a given transaction hash
-def calculate_shortid(k0, k1, tx_hash):
- expected_shortid = siphash256(k0, k1, tx_hash)
- expected_shortid &= 0x0000ffffffffffff
- return expected_shortid
-
-# This version gets rid of the array lengths, and reinterprets the differential
-# encoding into indices that can be used for lookup.
-class HeaderAndShortIDs(object):
- def __init__(self, p2pheaders_and_shortids = None):
- self.header = CBlockHeader()
- self.nonce = 0
- self.shortids = []
- self.prefilled_txn = []
- self.use_witness = False
-
- if p2pheaders_and_shortids != None:
- self.header = p2pheaders_and_shortids.header
- self.nonce = p2pheaders_and_shortids.nonce
- self.shortids = p2pheaders_and_shortids.shortids
- last_index = -1
- for x in p2pheaders_and_shortids.prefilled_txn:
- self.prefilled_txn.append(PrefilledTransaction(x.index + last_index + 1, x.tx))
- last_index = self.prefilled_txn[-1].index
-
- def to_p2p(self):
- if self.use_witness:
- ret = P2PHeaderAndShortWitnessIDs()
- else:
- ret = P2PHeaderAndShortIDs()
- ret.header = self.header
- ret.nonce = self.nonce
- ret.shortids_length = len(self.shortids)
- ret.shortids = self.shortids
- ret.prefilled_txn_length = len(self.prefilled_txn)
- ret.prefilled_txn = []
- last_index = -1
- for x in self.prefilled_txn:
- ret.prefilled_txn.append(PrefilledTransaction(x.index - last_index - 1, x.tx))
- last_index = x.index
- return ret
-
- def get_siphash_keys(self):
- header_nonce = self.header.serialize()
- header_nonce += struct.pack("<Q", self.nonce)
- hash_header_nonce_as_str = sha256(header_nonce)
- key0 = struct.unpack("<Q", hash_header_nonce_as_str[0:8])[0]
- key1 = struct.unpack("<Q", hash_header_nonce_as_str[8:16])[0]
- return [ key0, key1 ]
-
- # Version 2 compact blocks use wtxid in shortids (rather than txid)
- def initialize_from_block(self, block, nonce=0, prefill_list = [0], use_witness = False):
- self.header = CBlockHeader(block)
- self.nonce = nonce
- self.prefilled_txn = [ PrefilledTransaction(i, block.vtx[i]) for i in prefill_list ]
- self.shortids = []
- self.use_witness = use_witness
- [k0, k1] = self.get_siphash_keys()
- for i in range(len(block.vtx)):
- if i not in prefill_list:
- tx_hash = block.vtx[i].sha256
- if use_witness:
- tx_hash = block.vtx[i].calc_sha256(with_witness=True)
- self.shortids.append(calculate_shortid(k0, k1, tx_hash))
-
- def __repr__(self):
- return "HeaderAndShortIDs(header=%s, nonce=%d, shortids=%s, prefilledtxn=%s" % (repr(self.header), self.nonce, repr(self.shortids), repr(self.prefilled_txn))
-
-
-class BlockTransactionsRequest(object):
-
- def __init__(self, blockhash=0, indexes = None):
- self.blockhash = blockhash
- self.indexes = indexes if indexes != None else []
-
- def deserialize(self, f):
- self.blockhash = deser_uint256(f)
- indexes_length = deser_compact_size(f)
- for i in range(indexes_length):
- self.indexes.append(deser_compact_size(f))
-
- def serialize(self):
- r = b""
- r += ser_uint256(self.blockhash)
- r += ser_compact_size(len(self.indexes))
- for x in self.indexes:
- r += ser_compact_size(x)
- return r
-
- # helper to set the differentially encoded indexes from absolute ones
- def from_absolute(self, absolute_indexes):
- self.indexes = []
- last_index = -1
- for x in absolute_indexes:
- self.indexes.append(x-last_index-1)
- last_index = x
-
- def to_absolute(self):
- absolute_indexes = []
- last_index = -1
- for x in self.indexes:
- absolute_indexes.append(x+last_index+1)
- last_index = absolute_indexes[-1]
- return absolute_indexes
-
- def __repr__(self):
- return "BlockTransactionsRequest(hash=%064x indexes=%s)" % (self.blockhash, repr(self.indexes))
-
-
-class BlockTransactions(object):
-
- def __init__(self, blockhash=0, transactions = None):
- self.blockhash = blockhash
- self.transactions = transactions if transactions != None else []
-
- def deserialize(self, f):
- self.blockhash = deser_uint256(f)
- self.transactions = deser_vector(f, CTransaction)
-
- def serialize(self, with_witness=False):
- r = b""
- r += ser_uint256(self.blockhash)
- if with_witness:
- r += ser_vector(self.transactions, "serialize_with_witness")
- else:
- r += ser_vector(self.transactions)
- return r
-
- def __repr__(self):
- return "BlockTransactions(hash=%064x transactions=%s)" % (self.blockhash, repr(self.transactions))
-
-
-# Objects that correspond to messages on the wire
-class msg_version(object):
- command = b"version"
-
- def __init__(self):
- self.nVersion = MY_VERSION
- self.nServices = 1
- self.nTime = int(time.time())
- self.addrTo = CAddress()
- self.addrFrom = CAddress()
- self.nNonce = random.getrandbits(64)
- self.strSubVer = MY_SUBVERSION
- self.nStartingHeight = -1
- self.nRelay = MY_RELAY
-
- def deserialize(self, f):
- self.nVersion = struct.unpack("<i", f.read(4))[0]
- if self.nVersion == 10300:
- self.nVersion = 300
- self.nServices = struct.unpack("<Q", f.read(8))[0]
- self.nTime = struct.unpack("<q", f.read(8))[0]
- self.addrTo = CAddress()
- self.addrTo.deserialize(f)
-
- if self.nVersion >= 106:
- self.addrFrom = CAddress()
- self.addrFrom.deserialize(f)
- self.nNonce = struct.unpack("<Q", f.read(8))[0]
- self.strSubVer = deser_string(f)
- else:
- self.addrFrom = None
- self.nNonce = None
- self.strSubVer = None
- self.nStartingHeight = None
-
- if self.nVersion >= 209:
- self.nStartingHeight = struct.unpack("<i", f.read(4))[0]
- else:
- self.nStartingHeight = None
-
- if self.nVersion >= 70001:
- # Relay field is optional for version 70001 onwards
- try:
- self.nRelay = struct.unpack("<b", f.read(1))[0]
- except:
- self.nRelay = 0
- else:
- self.nRelay = 0
-
- def serialize(self):
- r = b""
- r += struct.pack("<i", self.nVersion)
- r += struct.pack("<Q", self.nServices)
- r += struct.pack("<q", self.nTime)
- r += self.addrTo.serialize()
- r += self.addrFrom.serialize()
- r += struct.pack("<Q", self.nNonce)
- r += ser_string(self.strSubVer)
- r += struct.pack("<i", self.nStartingHeight)
- r += struct.pack("<b", self.nRelay)
- return r
-
- def __repr__(self):
- return 'msg_version(nVersion=%i nServices=%i nTime=%s addrTo=%s addrFrom=%s nNonce=0x%016X strSubVer=%s nStartingHeight=%i nRelay=%i)' \
- % (self.nVersion, self.nServices, time.ctime(self.nTime),
- repr(self.addrTo), repr(self.addrFrom), self.nNonce,
- self.strSubVer, self.nStartingHeight, self.nRelay)
-
-
-class msg_verack(object):
- command = b"verack"
-
- def __init__(self):
- pass
-
- def deserialize(self, f):
- pass
-
- def serialize(self):
- return b""
-
- def __repr__(self):
- return "msg_verack()"
-
-
-class msg_addr(object):
- command = b"addr"
-
- def __init__(self):
- self.addrs = []
-
- def deserialize(self, f):
- self.addrs = deser_vector(f, CAddress)
-
- def serialize(self):
- return ser_vector(self.addrs)
-
- def __repr__(self):
- return "msg_addr(addrs=%s)" % (repr(self.addrs))
-
-
-class msg_alert(object):
- command = b"alert"
-
- def __init__(self):
- self.alert = CAlert()
-
- def deserialize(self, f):
- self.alert = CAlert()
- self.alert.deserialize(f)
-
- def serialize(self):
- r = b""
- r += self.alert.serialize()
- return r
-
- def __repr__(self):
- return "msg_alert(alert=%s)" % (repr(self.alert), )
-
-
-class msg_inv(object):
- command = b"inv"
-
- def __init__(self, inv=None):
- if inv is None:
- self.inv = []
- else:
- self.inv = inv
-
- def deserialize(self, f):
- self.inv = deser_vector(f, CInv)
-
- def serialize(self):
- return ser_vector(self.inv)
-
- def __repr__(self):
- return "msg_inv(inv=%s)" % (repr(self.inv))
-
-
-class msg_getdata(object):
- command = b"getdata"
-
- def __init__(self, inv=None):
- self.inv = inv if inv != None else []
-
- def deserialize(self, f):
- self.inv = deser_vector(f, CInv)
-
- def serialize(self):
- return ser_vector(self.inv)
-
- def __repr__(self):
- return "msg_getdata(inv=%s)" % (repr(self.inv))
-
-
-class msg_getblocks(object):
- command = b"getblocks"
-
- def __init__(self):
- self.locator = CBlockLocator()
- self.hashstop = 0
-
- def deserialize(self, f):
- self.locator = CBlockLocator()
- self.locator.deserialize(f)
- self.hashstop = deser_uint256(f)
-
- def serialize(self):
- r = b""
- r += self.locator.serialize()
- r += ser_uint256(self.hashstop)
- return r
-
- def __repr__(self):
- return "msg_getblocks(locator=%s hashstop=%064x)" \
- % (repr(self.locator), self.hashstop)
-
-
-class msg_tx(object):
- command = b"tx"
-
- def __init__(self, tx=CTransaction()):
- self.tx = tx
-
- def deserialize(self, f):
- self.tx.deserialize(f)
-
- def serialize(self):
- return self.tx.serialize_without_witness()
-
- def __repr__(self):
- return "msg_tx(tx=%s)" % (repr(self.tx))
-
-class msg_witness_tx(msg_tx):
-
- def serialize(self):
- return self.tx.serialize_with_witness()
-
-
-class msg_block(object):
- command = b"block"
-
- def __init__(self, block=None):
- if block is None:
- self.block = CBlock()
- else:
- self.block = block
-
- def deserialize(self, f):
- self.block.deserialize(f)
-
- def serialize(self):
- return self.block.serialize()
-
- def __repr__(self):
- return "msg_block(block=%s)" % (repr(self.block))
-
-# for cases where a user needs tighter control over what is sent over the wire
-# note that the user must supply the name of the command, and the data
-class msg_generic(object):
- def __init__(self, command, data=None):
- self.command = command
- self.data = data
-
- def serialize(self):
- return self.data
-
- def __repr__(self):
- return "msg_generic()"
-
-class msg_witness_block(msg_block):
-
- def serialize(self):
- r = self.block.serialize(with_witness=True)
- return r
-
-class msg_getaddr(object):
- command = b"getaddr"
-
- def __init__(self):
- pass
-
- def deserialize(self, f):
- pass
-
- def serialize(self):
- return b""
-
- def __repr__(self):
- return "msg_getaddr()"
-
-
-class msg_ping_prebip31(object):
- command = b"ping"
+MESSAGEMAP = {
+ b"addr": msg_addr,
+ b"block": msg_block,
+ b"blocktxn": msg_blocktxn,
+ b"cmpctblock": msg_cmpctblock,
+ b"feefilter": msg_feefilter,
+ b"getaddr": msg_getaddr,
+ b"getblocks": msg_getblocks,
+ b"getblocktxn": msg_getblocktxn,
+ b"getdata": msg_getdata,
+ b"getheaders": msg_getheaders,
+ b"headers": msg_headers,
+ b"inv": msg_inv,
+ b"mempool": msg_mempool,
+ b"ping": msg_ping,
+ b"pong": msg_pong,
+ b"reject": msg_reject,
+ b"sendcmpct": msg_sendcmpct,
+ b"sendheaders": msg_sendheaders,
+ b"tx": msg_tx,
+ b"verack": msg_verack,
+ b"version": msg_version,
+}
+
+MAGIC_BYTES = {
+ "mainnet": b"\xf9\xbe\xb4\xd9", # mainnet
+ "testnet3": b"\x0b\x11\x09\x07", # testnet3
+ "regtest": b"\xfa\xbf\xb5\xda", # regtest
+}
+
+class P2PConnection(asyncore.dispatcher):
+ """A low-level connection object to a node's P2P interface.
+
+ This class is responsible for:
+
+ - opening and closing the TCP connection to the node
+ - reading bytes from and writing bytes to the socket
+ - deserializing and serializing the P2P message header
+ - logging messages as they are sent and received
+
+ This class contains no logic for handing the P2P message payloads. It must be
+ sub-classed and the on_message() callback overridden."""
def __init__(self):
- pass
-
- def deserialize(self, f):
- pass
-
- def serialize(self):
- return b""
-
- def __repr__(self):
- return "msg_ping() (pre-bip31)"
-
-
-class msg_ping(object):
- command = b"ping"
-
- def __init__(self, nonce=0):
- self.nonce = nonce
-
- def deserialize(self, f):
- self.nonce = struct.unpack("<Q", f.read(8))[0]
-
- def serialize(self):
- r = b""
- r += struct.pack("<Q", self.nonce)
- return r
-
- def __repr__(self):
- return "msg_ping(nonce=%08x)" % self.nonce
-
-
-class msg_pong(object):
- command = b"pong"
-
- def __init__(self, nonce=0):
- self.nonce = nonce
-
- def deserialize(self, f):
- self.nonce = struct.unpack("<Q", f.read(8))[0]
-
- def serialize(self):
- r = b""
- r += struct.pack("<Q", self.nonce)
- return r
-
- def __repr__(self):
- return "msg_pong(nonce=%08x)" % self.nonce
-
-
-class msg_mempool(object):
- command = b"mempool"
-
- def __init__(self):
- pass
-
- def deserialize(self, f):
- pass
-
- def serialize(self):
- return b""
-
- def __repr__(self):
- return "msg_mempool()"
-
-class msg_sendheaders(object):
- command = b"sendheaders"
-
- def __init__(self):
- pass
-
- def deserialize(self, f):
- pass
-
- def serialize(self):
- return b""
-
- def __repr__(self):
- return "msg_sendheaders()"
-
-
-# getheaders message has
-# number of entries
-# vector of hashes
-# hash_stop (hash of last desired block header, 0 to get as many as possible)
-class msg_getheaders(object):
- command = b"getheaders"
-
- def __init__(self):
- self.locator = CBlockLocator()
- self.hashstop = 0
-
- def deserialize(self, f):
- self.locator = CBlockLocator()
- self.locator.deserialize(f)
- self.hashstop = deser_uint256(f)
-
- def serialize(self):
- r = b""
- r += self.locator.serialize()
- r += ser_uint256(self.hashstop)
- return r
-
- def __repr__(self):
- return "msg_getheaders(locator=%s, stop=%064x)" \
- % (repr(self.locator), self.hashstop)
+ # All P2PConnections must be created before starting the NetworkThread.
+ # assert that the network thread is not running.
+ assert not network_thread_running()
+ super().__init__(map=mininode_socket_map)
-# headers message has
-# <count> <vector of block headers>
-class msg_headers(object):
- command = b"headers"
-
- def __init__(self):
- self.headers = []
-
- def deserialize(self, f):
- # comment in bitcoind indicates these should be deserialized as blocks
- blocks = deser_vector(f, CBlock)
- for x in blocks:
- self.headers.append(CBlockHeader(x))
-
- def serialize(self):
- blocks = [CBlock(x) for x in self.headers]
- return ser_vector(blocks)
-
- def __repr__(self):
- return "msg_headers(headers=%s)" % repr(self.headers)
+ def peer_connect(self, dstaddr, dstport, net="regtest"):
+ self.dstaddr = dstaddr
+ self.dstport = dstport
+ self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
+ self.sendbuf = b""
+ self.recvbuf = b""
+ self.state = "connecting"
+ self.network = net
+ self.disconnect = False
+ logger.info('Connecting to Bitcoin Node: %s:%d' % (self.dstaddr, self.dstport))
-class msg_reject(object):
- command = b"reject"
- REJECT_MALFORMED = 1
+ try:
+ self.connect((dstaddr, dstport))
+ except:
+ self.handle_close()
- def __init__(self):
- self.message = b""
- self.code = 0
- self.reason = b""
- self.data = 0
-
- def deserialize(self, f):
- self.message = deser_string(f)
- self.code = struct.unpack("<B", f.read(1))[0]
- self.reason = deser_string(f)
- if (self.code != self.REJECT_MALFORMED and
- (self.message == b"block" or self.message == b"tx")):
- self.data = deser_uint256(f)
-
- def serialize(self):
- r = ser_string(self.message)
- r += struct.pack("<B", self.code)
- r += ser_string(self.reason)
- if (self.code != self.REJECT_MALFORMED and
- (self.message == b"block" or self.message == b"tx")):
- r += ser_uint256(self.data)
- return r
-
- def __repr__(self):
- return "msg_reject: %s %d %s [%064x]" \
- % (self.message, self.code, self.reason, self.data)
-
-class msg_feefilter(object):
- command = b"feefilter"
-
- def __init__(self, feerate=0):
- self.feerate = feerate
-
- def deserialize(self, f):
- self.feerate = struct.unpack("<Q", f.read(8))[0]
-
- def serialize(self):
- r = b""
- r += struct.pack("<Q", self.feerate)
- return r
-
- def __repr__(self):
- return "msg_feefilter(feerate=%08x)" % self.feerate
-
-class msg_sendcmpct(object):
- command = b"sendcmpct"
+ def peer_disconnect(self):
+ # Connection could have already been closed by other end.
+ if self.state == "connected":
+ self.disconnect_node()
- def __init__(self):
- self.announce = False
- self.version = 1
+ # Connection and disconnection methods
- def deserialize(self, f):
- self.announce = struct.unpack("<?", f.read(1))[0]
- self.version = struct.unpack("<Q", f.read(8))[0]
+ def handle_connect(self):
+ """asyncore callback when a connection is opened."""
+ if self.state != "connected":
+ logger.debug("Connected & Listening: %s:%d" % (self.dstaddr, self.dstport))
+ self.state = "connected"
+ self.on_open()
- def serialize(self):
- r = b""
- r += struct.pack("<?", self.announce)
- r += struct.pack("<Q", self.version)
- return r
+ def handle_close(self):
+ """asyncore callback when a connection is closed."""
+ logger.debug("Closing connection to: %s:%d" % (self.dstaddr, self.dstport))
+ self.state = "closed"
+ self.recvbuf = b""
+ self.sendbuf = b""
+ try:
+ self.close()
+ except:
+ pass
+ self.on_close()
- def __repr__(self):
- return "msg_sendcmpct(announce=%s, version=%lu)" % (self.announce, self.version)
+ def disconnect_node(self):
+ """Disconnect the p2p connection.
-class msg_cmpctblock(object):
- command = b"cmpctblock"
+ Called by the test logic thread. Causes the p2p connection
+ to be disconnected on the next iteration of the asyncore loop."""
+ self.disconnect = True
- def __init__(self, header_and_shortids = None):
- self.header_and_shortids = header_and_shortids
+ # Socket read methods
- def deserialize(self, f):
- self.header_and_shortids = P2PHeaderAndShortIDs()
- self.header_and_shortids.deserialize(f)
+ def handle_read(self):
+ """asyncore callback when data is read from the socket."""
+ t = self.recv(8192)
+ if len(t) > 0:
+ self.recvbuf += t
+ self._on_data()
- def serialize(self):
- r = b""
- r += self.header_and_shortids.serialize()
- return r
+ def _on_data(self):
+ """Try to read P2P messages from the recv buffer.
- def __repr__(self):
- return "msg_cmpctblock(HeaderAndShortIDs=%s)" % repr(self.header_and_shortids)
+ This method reads data from the buffer in a loop. It deserializes,
+ parses and verifies the P2P header, then passes the P2P payload to
+ the on_message callback for processing."""
+ try:
+ while True:
+ if len(self.recvbuf) < 4:
+ return
+ if self.recvbuf[:4] != MAGIC_BYTES[self.network]:
+ raise ValueError("got garbage %s" % repr(self.recvbuf))
+ if len(self.recvbuf) < 4 + 12 + 4 + 4:
+ return
+ command = self.recvbuf[4:4+12].split(b"\x00", 1)[0]
+ msglen = struct.unpack("<i", self.recvbuf[4+12:4+12+4])[0]
+ checksum = self.recvbuf[4+12+4:4+12+4+4]
+ if len(self.recvbuf) < 4 + 12 + 4 + 4 + msglen:
+ return
+ msg = self.recvbuf[4+12+4+4:4+12+4+4+msglen]
+ th = sha256(msg)
+ h = sha256(th)
+ if checksum != h[:4]:
+ raise ValueError("got bad checksum " + repr(self.recvbuf))
+ self.recvbuf = self.recvbuf[4+12+4+4+msglen:]
+ if command not in MESSAGEMAP:
+ raise ValueError("Received unknown command from %s:%d: '%s' %s" % (self.dstaddr, self.dstport, command, repr(msg)))
+ f = BytesIO(msg)
+ t = MESSAGEMAP[command]()
+ t.deserialize(f)
+ self._log_message("receive", t)
+ self.on_message(t)
+ except Exception as e:
+ logger.exception('Error reading message:', repr(e))
+ raise
-class msg_getblocktxn(object):
- command = b"getblocktxn"
+ def on_message(self, message):
+ """Callback for processing a P2P payload. Must be overridden by derived class."""
+ raise NotImplementedError
- def __init__(self):
- self.block_txn_request = None
+ # Socket write methods
- def deserialize(self, f):
- self.block_txn_request = BlockTransactionsRequest()
- self.block_txn_request.deserialize(f)
+ def writable(self):
+ """asyncore method to determine whether the handle_write() callback should be called on the next loop."""
+ with mininode_lock:
+ pre_connection = self.state == "connecting"
+ length = len(self.sendbuf)
+ return (length > 0 or pre_connection)
- def serialize(self):
- r = b""
- r += self.block_txn_request.serialize()
- return r
+ def handle_write(self):
+ """asyncore callback when data should be written to the socket."""
+ with mininode_lock:
+ # asyncore does not expose socket connection, only the first read/write
+ # event, thus we must check connection manually here to know when we
+ # actually connect
+ if self.state == "connecting":
+ self.handle_connect()
+ if not self.writable():
+ return
- def __repr__(self):
- return "msg_getblocktxn(block_txn_request=%s)" % (repr(self.block_txn_request))
+ try:
+ sent = self.send(self.sendbuf)
+ except:
+ self.handle_close()
+ return
+ self.sendbuf = self.sendbuf[sent:]
-class msg_blocktxn(object):
- command = b"blocktxn"
+ def send_message(self, message, pushbuf=False):
+ """Send a P2P message over the socket.
- def __init__(self):
- self.block_transactions = BlockTransactions()
+ This method takes a P2P payload, builds the P2P header and adds
+ the message to the send buffer to be sent over the socket."""
+ if self.state != "connected" and not pushbuf:
+ raise IOError('Not connected, no pushbuf')
+ self._log_message("send", message)
+ command = message.command
+ data = message.serialize()
+ tmsg = MAGIC_BYTES[self.network]
+ tmsg += command
+ tmsg += b"\x00" * (12 - len(command))
+ tmsg += struct.pack("<I", len(data))
+ th = sha256(data)
+ h = sha256(th)
+ tmsg += h[:4]
+ tmsg += data
+ with mininode_lock:
+ if (len(self.sendbuf) == 0 and not pushbuf):
+ try:
+ sent = self.send(tmsg)
+ self.sendbuf = tmsg[sent:]
+ except BlockingIOError:
+ self.sendbuf = tmsg
+ else:
+ self.sendbuf += tmsg
- def deserialize(self, f):
- self.block_transactions.deserialize(f)
+ # Class utility methods
- def serialize(self):
- r = b""
- r += self.block_transactions.serialize()
- return r
+ def _log_message(self, direction, msg):
+ """Logs a message being sent or received over the connection."""
+ if direction == "send":
+ log_message = "Send message to "
+ elif direction == "receive":
+ log_message = "Received message from "
+ log_message += "%s:%d: %s" % (self.dstaddr, self.dstport, repr(msg)[:500])
+ if len(log_message) > 500:
+ log_message += "... (msg truncated)"
+ logger.debug(log_message)
- def __repr__(self):
- return "msg_blocktxn(block_transactions=%s)" % (repr(self.block_transactions))
-class msg_witness_blocktxn(msg_blocktxn):
- def serialize(self):
- r = b""
- r += self.block_transactions.serialize(with_witness=True)
- return r
+class P2PInterface(P2PConnection):
+ """A high-level P2P interface class for communicating with a Bitcoin node.
-class NodeConnCB(object):
- """Callback and helper functions for P2P connection to a bitcoind node.
+ This class provides high-level callbacks for processing P2P message
+ payloads, as well as convenience methods for interacting with the
+ node over P2P.
Individual testcases should subclass this and override the on_* methods
- if they want to alter message handling behaviour.
- """
-
+ if they want to alter message handling behaviour."""
def __init__(self):
- # Track whether we have a P2P connection open to the node
- self.connected = False
- self.connection = None
+ super().__init__()
# Track number of messages of each type received and the most recent
# message of each type
@@ -1474,100 +264,89 @@ class NodeConnCB(object):
# A count of the number of ping messages we've sent to the node
self.ping_counter = 1
- # deliver_sleep_time is helpful for debugging race conditions in p2p
- # tests; it causes message delivery to sleep for the specified time
- # before acquiring the global lock and delivering the next message.
- self.deliver_sleep_time = None
+ # The network services received from the peer
+ self.nServices = 0
+
+ def peer_connect(self, *args, services=NODE_NETWORK|NODE_WITNESS, send_version=True, **kwargs):
+ super().peer_connect(*args, **kwargs)
+
+ if send_version:
+ # Send a version msg
+ vt = msg_version()
+ vt.nServices = services
+ vt.addrTo.ip = self.dstaddr
+ vt.addrTo.port = self.dstport
+ vt.addrFrom.ip = "0.0.0.0"
+ vt.addrFrom.port = 0
+ self.send_message(vt, True)
# Message receiving methods
- def deliver(self, conn, message):
+ def on_message(self, message):
"""Receive message and dispatch message to appropriate callback.
We keep a count of how many of each message type has been received
- and the most recent message of each type.
-
- Optionally waits for deliver_sleep_time before dispatching message.
- """
-
- deliver_sleep = self.get_deliver_sleep_time()
- if deliver_sleep is not None:
- time.sleep(deliver_sleep)
+ and the most recent message of each type."""
with mininode_lock:
try:
command = message.command.decode('ascii')
self.message_count[command] += 1
self.last_message[command] = message
- getattr(self, 'on_' + command)(conn, message)
+ getattr(self, 'on_' + command)(message)
except:
- print("ERROR delivering %s (%s)" % (repr(message),
- sys.exc_info()[0]))
+ print("ERROR delivering %s (%s)" % (repr(message), sys.exc_info()[0]))
raise
- def get_deliver_sleep_time(self):
- with mininode_lock:
- return self.deliver_sleep_time
-
# Callback methods. Can be overridden by subclasses in individual test
# cases to provide custom message handling behaviour.
- def on_open(self, conn):
- self.connected = True
-
- def on_close(self, conn):
- self.connected = False
- self.connection = None
-
- def on_addr(self, conn, message): pass
- def on_alert(self, conn, message): pass
- def on_block(self, conn, message): pass
- def on_blocktxn(self, conn, message): pass
- def on_cmpctblock(self, conn, message): pass
- def on_feefilter(self, conn, message): pass
- def on_getaddr(self, conn, message): pass
- def on_getblocks(self, conn, message): pass
- def on_getblocktxn(self, conn, message): pass
- def on_getdata(self, conn, message): pass
- def on_getheaders(self, conn, message): pass
- def on_headers(self, conn, message): pass
- def on_mempool(self, conn): pass
- def on_pong(self, conn, message): pass
- def on_reject(self, conn, message): pass
- def on_sendcmpct(self, conn, message): pass
- def on_sendheaders(self, conn, message): pass
- def on_tx(self, conn, message): pass
-
- def on_inv(self, conn, message):
+ def on_open(self):
+ pass
+
+ def on_close(self):
+ pass
+
+ def on_addr(self, message): pass
+ def on_block(self, message): pass
+ def on_blocktxn(self, message): pass
+ def on_cmpctblock(self, message): pass
+ def on_feefilter(self, message): pass
+ def on_getaddr(self, message): pass
+ def on_getblocks(self, message): pass
+ def on_getblocktxn(self, message): pass
+ def on_getdata(self, message): pass
+ def on_getheaders(self, message): pass
+ def on_headers(self, message): pass
+ def on_mempool(self, message): pass
+ def on_pong(self, message): pass
+ def on_reject(self, message): pass
+ def on_sendcmpct(self, message): pass
+ def on_sendheaders(self, message): pass
+ def on_tx(self, message): pass
+
+ def on_inv(self, message):
want = msg_getdata()
for i in message.inv:
if i.type != 0:
want.inv.append(i)
if len(want.inv):
- conn.send_message(want)
+ self.send_message(want)
- def on_ping(self, conn, message):
- if conn.ver_send > BIP0031_VERSION:
- conn.send_message(msg_pong(message.nonce))
+ def on_ping(self, message):
+ self.send_message(msg_pong(message.nonce))
- def on_verack(self, conn, message):
- conn.ver_recv = conn.ver_send
+ def on_verack(self, message):
self.verack_received = True
- def on_version(self, conn, message):
- if message.nVersion >= 209:
- conn.send_message(msg_verack())
- conn.ver_send = min(MY_VERSION, message.nVersion)
- if message.nVersion < 209:
- conn.ver_recv = conn.ver_send
- conn.nServices = message.nServices
+ def on_version(self, message):
+ assert message.nVersion >= MIN_VERSION_SUPPORTED, "Version {} received. Test framework only supports versions greater than {}".format(message.nVersion, MIN_VERSION_SUPPORTED)
+ self.send_message(msg_verack())
+ self.nServices = message.nServices
# Connection helper methods
- def add_connection(self, conn):
- self.connection = conn
-
def wait_for_disconnect(self, timeout=60):
- test_function = lambda: not self.connected
+ test_function = lambda: self.state != "connected"
wait_until(test_function, timeout=timeout, lock=mininode_lock)
# Message receiving helper methods
@@ -1599,12 +378,6 @@ class NodeConnCB(object):
# Message sending helper functions
- def send_message(self, message):
- if self.connection:
- self.connection.send_message(message)
- else:
- logger.error("Cannot send message. No connection to node!")
-
def send_and_ping(self, message):
self.send_message(message)
self.sync_with_ping()
@@ -1615,218 +388,25 @@ class NodeConnCB(object):
test_function = lambda: self.last_message.get("pong") and self.last_message["pong"].nonce == self.ping_counter
wait_until(test_function, timeout=timeout, lock=mininode_lock)
self.ping_counter += 1
- return True
-
-# The actual NodeConn class
-# This class provides an interface for a p2p connection to a specified node
-class NodeConn(asyncore.dispatcher):
- messagemap = {
- b"version": msg_version,
- b"verack": msg_verack,
- b"addr": msg_addr,
- b"alert": msg_alert,
- b"inv": msg_inv,
- b"getdata": msg_getdata,
- b"getblocks": msg_getblocks,
- b"tx": msg_tx,
- b"block": msg_block,
- b"getaddr": msg_getaddr,
- b"ping": msg_ping,
- b"pong": msg_pong,
- b"headers": msg_headers,
- b"getheaders": msg_getheaders,
- b"reject": msg_reject,
- b"mempool": msg_mempool,
- b"feefilter": msg_feefilter,
- b"sendheaders": msg_sendheaders,
- b"sendcmpct": msg_sendcmpct,
- b"cmpctblock": msg_cmpctblock,
- b"getblocktxn": msg_getblocktxn,
- b"blocktxn": msg_blocktxn
- }
- MAGIC_BYTES = {
- "mainnet": b"\xf9\xbe\xb4\xd9", # mainnet
- "testnet3": b"\x0b\x11\x09\x07", # testnet3
- "regtest": b"\xfa\xbf\xb5\xda", # regtest
- }
-
- def __init__(self, dstaddr, dstport, rpc, callback, net="regtest", services=NODE_NETWORK, send_version=True):
- asyncore.dispatcher.__init__(self, map=mininode_socket_map)
- self.dstaddr = dstaddr
- self.dstport = dstport
- self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
- self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
- self.sendbuf = b""
- self.recvbuf = b""
- self.ver_send = 209
- self.ver_recv = 209
- self.last_sent = 0
- self.state = "connecting"
- self.network = net
- self.cb = callback
- self.disconnect = False
- self.nServices = 0
-
- if send_version:
- # stuff version msg into sendbuf
- vt = msg_version()
- vt.nServices = services
- vt.addrTo.ip = self.dstaddr
- vt.addrTo.port = self.dstport
- vt.addrFrom.ip = "0.0.0.0"
- vt.addrFrom.port = 0
- self.send_message(vt, True)
- logger.info('Connecting to Bitcoin Node: %s:%d' % (self.dstaddr, self.dstport))
- try:
- self.connect((dstaddr, dstport))
- except:
- self.handle_close()
- self.rpc = rpc
-
- def handle_connect(self):
- if self.state != "connected":
- logger.debug("Connected & Listening: %s:%d" % (self.dstaddr, self.dstport))
- self.state = "connected"
- self.cb.on_open(self)
-
- def handle_close(self):
- logger.debug("Closing connection to: %s:%d" % (self.dstaddr, self.dstport))
- self.state = "closed"
- self.recvbuf = b""
- self.sendbuf = b""
- try:
- self.close()
- except:
- pass
- self.cb.on_close(self)
-
- def handle_read(self):
- t = self.recv(8192)
- if len(t) > 0:
- self.recvbuf += t
- self.got_data()
-
- def readable(self):
- return True
-
- def writable(self):
- with mininode_lock:
- pre_connection = self.state == "connecting"
- length = len(self.sendbuf)
- return (length > 0 or pre_connection)
-
- def handle_write(self):
- with mininode_lock:
- # asyncore does not expose socket connection, only the first read/write
- # event, thus we must check connection manually here to know when we
- # actually connect
- if self.state == "connecting":
- self.handle_connect()
- if not self.writable():
- return
-
- try:
- sent = self.send(self.sendbuf)
- except:
- self.handle_close()
- return
- self.sendbuf = self.sendbuf[sent:]
-
- def got_data(self):
- try:
- while True:
- if len(self.recvbuf) < 4:
- return
- if self.recvbuf[:4] != self.MAGIC_BYTES[self.network]:
- raise ValueError("got garbage %s" % repr(self.recvbuf))
- if self.ver_recv < 209:
- if len(self.recvbuf) < 4 + 12 + 4:
- return
- command = self.recvbuf[4:4+12].split(b"\x00", 1)[0]
- msglen = struct.unpack("<i", self.recvbuf[4+12:4+12+4])[0]
- checksum = None
- if len(self.recvbuf) < 4 + 12 + 4 + msglen:
- return
- msg = self.recvbuf[4+12+4:4+12+4+msglen]
- self.recvbuf = self.recvbuf[4+12+4+msglen:]
- else:
- if len(self.recvbuf) < 4 + 12 + 4 + 4:
- return
- command = self.recvbuf[4:4+12].split(b"\x00", 1)[0]
- msglen = struct.unpack("<i", self.recvbuf[4+12:4+12+4])[0]
- checksum = self.recvbuf[4+12+4:4+12+4+4]
- if len(self.recvbuf) < 4 + 12 + 4 + 4 + msglen:
- return
- msg = self.recvbuf[4+12+4+4:4+12+4+4+msglen]
- th = sha256(msg)
- h = sha256(th)
- if checksum != h[:4]:
- raise ValueError("got bad checksum " + repr(self.recvbuf))
- self.recvbuf = self.recvbuf[4+12+4+4+msglen:]
- if command in self.messagemap:
- f = BytesIO(msg)
- t = self.messagemap[command]()
- t.deserialize(f)
- self.got_message(t)
- else:
- logger.warning("Received unknown command from %s:%d: '%s' %s" % (self.dstaddr, self.dstport, command, repr(msg)))
- raise ValueError("Unknown command: '%s'" % (command))
- except Exception as e:
- logger.exception('got_data:', repr(e))
- raise
-
- def send_message(self, message, pushbuf=False):
- if self.state != "connected" and not pushbuf:
- raise IOError('Not connected, no pushbuf')
- self._log_message("send", message)
- command = message.command
- data = message.serialize()
- tmsg = self.MAGIC_BYTES[self.network]
- tmsg += command
- tmsg += b"\x00" * (12 - len(command))
- tmsg += struct.pack("<I", len(data))
- if self.ver_send >= 209:
- th = sha256(data)
- h = sha256(th)
- tmsg += h[:4]
- tmsg += data
- with mininode_lock:
- if (len(self.sendbuf) == 0 and not pushbuf):
- try:
- sent = self.send(tmsg)
- self.sendbuf = tmsg[sent:]
- except BlockingIOError:
- self.sendbuf = tmsg
- else:
- self.sendbuf += tmsg
- self.last_sent = time.time()
-
- def got_message(self, message):
- if message.command == b"version":
- if message.nVersion <= BIP0031_VERSION:
- self.messagemap[b'ping'] = msg_ping_prebip31
- if self.last_sent + 30 * 60 < time.time():
- self.send_message(self.messagemap[b'ping']())
- self._log_message("receive", message)
- self.cb.deliver(self, message)
-
- def _log_message(self, direction, msg):
- if direction == "send":
- log_message = "Send message to "
- elif direction == "receive":
- log_message = "Received message from "
- log_message += "%s:%d: %s" % (self.dstaddr, self.dstport, repr(msg)[:500])
- if len(log_message) > 500:
- log_message += "... (msg truncated)"
- logger.debug(log_message)
+# Keep our own socket map for asyncore, so that we can track disconnects
+# ourselves (to workaround an issue with closing an asyncore socket when
+# using select)
+mininode_socket_map = dict()
- def disconnect_node(self):
- self.disconnect = True
+# One lock for synchronizing all data access between the networking thread (see
+# NetworkThread below) and the thread running the test logic. For simplicity,
+# P2PConnection acquires this lock whenever delivering a message to a P2PInterface,
+# and whenever adding anything to the send buffer (in send_message()). This
+# lock should be acquired in the thread running the test logic to synchronize
+# access to any data shared with the P2PInterface or P2PConnection.
+mininode_lock = threading.RLock()
+class NetworkThread(threading.Thread):
+ def __init__(self):
+ super().__init__(name="NetworkThread")
-class NetworkThread(Thread):
def run(self):
while mininode_socket_map:
# We check for whether to disconnect outside of the asyncore
@@ -1836,16 +416,27 @@ class NetworkThread(Thread):
for fd, obj in mininode_socket_map.items():
if obj.disconnect:
disconnected.append(obj)
- [ obj.handle_close() for obj in disconnected ]
+ [obj.handle_close() for obj in disconnected]
asyncore.loop(0.1, use_poll=True, map=mininode_socket_map, count=1)
logger.debug("Network thread closing")
+def network_thread_start():
+ """Start the network thread."""
+ # Only one network thread may run at a time
+ assert not network_thread_running()
+
+ NetworkThread().start()
+
+def network_thread_running():
+ """Return whether the network thread is running."""
+ return any([thread.name == "NetworkThread" for thread in threading.enumerate()])
-# An exception we can raise if we detect a potential disconnect
-# (p2p or rpc) before the test is complete
-class EarlyDisconnectError(Exception):
- def __init__(self, value):
- self.value = value
+def network_thread_join(timeout=10):
+ """Wait timeout seconds for the network thread to terminate.
- def __str__(self):
- return repr(self.value)
+ Throw if the network thread doesn't terminate in timeout seconds."""
+ network_threads = [thread for thread in threading.enumerate() if thread.name == "NetworkThread"]
+ assert len(network_threads) <= 1
+ for thread in network_threads:
+ thread.join(timeout)
+ assert not thread.is_alive()
diff --git a/test/functional/test_framework/netutil.py b/test/functional/test_framework/netutil.py
index e5d415788f..96fe283347 100644
--- a/test/functional/test_framework/netutil.py
+++ b/test/functional/test_framework/netutil.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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.
"""Linux network utilities.
diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py
index 8f5339a02a..dae8a4e569 100644
--- a/test/functional/test_framework/script.py
+++ b/test/functional/test_framework/script.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Copyright (c) 2015-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.
"""Functionality to build scripts, as well as SignatureHash().
@@ -370,7 +370,7 @@ class CScriptTruncatedPushDataError(CScriptInvalidError):
super(CScriptTruncatedPushDataError, self).__init__(msg)
# This is used, eg, for blockchain heights in coinbase scripts (bip34)
-class CScriptNum(object):
+class CScriptNum():
def __init__(self, d=0):
self.value = d
@@ -641,7 +641,7 @@ def SignatureHash(script, txTo, inIdx, hashtype):
txtmp.vin = []
txtmp.vin.append(tmp)
- s = txtmp.serialize()
+ s = txtmp.serialize_without_witness()
s += struct.pack(b"<I", hashtype)
hash = hash256(s)
diff --git a/test/functional/test_framework/siphash.py b/test/functional/test_framework/siphash.py
index f68ecad36b..6ffc982cea 100644
--- a/test/functional/test_framework/siphash.py
+++ b/test/functional/test_framework/siphash.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016 The Bitcoin Core developers
+# Copyright (c) 2016-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.
"""Specialized SipHash-2-4 implementations.
diff --git a/test/functional/test_framework/socks5.py b/test/functional/test_framework/socks5.py
index 0070844168..4721809a3b 100644
--- a/test/functional/test_framework/socks5.py
+++ b/test/functional/test_framework/socks5.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Copyright (c) 2015-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.
"""Dummy Socks5 server for testing."""
@@ -31,7 +31,7 @@ def recvall(s, n):
return rv
### Implementation classes
-class Socks5Configuration(object):
+class Socks5Configuration():
"""Proxy configuration."""
def __init__(self):
self.addr = None # Bind address (must be set)
@@ -39,7 +39,7 @@ class Socks5Configuration(object):
self.unauth = False # Support unauthenticated
self.auth = False # Support authentication
-class Socks5Command(object):
+class Socks5Command():
"""Information about an incoming socks5 command."""
def __init__(self, cmd, atyp, addr, port, username, password):
self.cmd = cmd # Command (one of Command.*)
@@ -51,7 +51,7 @@ class Socks5Command(object):
def __repr__(self):
return 'Socks5Command(%s,%s,%s,%s,%s,%s)' % (self.cmd, self.atyp, self.addr, self.port, self.username, self.password)
-class Socks5Connection(object):
+class Socks5Connection():
def __init__(self, serv, conn, peer):
self.serv = serv
self.conn = conn
@@ -122,7 +122,7 @@ class Socks5Connection(object):
finally:
self.conn.close()
-class Socks5Server(object):
+class Socks5Server():
def __init__(self, conf):
self.conf = conf
self.s = socket.socket(conf.af)
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 381513ab9e..5d4f8e6720 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -1,10 +1,9 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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.
"""Base class for RPC testing."""
-from collections import deque
from enum import Enum
import logging
import optparse
@@ -14,7 +13,6 @@ import shutil
import sys
import tempfile
import time
-import traceback
from .authproxy import JSONRPCException
from . import coverage
@@ -43,7 +41,7 @@ TEST_EXIT_PASSED = 0
TEST_EXIT_FAILED = 1
TEST_EXIT_SKIPPED = 77
-class BitcoinTestFramework(object):
+class BitcoinTestFramework():
"""Base class for a bitcoin test script.
Individual bitcoin test scripts should subclass this class and override the set_test_params() and run_test() methods.
@@ -64,6 +62,7 @@ class BitcoinTestFramework(object):
self.setup_clean_chain = False
self.nodes = []
self.mocktime = 0
+ self.supports_cli = False
self.set_test_params()
assert hasattr(self, "num_nodes"), "Test must set self.num_nodes in set_test_params()"
@@ -93,6 +92,8 @@ class BitcoinTestFramework(object):
help="Location of the test framework config file")
parser.add_option("--pdbonfailure", dest="pdbonfailure", default=False, action="store_true",
help="Attach a python debugger if test fails")
+ parser.add_option("--usecli", dest="usecli", default=False, action="store_true",
+ help="use bitcoin-cli instead of RPC for all commands")
self.add_options(parser)
(self.options, self.args) = parser.parse_args()
@@ -102,8 +103,11 @@ class BitcoinTestFramework(object):
check_json_precision()
+ self.options.cachedir = os.path.abspath(self.options.cachedir)
+
# Set up temp directory and start logging
if self.options.tmpdir:
+ self.options.tmpdir = os.path.abspath(self.options.tmpdir)
os.makedirs(self.options.tmpdir, exist_ok=False)
else:
self.options.tmpdir = tempfile.mkdtemp(prefix="test")
@@ -112,6 +116,8 @@ class BitcoinTestFramework(object):
success = TestStatus.FAILED
try:
+ if self.options.usecli and not self.supports_cli:
+ raise SkipTest("--usecli specified but test does not support using CLI")
self.setup_chain()
self.setup_network()
self.run_test()
@@ -146,32 +152,19 @@ class BitcoinTestFramework(object):
shutil.rmtree(self.options.tmpdir)
else:
self.log.warning("Not cleaning up dir %s" % self.options.tmpdir)
- if os.getenv("PYTHON_DEBUG", ""):
- # Dump the end of the debug logs, to aid in debugging rare
- # travis failures.
- import glob
- filenames = [self.options.tmpdir + "/test_framework.log"]
- filenames += glob.glob(self.options.tmpdir + "/node*/regtest/debug.log")
- MAX_LINES_TO_PRINT = 1000
- for fn in filenames:
- try:
- with open(fn, 'r') as f:
- print("From", fn, ":")
- print("".join(deque(f, MAX_LINES_TO_PRINT)))
- except OSError:
- print("Opening file %s failed." % fn)
- traceback.print_exc()
if success == TestStatus.PASSED:
self.log.info("Tests successful")
- sys.exit(TEST_EXIT_PASSED)
+ exit_code = TEST_EXIT_PASSED
elif success == TestStatus.SKIPPED:
self.log.info("Test skipped")
- sys.exit(TEST_EXIT_SKIPPED)
+ exit_code = TEST_EXIT_SKIPPED
else:
self.log.error("Test failed. Test logging available at %s/test_framework.log", self.options.tmpdir)
- logging.shutdown()
- sys.exit(TEST_EXIT_FAILED)
+ self.log.error("Hint: Call {} '{}' to consolidate all logs".format(os.path.normpath(os.path.dirname(os.path.realpath(__file__)) + "/../combine_logs.py"), self.options.tmpdir))
+ exit_code = TEST_EXIT_FAILED
+ logging.shutdown()
+ sys.exit(exit_code)
# Methods to override in subclass test scripts.
def set_test_params(self):
@@ -225,7 +218,7 @@ class BitcoinTestFramework(object):
assert_equal(len(extra_args), num_nodes)
assert_equal(len(binary), num_nodes)
for i in range(num_nodes):
- self.nodes.append(TestNode(i, self.options.tmpdir, extra_args[i], rpchost, timewait=timewait, binary=binary[i], stderr=None, mocktime=self.mocktime, coverage_dir=self.options.coveragedir))
+ self.nodes.append(TestNode(i, self.options.tmpdir, extra_args[i], rpchost, timewait=timewait, binary=binary[i], stderr=None, mocktime=self.mocktime, coverage_dir=self.options.coveragedir, use_cli=self.options.usecli))
def start_node(self, i, extra_args=None, stderr=None):
"""Start a bitcoind"""
@@ -429,7 +422,7 @@ class BitcoinTestFramework(object):
self.disable_mocktime()
for i in range(MAX_NODES):
os.remove(log_filename(self.options.cachedir, i, "debug.log"))
- os.remove(log_filename(self.options.cachedir, i, "db.log"))
+ os.remove(log_filename(self.options.cachedir, i, "wallets/db.log"))
os.remove(log_filename(self.options.cachedir, i, "peers.dat"))
os.remove(log_filename(self.options.cachedir, i, "fee_estimates.dat"))
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 12dab57a02..589a8f3969 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -10,16 +10,21 @@ import http.client
import json
import logging
import os
+import re
import subprocess
import time
+from .authproxy import JSONRPCException
from .util import (
assert_equal,
get_rpc_proxy,
rpc_url,
wait_until,
+ p2p_port,
)
-from .authproxy import JSONRPCException
+
+# For Python 3.4 compatibility
+JSONDecodeError = getattr(json, "JSONDecodeError", ValueError)
BITCOIND_PROC_WAIT_TIMEOUT = 60
@@ -31,11 +36,13 @@ class TestNode():
- state about the node (whether it's running, etc)
- a Python subprocess.Popen object representing the running process
- an RPC connection to the node
+ - one or more P2P connections to the node
- To make things easier for the test writer, a bit of magic is happening under the covers.
- Any unrecognised messages will be dispatched to the RPC connection."""
- def __init__(self, i, dirname, extra_args, rpchost, timewait, binary, stderr, mocktime, coverage_dir):
+ To make things easier for the test writer, any unrecognised messages will
+ be dispatched to the RPC connection."""
+
+ def __init__(self, i, dirname, extra_args, rpchost, timewait, binary, stderr, mocktime, coverage_dir, use_cli=False):
self.index = i
self.datadir = os.path.join(dirname, "node" + str(i))
self.rpchost = rpchost
@@ -55,6 +62,7 @@ class TestNode():
self.args = [self.binary, "-datadir=" + self.datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-logtimemicros", "-debug", "-debugexclude=libevent", "-debugexclude=leveldb", "-mocktime=" + str(mocktime), "-uacomment=testnode%d" % i]
self.cli = TestNodeCLI(os.getenv("BITCOINCLI", "bitcoin-cli"), self.datadir)
+ self.use_cli = use_cli
self.running = False
self.process = None
@@ -63,10 +71,15 @@ class TestNode():
self.url = None
self.log = logging.getLogger('TestFramework.node%d' % i)
- def __getattr__(self, *args, **kwargs):
- """Dispatches any unrecognised messages to the RPC connection."""
- assert self.rpc_connected and self.rpc is not None, "Error: no RPC connection"
- return self.rpc.__getattr__(*args, **kwargs)
+ self.p2ps = []
+
+ def __getattr__(self, name):
+ """Dispatches any unrecognised messages to the RPC connection or a CLI instance."""
+ if self.use_cli:
+ return getattr(self.cli, name)
+ else:
+ assert self.rpc_connected and self.rpc is not None, "Error: no RPC connection"
+ return getattr(self.rpc, name)
def start(self, extra_args=None, stderr=None):
"""Start the node."""
@@ -105,10 +118,13 @@ class TestNode():
raise AssertionError("Unable to connect to bitcoind")
def get_wallet_rpc(self, wallet_name):
- assert self.rpc_connected
- assert self.rpc
- wallet_path = "wallet/%s" % wallet_name
- return self.rpc / wallet_path
+ if self.use_cli:
+ return self.cli("-rpcwallet={}".format(wallet_name))
+ else:
+ assert self.rpc_connected
+ assert self.rpc
+ wallet_path = "wallet/%s" % wallet_name
+ return self.rpc / wallet_path
def stop_node(self):
"""Stop the node."""
@@ -119,6 +135,7 @@ class TestNode():
self.stop()
except http.client.CannotSendRequest:
self.log.exception("Unable to stop node.")
+ del self.p2ps[:]
def is_node_stopped(self):
"""Checks whether the node has stopped.
@@ -151,6 +168,47 @@ class TestNode():
self.encryptwallet(passphrase)
self.wait_until_stopped()
+ def add_p2p_connection(self, p2p_conn, *args, **kwargs):
+ """Add a p2p connection to the node.
+
+ This method adds the p2p connection to the self.p2ps list and also
+ returns the connection to the caller."""
+ if 'dstport' not in kwargs:
+ kwargs['dstport'] = p2p_port(self.index)
+ if 'dstaddr' not in kwargs:
+ kwargs['dstaddr'] = '127.0.0.1'
+
+ p2p_conn.peer_connect(*args, **kwargs)
+ self.p2ps.append(p2p_conn)
+
+ return p2p_conn
+
+ @property
+ def p2p(self):
+ """Return the first p2p connection
+
+ Convenience property - most tests only use a single p2p connection to each
+ node, so this saves having to write node.p2ps[0] many times."""
+ assert self.p2ps, "No p2p connection"
+ return self.p2ps[0]
+
+ def disconnect_p2ps(self):
+ """Close all p2p connections to the node."""
+ for p in self.p2ps:
+ p.peer_disconnect()
+ del self.p2ps[:]
+
+class TestNodeCLIAttr:
+ def __init__(self, cli, command):
+ self.cli = cli
+ self.command = command
+
+ def __call__(self, *args, **kwargs):
+ return self.cli.send_cli(self.command, *args, **kwargs)
+
+ def get_request(self, *args, **kwargs):
+ return lambda: self(*args, **kwargs)
+
class TestNodeCLI():
"""Interface to bitcoin-cli for an individual node"""
@@ -159,17 +217,26 @@ class TestNodeCLI():
self.binary = binary
self.datadir = datadir
self.input = None
+ self.log = logging.getLogger('TestFramework.bitcoincli')
def __call__(self, *args, input=None):
# TestNodeCLI is callable with bitcoin-cli command-line args
- self.args = [str(arg) for arg in args]
- self.input = input
- return self
+ cli = TestNodeCLI(self.binary, self.datadir)
+ cli.args = [str(arg) for arg in args]
+ cli.input = input
+ return cli
def __getattr__(self, command):
- def dispatcher(*args, **kwargs):
- return self.send_cli(command, *args, **kwargs)
- return dispatcher
+ return TestNodeCLIAttr(self, command)
+
+ def batch(self, requests):
+ results = []
+ for request in requests:
+ try:
+ results.append(dict(result=request()))
+ except JSONRPCException as e:
+ results.append(dict(error=e))
+ return results
def send_cli(self, command, *args, **kwargs):
"""Run bitcoin-cli command. Deserializes returned string as python object."""
@@ -181,10 +248,18 @@ class TestNodeCLI():
if named_args:
p_args += ["-named"]
p_args += [command] + pos_args + named_args
+ self.log.debug("Running bitcoin-cli command: %s" % command)
process = subprocess.Popen(p_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
cli_stdout, cli_stderr = process.communicate(input=self.input)
returncode = process.poll()
if returncode:
+ match = re.match(r'error code: ([-0-9]+)\nerror message:\n(.*)', cli_stderr)
+ if match:
+ code, message = match.groups()
+ raise JSONRPCException(dict(code=int(code), message=message))
# Ignore cli_stdout, raise with cli_stderr
raise subprocess.CalledProcessError(returncode, self.binary, output=cli_stderr)
- return json.loads(cli_stdout, parse_float=decimal.Decimal)
+ try:
+ return json.loads(cli_stdout, parse_float=decimal.Decimal)
+ except JSONDecodeError:
+ return cli_stdout.rstrip("\n")
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 102c903018..664dc6d0af 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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.
"""Helpful routines for regression testing."""
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index e85d672096..72ad300e7e 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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.
"""Run regression test suite.
@@ -15,6 +15,7 @@ For a description of arguments recognized by test scripts, see
"""
import argparse
+from collections import deque
import configparser
import datetime
import os
@@ -77,6 +78,7 @@ BASE_SCRIPTS= [
'abandonconflict.py',
'bip68-112-113-p2p.py',
'rawtransactions.py',
+ 'address_types.py',
'reindex.py',
# vv Tests less than 30s vv
'keypool-topup.py',
@@ -85,12 +87,14 @@ BASE_SCRIPTS= [
'mempool_resurrect_test.py',
'txn_doublespend.py --mineblock',
'txn_clone.py',
+ 'txn_clone.py --segwit',
'getchaintips.py',
'rest.py',
'mempool_spendcoinbase.py',
'mempool_reorg.py',
'mempool_persist.py',
'multiwallet.py',
+ 'multiwallet.py --usecli',
'httpbasics.py',
'multi_rpc.py',
'proxy_test.py',
@@ -123,6 +127,14 @@ BASE_SCRIPTS= [
'uptime.py',
'resendwallettransactions.py',
'minchainwork.py',
+ 'p2p-fingerprint.py',
+ 'uacomment.py',
+ 'p2p-acceptblock.py',
+ 'feature_logging.py',
+ 'node_network_limited.py',
+ 'conf_args.py',
+ # Don't append tests at the end to avoid merge conflicts
+ # Put them in a random line within the section that fits their approximate run-time
]
EXTENDED_SCRIPTS = [
@@ -148,9 +160,8 @@ EXTENDED_SCRIPTS = [
'example_test.py',
'txn_doublespend.py',
'txn_clone.py --mineblock',
- 'forknotify.py',
+ 'notifications.py',
'invalidateblock.py',
- 'p2p-acceptblock.py',
'replace-by-fee.py',
]
@@ -172,6 +183,7 @@ def main():
epilog='''
Help text and arguments for individual test script:''',
formatter_class=argparse.RawTextHelpFormatter)
+ parser.add_argument('--combinedlogslen', '-c', type=int, default=0, help='print a combined log (of length n lines) from all test nodes and test framework to the console on failure.')
parser.add_argument('--coverage', action='store_true', help='generate a basic coverage report for the RPC interface')
parser.add_argument('--exclude', '-x', help='specify a comma-separated-list of scripts to exclude.')
parser.add_argument('--extended', action='store_true', help='run the extended test suite in addition to the basic tests')
@@ -265,9 +277,9 @@ def main():
if not args.keepcache:
shutil.rmtree("%s/test/cache" % config["environment"]["BUILDDIR"], ignore_errors=True)
- run_tests(test_list, config["environment"]["SRCDIR"], config["environment"]["BUILDDIR"], config["environment"]["EXEEXT"], tmpdir, args.jobs, args.coverage, passon_args)
+ run_tests(test_list, config["environment"]["SRCDIR"], config["environment"]["BUILDDIR"], config["environment"]["EXEEXT"], tmpdir, args.jobs, args.coverage, passon_args, args.combinedlogslen)
-def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_coverage=False, args=[]):
+def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_coverage=False, args=[], combined_logs_len=0):
# Warn if bitcoind is already running (unix only)
try:
if subprocess.check_output(["pidof", "bitcoind"]) is not None:
@@ -299,7 +311,11 @@ def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_cove
if len(test_list) > 1 and jobs > 1:
# Populate cache
- subprocess.check_output([tests_dir + 'create_cache.py'] + flags + ["--tmpdir=%s/cache" % tmpdir])
+ try:
+ subprocess.check_output([tests_dir + 'create_cache.py'] + flags + ["--tmpdir=%s/cache" % tmpdir])
+ except Exception as e:
+ print(e.output)
+ raise e
#Run Tests
job_queue = TestHandler(jobs, tests_dir, tmpdir, test_list, flags)
@@ -309,7 +325,7 @@ def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_cove
max_len_name = len(max(test_list, key=len))
for _ in range(len(test_list)):
- test_result, stdout, stderr = job_queue.get_next()
+ test_result, testdir, stdout, stderr = job_queue.get_next()
test_results.append(test_result)
if test_result.status == "Passed":
@@ -320,6 +336,14 @@ def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_cove
print("\n%s%s%s failed, Duration: %s s\n" % (BOLD[1], test_result.name, BOLD[0], test_result.time))
print(BOLD[1] + 'stdout:\n' + BOLD[0] + stdout + '\n')
print(BOLD[1] + 'stderr:\n' + BOLD[0] + stderr + '\n')
+ if combined_logs_len and os.path.isdir(testdir):
+ # Print the final `combinedlogslen` lines of the combined logs
+ print('{}Combine the logs and print the last {} lines ...{}'.format(BOLD[1], combined_logs_len, BOLD[0]))
+ print('\n============')
+ print('{}Combined log for {}:{}'.format(BOLD[1], testdir, BOLD[0]))
+ print('============\n')
+ combined_logs, _ = subprocess.Popen([os.path.join(tests_dir, 'combine_logs.py'), '-c', testdir], universal_newlines=True, stdout=subprocess.PIPE).communicate()
+ print("\n".join(deque(combined_logs.splitlines(), combined_logs_len)))
print_results(test_results, max_len_name, (int(time.time() - time0)))
@@ -384,13 +408,15 @@ class TestHandler:
log_stdout = tempfile.SpooledTemporaryFile(max_size=2**16)
log_stderr = tempfile.SpooledTemporaryFile(max_size=2**16)
test_argv = t.split()
- tmpdir = ["--tmpdir=%s/%s_%s" % (self.tmpdir, re.sub(".py$", "", test_argv[0]), portseed)]
+ testdir = "{}/{}_{}".format(self.tmpdir, re.sub(".py$", "", test_argv[0]), portseed)
+ tmpdir_arg = ["--tmpdir={}".format(testdir)]
self.jobs.append((t,
time.time(),
- subprocess.Popen([self.tests_dir + test_argv[0]] + test_argv[1:] + self.flags + portseed_arg + tmpdir,
+ subprocess.Popen([self.tests_dir + test_argv[0]] + test_argv[1:] + self.flags + portseed_arg + tmpdir_arg,
universal_newlines=True,
stdout=log_stdout,
stderr=log_stderr),
+ testdir,
log_stdout,
log_stderr))
if not self.jobs:
@@ -399,7 +425,7 @@ class TestHandler:
# Return first proc that finishes
time.sleep(.5)
for j in self.jobs:
- (name, time0, proc, log_out, log_err) = j
+ (name, time0, proc, testdir, log_out, log_err) = j
if os.getenv('TRAVIS') == 'true' and int(time.time() - time0) > 20 * 60:
# In travis, timeout individual tests after 20 minutes (to stop tests hanging and not
# providing useful output.
@@ -417,7 +443,7 @@ class TestHandler:
self.num_running -= 1
self.jobs.remove(j)
- return TestResult(name, status, int(time.time() - time0)), stdout, stderr
+ return TestResult(name, status, int(time.time() - time0)), testdir, stdout, stderr
print('.', end='', flush=True)
class TestResult():
@@ -481,7 +507,7 @@ def check_script_list(src_dir):
# On travis this warning is an error to prevent merging incomplete commits into master
sys.exit(1)
-class RPCCoverage(object):
+class RPCCoverage():
"""
Coverage reporting utilities for test_runner.
diff --git a/test/functional/txn_clone.py b/test/functional/txn_clone.py
index 740bb2d4c5..ce26d6e0ee 100755
--- a/test/functional/txn_clone.py
+++ b/test/functional/txn_clone.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 the wallet accounts properly when there are cloned transactions with malleated scriptsigs."""
@@ -14,6 +14,8 @@ class TxnMallTest(BitcoinTestFramework):
def add_options(self, parser):
parser.add_option("--mineblock", dest="mine_block", default=False, action="store_true",
help="Test double-spend of 1-confirmed transaction")
+ parser.add_option("--segwit", dest="segwit", default=False, action="store_true",
+ help="Test behaviour with SegWit txn (which should fail")
def setup_network(self):
# Start with split network:
@@ -22,6 +24,11 @@ class TxnMallTest(BitcoinTestFramework):
disconnect_nodes(self.nodes[2], 1)
def run_test(self):
+ if self.options.segwit:
+ output_type="p2sh-segwit"
+ else:
+ output_type="legacy"
+
# All nodes should start with 1,250 BTC:
starting_balance = 1250
for i in range(4):
@@ -31,11 +38,11 @@ class TxnMallTest(BitcoinTestFramework):
# Assign coins to foo and bar accounts:
self.nodes[0].settxfee(.001)
- node0_address_foo = self.nodes[0].getnewaddress("foo")
+ node0_address_foo = self.nodes[0].getnewaddress("foo", output_type)
fund_foo_txid = self.nodes[0].sendfrom("", node0_address_foo, 1219)
fund_foo_tx = self.nodes[0].gettransaction(fund_foo_txid)
- node0_address_bar = self.nodes[0].getnewaddress("bar")
+ node0_address_bar = self.nodes[0].getnewaddress("bar", output_type)
fund_bar_txid = self.nodes[0].sendfrom("", node0_address_bar, 29)
fund_bar_tx = self.nodes[0].gettransaction(fund_bar_txid)
@@ -106,6 +113,10 @@ class TxnMallTest(BitcoinTestFramework):
# Send clone and its parent to miner
self.nodes[2].sendrawtransaction(fund_foo_tx["hex"])
txid1_clone = self.nodes[2].sendrawtransaction(tx1_clone["hex"])
+ if self.options.segwit:
+ assert_equal(txid1, txid1_clone)
+ return
+
# ... mine a block...
self.nodes[2].generate(1)
diff --git a/test/functional/txn_doublespend.py b/test/functional/txn_doublespend.py
index 69629ef951..01129f3817 100755
--- a/test/functional/txn_doublespend.py
+++ b/test/functional/txn_doublespend.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 the wallet accounts properly when there is a double-spend conflict."""
diff --git a/test/functional/uacomment.py b/test/functional/uacomment.py
new file mode 100755
index 0000000000..0b2c64ab69
--- /dev/null
+++ b/test/functional/uacomment.py
@@ -0,0 +1,35 @@
+#!/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 the -uacomment option."""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+
+class UacommentTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+ self.setup_clean_chain = True
+
+ def run_test(self):
+ self.log.info("test multiple -uacomment")
+ test_uacomment = self.nodes[0].getnetworkinfo()["subversion"][-12:-1]
+ assert_equal(test_uacomment, "(testnode0)")
+
+ self.restart_node(0, ["-uacomment=foo"])
+ foo_uacomment = self.nodes[0].getnetworkinfo()["subversion"][-17:-1]
+ assert_equal(foo_uacomment, "(testnode0; foo)")
+
+ self.log.info("test -uacomment max length")
+ self.stop_node(0)
+ expected = "Total length of network version string (286) exceeds maximum length (256). Reduce the number or size of uacomments."
+ self.assert_start_raises_init_error(0, ["-uacomment=" + 'a' * 256], expected)
+
+ self.log.info("test -uacomment unsafe characters")
+ for unsafe_char in ['/', ':', '(', ')']:
+ expected = "User Agent comment (" + unsafe_char + ") contains unsafe characters"
+ self.assert_start_raises_init_error(0, ["-uacomment=" + unsafe_char], expected)
+
+if __name__ == '__main__':
+ UacommentTest().main()
diff --git a/test/functional/wallet-accounts.py b/test/functional/wallet-accounts.py
index 40726d2a76..287436877f 100755
--- a/test/functional/wallet-accounts.py
+++ b/test/functional/wallet-accounts.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016 The Bitcoin Core developers
+# Copyright (c) 2016-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 account RPCs.
@@ -72,62 +72,135 @@ class WalletAccountsTest(BitcoinTestFramework):
# otherwise we're off by exactly the fee amount as that's mined
# and matures in the next 100 blocks
node.sendfrom("", common_address, fee)
- accounts = ["a", "b", "c", "d", "e"]
amount_to_send = 1.0
- account_addresses = dict()
+
+ # Create accounts and make sure subsequent account API calls
+ # recognize the account/address associations.
+ accounts = [Account(name) for name in ("a", "b", "c", "d", "e")]
for account in accounts:
- address = node.getaccountaddress(account)
- account_addresses[account] = address
-
- node.getnewaddress(account)
- assert_equal(node.getaccount(address), account)
- assert(address in node.getaddressesbyaccount(account))
-
- node.sendfrom("", address, amount_to_send)
-
+ account.add_receive_address(node.getaccountaddress(account.name))
+ account.verify(node)
+
+ # Send a transaction to each account, and make sure this forces
+ # getaccountaddress to generate a new receiving address.
+ for account in accounts:
+ node.sendtoaddress(account.receive_address, amount_to_send)
+ account.add_receive_address(node.getaccountaddress(account.name))
+ account.verify(node)
+
+ # Check the amounts received.
node.generate(1)
+ for account in accounts:
+ assert_equal(
+ node.getreceivedbyaddress(account.addresses[0]), amount_to_send)
+ assert_equal(node.getreceivedbyaccount(account.name), amount_to_send)
- for i in range(len(accounts)):
- from_account = accounts[i]
+ # Check that sendfrom account reduces listaccounts balances.
+ for i, account in enumerate(accounts):
to_account = accounts[(i+1) % len(accounts)]
- to_address = account_addresses[to_account]
- node.sendfrom(from_account, to_address, amount_to_send)
-
+ node.sendfrom(account.name, to_account.receive_address, amount_to_send)
node.generate(1)
-
for account in accounts:
- address = node.getaccountaddress(account)
- assert(address != account_addresses[account])
- assert_equal(node.getreceivedbyaccount(account), 2)
- node.move(account, "", node.getbalance(account))
-
+ account.add_receive_address(node.getaccountaddress(account.name))
+ account.verify(node)
+ assert_equal(node.getreceivedbyaccount(account.name), 2)
+ node.move(account.name, "", node.getbalance(account.name))
+ account.verify(node)
node.generate(101)
-
expected_account_balances = {"": 5200}
for account in accounts:
- expected_account_balances[account] = 0
-
+ expected_account_balances[account.name] = 0
assert_equal(node.listaccounts(), expected_account_balances)
-
assert_equal(node.getbalance(""), 5200)
+ # Check that setaccount can assign an account to a new unused address.
for account in accounts:
address = node.getaccountaddress("")
- node.setaccount(address, account)
- assert(address in node.getaddressesbyaccount(account))
+ node.setaccount(address, account.name)
+ account.add_address(address)
+ account.verify(node)
assert(address not in node.getaddressesbyaccount(""))
+ # Check that addmultisigaddress can assign accounts.
for account in accounts:
addresses = []
for x in range(10):
addresses.append(node.getnewaddress())
- multisig_address = node.addmultisigaddress(5, addresses, account)
+ multisig_address = node.addmultisigaddress(5, addresses, account.name)
+ account.add_address(multisig_address)
+ account.verify(node)
node.sendfrom("", multisig_address, 50)
-
node.generate(101)
-
for account in accounts:
- assert_equal(node.getbalance(account), 50)
+ assert_equal(node.getbalance(account.name), 50)
+
+ # Check that setaccount can change the account of an address from a
+ # different account.
+ change_account(node, accounts[0].addresses[0], accounts[0], accounts[1])
+
+ # Check that setaccount can change the account of an address which
+ # is the receiving address of a different account.
+ change_account(node, accounts[0].receive_address, accounts[0], accounts[1])
+
+ # Check that setaccount can set the account of an address already
+ # in the account. This is a no-op.
+ change_account(node, accounts[2].addresses[0], accounts[2], accounts[2])
+
+ # Check that setaccount can set the account of an address which is
+ # already the receiving address of the account. It would probably make
+ # sense for this to be a no-op, but right now it resets the receiving
+ # address, causing getaccountaddress to return a brand new address.
+ change_account(node, accounts[2].receive_address, accounts[2], accounts[2])
+
+class Account:
+ def __init__(self, name):
+ # Account name
+ self.name = name
+ # Current receiving address associated with this account.
+ self.receive_address = None
+ # List of all addresses assigned with this account
+ self.addresses = []
+
+ def add_address(self, address):
+ assert_equal(address not in self.addresses, True)
+ self.addresses.append(address)
+
+ def add_receive_address(self, address):
+ self.add_address(address)
+ self.receive_address = address
+
+ def verify(self, node):
+ if self.receive_address is not None:
+ assert self.receive_address in self.addresses
+ assert_equal(node.getaccountaddress(self.name), self.receive_address)
+
+ for address in self.addresses:
+ assert_equal(node.getaccount(address), self.name)
+
+ assert_equal(
+ set(node.getaddressesbyaccount(self.name)), set(self.addresses))
+
+
+def change_account(node, address, old_account, new_account):
+ assert_equal(address in old_account.addresses, True)
+ node.setaccount(address, new_account.name)
+
+ old_account.addresses.remove(address)
+ new_account.add_address(address)
+
+ # Calling setaccount on an address which was previously the receiving
+ # address of a different account should reset the receiving address of
+ # the old account, causing getaccountaddress to return a brand new
+ # address.
+ if address == old_account.receive_address:
+ new_address = node.getaccountaddress(old_account.name)
+ assert_equal(new_address not in old_account.addresses, True)
+ assert_equal(new_address not in new_account.addresses, True)
+ old_account.add_receive_address(new_address)
+
+ old_account.verify(node)
+ new_account.verify(node)
+
if __name__ == '__main__':
WalletAccountsTest().main()
diff --git a/test/functional/wallet-dump.py b/test/functional/wallet-dump.py
index 47de8777a6..8b81c81ba8 100755
--- a/test/functional/wallet-dump.py
+++ b/test/functional/wallet-dump.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016 The Bitcoin Core developers
+# Copyright (c) 2016-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 the dumpwallet RPC."""
@@ -10,13 +10,14 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (assert_equal, assert_raises_rpc_error)
-def read_dump(file_name, addrs, hd_master_addr_old):
+def read_dump(file_name, addrs, script_addrs, hd_master_addr_old):
"""
Read the given dump, count the addrs that match, count change and reserve.
Also check that the old hd_master is inactive
"""
with open(file_name, encoding='utf8') as inputfile:
found_addr = 0
+ found_script_addr = 0
found_addr_chg = 0
found_addr_rsv = 0
hd_master_addr_ret = None
@@ -38,6 +39,9 @@ def read_dump(file_name, addrs, hd_master_addr_old):
# ensure we have generated a new hd master key
assert(hd_master_addr_old != addr)
hd_master_addr_ret = addr
+ elif keytype == "script=1":
+ # scripts don't have keypaths
+ keypath = None
else:
keypath = addr_keypath.rstrip().split("hdkeypath=")[1]
@@ -52,13 +56,20 @@ def read_dump(file_name, addrs, hd_master_addr_old):
elif keytype == "reserve=1":
found_addr_rsv += 1
break
- return found_addr, found_addr_chg, found_addr_rsv, hd_master_addr_ret
+
+ # count scripts
+ for script_addr in script_addrs:
+ if script_addr == addr.rstrip() and keytype == "script=1":
+ found_script_addr += 1
+ break
+
+ return found_addr, found_script_addr, found_addr_chg, found_addr_rsv, hd_master_addr_ret
class WalletDumpTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
- self.extra_args = [["-keypool=90"]]
+ self.extra_args = [["-keypool=90", "-addresstype=legacy"]]
def setup_network(self, split=False):
# Use 1 minute timeout because the initial getnewaddress RPC can take
@@ -81,13 +92,19 @@ class WalletDumpTest(BitcoinTestFramework):
# Should be a no-op:
self.nodes[0].keypoolrefill()
+ # Test scripts dump by adding a P2SH witness and a 1-of-1 multisig address
+ witness_addr = self.nodes[0].addwitnessaddress(addrs[0]["address"], True)
+ multisig_addr = self.nodes[0].addmultisigaddress(1, [addrs[1]["address"]])
+ script_addrs = [witness_addr, multisig_addr]
+
# dump unencrypted wallet
result = self.nodes[0].dumpwallet(tmpdir + "/node0/wallet.unencrypted.dump")
assert_equal(result['filename'], os.path.abspath(tmpdir + "/node0/wallet.unencrypted.dump"))
- found_addr, found_addr_chg, found_addr_rsv, hd_master_addr_unenc = \
- read_dump(tmpdir + "/node0/wallet.unencrypted.dump", addrs, None)
+ found_addr, found_script_addr, found_addr_chg, found_addr_rsv, hd_master_addr_unenc = \
+ read_dump(tmpdir + "/node0/wallet.unencrypted.dump", addrs, script_addrs, None)
assert_equal(found_addr, test_addr_count) # all keys must be in the dump
+ assert_equal(found_script_addr, 2) # all scripts must be in the dump
assert_equal(found_addr_chg, 50) # 50 blocks where mined
assert_equal(found_addr_rsv, 90*2) # 90 keys plus 100% internal keys
@@ -99,14 +116,29 @@ class WalletDumpTest(BitcoinTestFramework):
self.nodes[0].keypoolrefill()
self.nodes[0].dumpwallet(tmpdir + "/node0/wallet.encrypted.dump")
- found_addr, found_addr_chg, found_addr_rsv, _ = \
- read_dump(tmpdir + "/node0/wallet.encrypted.dump", addrs, hd_master_addr_unenc)
+ found_addr, found_script_addr, found_addr_chg, found_addr_rsv, _ = \
+ read_dump(tmpdir + "/node0/wallet.encrypted.dump", addrs, script_addrs, hd_master_addr_unenc)
assert_equal(found_addr, test_addr_count)
+ assert_equal(found_script_addr, 2)
assert_equal(found_addr_chg, 90*2 + 50) # old reserve keys are marked as change now
assert_equal(found_addr_rsv, 90*2)
# Overwriting should fail
assert_raises_rpc_error(-8, "already exists", self.nodes[0].dumpwallet, tmpdir + "/node0/wallet.unencrypted.dump")
+ # Restart node with new wallet, and test importwallet
+ self.stop_node(0)
+ self.start_node(0, ['-wallet=w2'])
+
+ # Make sure the address is not IsMine before import
+ result = self.nodes[0].validateaddress(multisig_addr)
+ assert(result['ismine'] == False)
+
+ self.nodes[0].importwallet(os.path.abspath(tmpdir + "/node0/wallet.unencrypted.dump"))
+
+ # Now check IsMine is true
+ result = self.nodes[0].validateaddress(multisig_addr)
+ assert(result['ismine'] == True)
+
if __name__ == '__main__':
WalletDumpTest().main ()
diff --git a/test/functional/wallet-encryption.py b/test/functional/wallet-encryption.py
index db62e1e30f..452e8ec291 100755
--- a/test/functional/wallet-encryption.py
+++ b/test/functional/wallet-encryption.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016 The Bitcoin Core developers
+# Copyright (c) 2016-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 Wallet encryption"""
diff --git a/test/functional/wallet-hd.py b/test/functional/wallet-hd.py
index 5ef3bf5bff..9f0e9acb47 100755
--- a/test/functional/wallet-hd.py
+++ b/test/functional/wallet-hd.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2016 The Bitcoin Core developers
+# Copyright (c) 2016-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 Hierarchical Deterministic wallet function."""
@@ -10,6 +10,7 @@ from test_framework.util import (
connect_nodes_bi,
)
import shutil
+import os
class WalletHDTest(BitcoinTestFramework):
def set_test_params(self):
@@ -70,9 +71,9 @@ class WalletHDTest(BitcoinTestFramework):
self.stop_node(1)
# we need to delete the complete regtest directory
# otherwise node1 would auto-recover all funds in flag the keypool keys as used
- shutil.rmtree(tmpdir + "/node1/regtest/blocks")
- shutil.rmtree(tmpdir + "/node1/regtest/chainstate")
- shutil.copyfile(tmpdir + "/hd.bak", tmpdir + "/node1/regtest/wallet.dat")
+ shutil.rmtree(os.path.join(tmpdir, "node1/regtest/blocks"))
+ shutil.rmtree(os.path.join(tmpdir, "node1/regtest/chainstate"))
+ shutil.copyfile(os.path.join(tmpdir, "hd.bak"), os.path.join(tmpdir, "node1/regtest/wallets/wallet.dat"))
self.start_node(1)
# Assert that derivation is deterministic
@@ -91,6 +92,22 @@ class WalletHDTest(BitcoinTestFramework):
self.start_node(1, extra_args=self.extra_args[1] + ['-rescan'])
assert_equal(self.nodes[1].getbalance(), num_hd_adds + 1)
+ # Try a RPC based rescan
+ self.stop_node(1)
+ shutil.rmtree(os.path.join(tmpdir, "node1/regtest/blocks"))
+ shutil.rmtree(os.path.join(tmpdir, "node1/regtest/chainstate"))
+ shutil.copyfile(os.path.join(tmpdir, "hd.bak"), os.path.join(tmpdir, "node1/regtest/wallet.dat"))
+ self.start_node(1, extra_args=self.extra_args[1])
+ connect_nodes_bi(self.nodes, 0, 1)
+ self.sync_all()
+ out = self.nodes[1].rescanblockchain(0, 1)
+ assert_equal(out['start_height'], 0)
+ assert_equal(out['stop_height'], 1)
+ out = self.nodes[1].rescanblockchain()
+ assert_equal(out['start_height'], 0)
+ assert_equal(out['stop_height'], self.nodes[1].getblockcount())
+ assert_equal(self.nodes[1].getbalance(), num_hd_adds + 1)
+
# send a tx and make sure its using the internal chain for the changeoutput
txid = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1)
outs = self.nodes[1].decoderawtransaction(self.nodes[1].gettransaction(txid)['hex'])['vout']
diff --git a/test/functional/wallet.py b/test/functional/wallet.py
index 9d8ae50354..a90dbc8adf 100755
--- a/test/functional/wallet.py
+++ b/test/functional/wallet.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 the wallet."""
@@ -27,6 +27,9 @@ class WalletTest(BitcoinTestFramework):
assert_fee_amount(fee, tx_size, fee_per_byte * 1000)
return curr_balance
+ def get_vsize(self, txn):
+ return self.nodes[0].decoderawtransaction(txn)['vsize']
+
def run_test(self):
# Check that there's no UTXO on none of the nodes
assert_equal(len(self.nodes[0].listunspent()), 0)
@@ -100,11 +103,19 @@ class WalletTest(BitcoinTestFramework):
# Exercise locking of unspent outputs
unspent_0 = self.nodes[2].listunspent()[0]
unspent_0 = {"txid": unspent_0["txid"], "vout": unspent_0["vout"]}
+ assert_raises_rpc_error(-8, "Invalid parameter, expected locked output", self.nodes[2].lockunspent, True, [unspent_0])
self.nodes[2].lockunspent(False, [unspent_0])
+ assert_raises_rpc_error(-8, "Invalid parameter, output already locked", self.nodes[2].lockunspent, False, [unspent_0])
assert_raises_rpc_error(-4, "Insufficient funds", self.nodes[2].sendtoaddress, self.nodes[2].getnewaddress(), 20)
assert_equal([unspent_0], self.nodes[2].listlockunspent())
self.nodes[2].lockunspent(True, [unspent_0])
assert_equal(len(self.nodes[2].listlockunspent()), 0)
+ assert_raises_rpc_error(-8, "Invalid parameter, unknown transaction",
+ self.nodes[2].lockunspent, False,
+ [{"txid": "0000000000000000000000000000000000", "vout": 0}])
+ assert_raises_rpc_error(-8, "Invalid parameter, vout index out of bounds",
+ self.nodes[2].lockunspent, False,
+ [{"txid": unspent_0["txid"], "vout": 999}])
# Have node1 generate 100 blocks (so node0 can recover the fee)
self.nodes[1].generate(100)
@@ -143,6 +154,10 @@ class WalletTest(BitcoinTestFramework):
assert_equal(self.nodes[2].getbalance(), 94)
assert_equal(self.nodes[2].getbalance("from1"), 94-21)
+ # Verify that a spent output cannot be locked anymore
+ spent_0 = {"txid": node0utxos[0]["txid"], "vout": node0utxos[0]["vout"]}
+ assert_raises_rpc_error(-8, "Invalid parameter, expected unspent output", self.nodes[0].lockunspent, False, [spent_0])
+
# Send 10 BTC normal
address = self.nodes[0].getnewaddress("test")
fee_per_byte = Decimal('0.001') / 1000
@@ -150,7 +165,7 @@ class WalletTest(BitcoinTestFramework):
txid = self.nodes[2].sendtoaddress(address, 10, "", "", False)
self.nodes[2].generate(1)
self.sync_all([self.nodes[0:3]])
- node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), Decimal('84'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid)))
+ node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), Decimal('84'), fee_per_byte, self.get_vsize(self.nodes[2].getrawtransaction(txid)))
assert_equal(self.nodes[0].getbalance(), Decimal('10'))
# Send 10 BTC with subtract fee from amount
@@ -159,14 +174,14 @@ class WalletTest(BitcoinTestFramework):
self.sync_all([self.nodes[0:3]])
node_2_bal -= Decimal('10')
assert_equal(self.nodes[2].getbalance(), node_2_bal)
- node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), Decimal('20'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid)))
+ node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), Decimal('20'), fee_per_byte, self.get_vsize(self.nodes[2].getrawtransaction(txid)))
# Sendmany 10 BTC
txid = self.nodes[2].sendmany('from1', {address: 10}, 0, "", [])
self.nodes[2].generate(1)
self.sync_all([self.nodes[0:3]])
node_0_bal += Decimal('10')
- node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), node_2_bal - Decimal('10'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid)))
+ node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), node_2_bal - Decimal('10'), fee_per_byte, self.get_vsize(self.nodes[2].getrawtransaction(txid)))
assert_equal(self.nodes[0].getbalance(), node_0_bal)
# Sendmany 10 BTC with subtract fee from amount
@@ -175,7 +190,7 @@ class WalletTest(BitcoinTestFramework):
self.sync_all([self.nodes[0:3]])
node_2_bal -= Decimal('10')
assert_equal(self.nodes[2].getbalance(), node_2_bal)
- node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), node_0_bal + Decimal('10'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid)))
+ node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), node_0_bal + Decimal('10'), fee_per_byte, self.get_vsize(self.nodes[2].getrawtransaction(txid)))
# Test ResendWalletTransactions:
# Create a couple of transactions, then start up a fourth
diff --git a/test/functional/walletbackup.py b/test/functional/walletbackup.py
index 15ea26afa1..b4be7debb5 100755
--- a/test/functional/walletbackup.py
+++ b/test/functional/walletbackup.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 the wallet backup features.
@@ -90,9 +90,9 @@ class WalletBackupTest(BitcoinTestFramework):
self.stop_node(2)
def erase_three(self):
- os.remove(self.options.tmpdir + "/node0/regtest/wallet.dat")
- os.remove(self.options.tmpdir + "/node1/regtest/wallet.dat")
- os.remove(self.options.tmpdir + "/node2/regtest/wallet.dat")
+ os.remove(self.options.tmpdir + "/node0/regtest/wallets/wallet.dat")
+ os.remove(self.options.tmpdir + "/node1/regtest/wallets/wallet.dat")
+ os.remove(self.options.tmpdir + "/node2/regtest/wallets/wallet.dat")
def run_test(self):
self.log.info("Generating initial blockchain")
@@ -154,9 +154,9 @@ class WalletBackupTest(BitcoinTestFramework):
shutil.rmtree(self.options.tmpdir + "/node2/regtest/chainstate")
# Restore wallets from backup
- shutil.copyfile(tmpdir + "/node0/wallet.bak", tmpdir + "/node0/regtest/wallet.dat")
- shutil.copyfile(tmpdir + "/node1/wallet.bak", tmpdir + "/node1/regtest/wallet.dat")
- shutil.copyfile(tmpdir + "/node2/wallet.bak", tmpdir + "/node2/regtest/wallet.dat")
+ shutil.copyfile(tmpdir + "/node0/wallet.bak", tmpdir + "/node0/regtest/wallets/wallet.dat")
+ shutil.copyfile(tmpdir + "/node1/wallet.bak", tmpdir + "/node1/regtest/wallets/wallet.dat")
+ shutil.copyfile(tmpdir + "/node2/wallet.bak", tmpdir + "/node2/regtest/wallets/wallet.dat")
self.log.info("Re-starting nodes")
self.start_three()
@@ -190,6 +190,16 @@ class WalletBackupTest(BitcoinTestFramework):
assert_equal(self.nodes[1].getbalance(), balance1)
assert_equal(self.nodes[2].getbalance(), balance2)
+ # Backup to source wallet file must fail
+ sourcePaths = [
+ tmpdir + "/node0/regtest/wallets/wallet.dat",
+ tmpdir + "/node0/./regtest/wallets/wallet.dat",
+ tmpdir + "/node0/regtest/wallets/",
+ tmpdir + "/node0/regtest/wallets"]
+
+ for sourcePath in sourcePaths:
+ assert_raises_rpc_error(-4, "backup failed", self.nodes[0].backupwallet, sourcePath)
+
if __name__ == '__main__':
WalletBackupTest().main()
diff --git a/test/functional/zapwallettxes.py b/test/functional/zapwallettxes.py
index 8cd622dc8e..0471f2afb3 100755
--- a/test/functional/zapwallettxes.py
+++ b/test/functional/zapwallettxes.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2014-2016 The Bitcoin Core developers
+# Copyright (c) 2014-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 the zapwallettxes functionality.
diff --git a/test/functional/zmq_test.py b/test/functional/zmq_test.py
index 382ef5bae2..86ccea4394 100755
--- a/test/functional/zmq_test.py
+++ b/test/functional/zmq_test.py
@@ -1,17 +1,38 @@
#!/usr/bin/env python3
-# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Copyright (c) 2015-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 the ZMQ API."""
+"""Test the ZMQ notification interface."""
import configparser
import os
import struct
from test_framework.test_framework import BitcoinTestFramework, SkipTest
+from test_framework.mininode import CTransaction
from test_framework.util import (assert_equal,
bytes_to_hex_str,
hash256,
)
+from io import BytesIO
+
+class ZMQSubscriber:
+ def __init__(self, socket, topic):
+ self.sequence = 0
+ self.socket = socket
+ self.topic = topic
+
+ import zmq
+ self.socket.setsockopt(zmq.SUBSCRIBE, self.topic)
+
+ def receive(self):
+ topic, body, seq = self.socket.recv_multipart()
+ # Topic should match the subscriber topic.
+ assert_equal(topic, self.topic)
+ # Sequence should be incremental.
+ assert_equal(struct.unpack('<I', seq)[-1], self.sequence)
+ self.sequence += 1
+ return body
+
class ZMQTest (BitcoinTestFramework):
def set_test_params(self):
@@ -24,26 +45,33 @@ class ZMQTest (BitcoinTestFramework):
except ImportError:
raise SkipTest("python3-zmq module not available.")
- # Check that bitcoin has been built with ZMQ enabled
+ # Check that bitcoin has been built with ZMQ enabled.
config = configparser.ConfigParser()
if not self.options.configfile:
- self.options.configfile = os.path.dirname(__file__) + "/../config.ini"
+ self.options.configfile = os.path.abspath(os.path.join(os.path.dirname(__file__), "../config.ini"))
config.read_file(open(self.options.configfile))
if not config["components"].getboolean("ENABLE_ZMQ"):
raise SkipTest("bitcoind has not been built with zmq enabled.")
- self.zmqContext = zmq.Context()
- self.zmqSubSocket = self.zmqContext.socket(zmq.SUB)
- self.zmqSubSocket.set(zmq.RCVTIMEO, 60000)
- self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashblock")
- self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashtx")
- self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawblock")
- self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawtx")
- ip_address = "tcp://127.0.0.1:28332"
- self.zmqSubSocket.connect(ip_address)
- self.extra_args = [['-zmqpubhashblock=%s' % ip_address, '-zmqpubhashtx=%s' % ip_address,
- '-zmqpubrawblock=%s' % ip_address, '-zmqpubrawtx=%s' % ip_address], []]
+ # Initialize ZMQ context and socket.
+ # All messages are received in the same socket which means
+ # that this test fails if the publishing order changes.
+ # Note that the publishing order is not defined in the documentation and
+ # is subject to change.
+ address = "tcp://127.0.0.1:28332"
+ self.zmq_context = zmq.Context()
+ socket = self.zmq_context.socket(zmq.SUB)
+ socket.set(zmq.RCVTIMEO, 60000)
+ socket.connect(address)
+
+ # Subscribe to all available topics.
+ self.hashblock = ZMQSubscriber(socket, b"hashblock")
+ self.hashtx = ZMQSubscriber(socket, b"hashtx")
+ self.rawblock = ZMQSubscriber(socket, b"rawblock")
+ self.rawtx = ZMQSubscriber(socket, b"rawtx")
+
+ self.extra_args = [["-zmqpub%s=%s" % (sub.topic.decode(), address) for sub in [self.hashblock, self.hashtx, self.rawblock, self.rawtx]], []]
self.add_nodes(self.num_nodes, self.extra_args)
self.start_nodes()
@@ -51,103 +79,48 @@ class ZMQTest (BitcoinTestFramework):
try:
self._zmq_test()
finally:
- # Destroy the zmq context
- self.log.debug("Destroying zmq context")
- self.zmqContext.destroy(linger=None)
+ # Destroy the ZMQ context.
+ self.log.debug("Destroying ZMQ context")
+ self.zmq_context.destroy(linger=None)
def _zmq_test(self):
- genhashes = self.nodes[0].generate(1)
+ num_blocks = 5
+ self.log.info("Generate %(n)d blocks (and %(n)d coinbase txes)" % {"n": num_blocks})
+ genhashes = self.nodes[0].generate(num_blocks)
self.sync_all()
- self.log.info("Wait for tx")
- msg = self.zmqSubSocket.recv_multipart()
- topic = msg[0]
- assert_equal(topic, b"hashtx")
- txhash = msg[1]
- msgSequence = struct.unpack('<I', msg[-1])[-1]
- assert_equal(msgSequence, 0) # must be sequence 0 on hashtx
-
- # rawtx
- msg = self.zmqSubSocket.recv_multipart()
- topic = msg[0]
- assert_equal(topic, b"rawtx")
- body = msg[1]
- msgSequence = struct.unpack('<I', msg[-1])[-1]
- assert_equal(msgSequence, 0) # must be sequence 0 on rawtx
-
- # Check that the rawtx hashes to the hashtx
- assert_equal(hash256(body), txhash)
-
- self.log.info("Wait for block")
- msg = self.zmqSubSocket.recv_multipart()
- topic = msg[0]
- assert_equal(topic, b"hashblock")
- body = msg[1]
- msgSequence = struct.unpack('<I', msg[-1])[-1]
- assert_equal(msgSequence, 0) # must be sequence 0 on hashblock
- blkhash = bytes_to_hex_str(body)
- assert_equal(genhashes[0], blkhash) # blockhash from generate must be equal to the hash received over zmq
-
- # rawblock
- msg = self.zmqSubSocket.recv_multipart()
- topic = msg[0]
- assert_equal(topic, b"rawblock")
- body = msg[1]
- msgSequence = struct.unpack('<I', msg[-1])[-1]
- assert_equal(msgSequence, 0) #must be sequence 0 on rawblock
-
- # Check the hash of the rawblock's header matches generate
- assert_equal(genhashes[0], bytes_to_hex_str(hash256(body[:80])))
-
- self.log.info("Generate 10 blocks (and 10 coinbase txes)")
- n = 10
- genhashes = self.nodes[1].generate(n)
- self.sync_all()
+ for x in range(num_blocks):
+ # Should receive the coinbase txid.
+ txid = self.hashtx.receive()
+
+ # Should receive the coinbase raw transaction.
+ hex = self.rawtx.receive()
+ tx = CTransaction()
+ tx.deserialize(BytesIO(hex))
+ tx.calc_sha256()
+ assert_equal(tx.hash, bytes_to_hex_str(txid))
- zmqHashes = []
- zmqRawHashed = []
- blockcount = 0
- for x in range(n * 4):
- msg = self.zmqSubSocket.recv_multipart()
- topic = msg[0]
- body = msg[1]
- if topic == b"hashblock":
- zmqHashes.append(bytes_to_hex_str(body))
- msgSequence = struct.unpack('<I', msg[-1])[-1]
- assert_equal(msgSequence, blockcount + 1)
- blockcount += 1
- if topic == b"rawblock":
- zmqRawHashed.append(bytes_to_hex_str(hash256(body[:80])))
- msgSequence = struct.unpack('<I', msg[-1])[-1]
- assert_equal(msgSequence, blockcount)
-
- for x in range(n):
- assert_equal(genhashes[x], zmqHashes[x]) # blockhash from generate must be equal to the hash received over zmq
- assert_equal(genhashes[x], zmqRawHashed[x])
+ # Should receive the generated block hash.
+ hash = bytes_to_hex_str(self.hashblock.receive())
+ assert_equal(genhashes[x], hash)
+ # The block should only have the coinbase txid.
+ assert_equal([bytes_to_hex_str(txid)], self.nodes[1].getblock(hash)["tx"])
+
+ # Should receive the generated raw block.
+ block = self.rawblock.receive()
+ assert_equal(genhashes[x], bytes_to_hex_str(hash256(block[:80])))
self.log.info("Wait for tx from second node")
- # test tx from a second node
- hashRPC = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.0)
+ payment_txid = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.0)
self.sync_all()
- # now we should receive a zmq msg because the tx was broadcast
- msg = self.zmqSubSocket.recv_multipart()
- topic = msg[0]
- assert_equal(topic, b"hashtx")
- body = msg[1]
- hashZMQ = bytes_to_hex_str(body)
- msgSequence = struct.unpack('<I', msg[-1])[-1]
- assert_equal(msgSequence, blockcount + 1)
-
- msg = self.zmqSubSocket.recv_multipart()
- topic = msg[0]
- assert_equal(topic, b"rawtx")
- body = msg[1]
- hashedZMQ = bytes_to_hex_str(hash256(body))
- msgSequence = struct.unpack('<I', msg[-1])[-1]
- assert_equal(msgSequence, blockcount+1)
- assert_equal(hashRPC, hashZMQ) # txid from sendtoaddress must be equal to the hash received over zmq
- assert_equal(hashRPC, hashedZMQ)
+ # Should receive the broadcasted txid.
+ txid = self.hashtx.receive()
+ assert_equal(payment_txid, bytes_to_hex_str(txid))
+
+ # Should receive the broadcasted raw transaction.
+ hex = self.rawtx.receive()
+ assert_equal(payment_txid, bytes_to_hex_str(hash256(hex)))
if __name__ == '__main__':
ZMQTest().main()