aboutsummaryrefslogtreecommitdiff
path: root/test/functional
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional')
-rwxr-xr-xtest/functional/example_test.py2
-rwxr-xr-xtest/functional/feature_bip9_softforks.py2
-rwxr-xr-xtest/functional/feature_block.py1308
-rwxr-xr-xtest/functional/feature_blocksdir.py37
-rwxr-xr-xtest/functional/feature_config_args.py9
-rwxr-xr-xtest/functional/feature_fee_estimation.py16
-rwxr-xr-xtest/functional/feature_logging.py7
-rwxr-xr-xtest/functional/feature_maxuploadtarget.py12
-rwxr-xr-xtest/functional/feature_pruning.py37
-rwxr-xr-xtest/functional/feature_reindex.py7
-rwxr-xr-xtest/functional/feature_uacomment.py12
-rwxr-xr-xtest/functional/interface_bitcoin_cli.py10
-rwxr-xr-xtest/functional/interface_rest.py41
-rwxr-xr-xtest/functional/mempool_persist.py6
-rwxr-xr-xtest/functional/mining_prioritisetransaction.py2
-rwxr-xr-xtest/functional/p2p_compactblocks.py12
-rwxr-xr-xtest/functional/p2p_feefilter.py4
-rwxr-xr-xtest/functional/p2p_leak.py2
-rwxr-xr-xtest/functional/p2p_node_network_limited.py2
-rwxr-xr-xtest/functional/p2p_segwit.py14
-rwxr-xr-xtest/functional/p2p_timeouts.py8
-rwxr-xr-xtest/functional/p2p_unrequested_blocks.py2
-rwxr-xr-xtest/functional/rpc_bind.py4
-rwxr-xr-xtest/functional/rpc_net.py44
-rwxr-xr-xtest/functional/rpc_users.py15
-rwxr-xr-xtest/functional/test_framework/comptool.py6
-rwxr-xr-xtest/functional/test_framework/messages.py2
-rwxr-xr-xtest/functional/test_framework/mininode.py4
-rwxr-xr-xtest/functional/test_framework/test_framework.py27
-rwxr-xr-xtest/functional/test_framework/test_node.py52
-rw-r--r--test/functional/test_framework/util.py29
-rwxr-xr-xtest/functional/test_runner.py29
-rwxr-xr-xtest/functional/wallet_abandonconflict.py4
-rwxr-xr-xtest/functional/wallet_accounts.py206
-rwxr-xr-xtest/functional/wallet_backup.py48
-rwxr-xr-xtest/functional/wallet_basic.py6
-rwxr-xr-xtest/functional/wallet_hd.py28
-rwxr-xr-xtest/functional/wallet_import_rescan.py2
-rwxr-xr-xtest/functional/wallet_keypool_topup.py21
-rwxr-xr-xtest/functional/wallet_labels.py206
-rwxr-xr-xtest/functional/wallet_listreceivedby.py60
-rwxr-xr-xtest/functional/wallet_listsinceblock.py2
-rwxr-xr-xtest/functional/wallet_multiwallet.py32
43 files changed, 1256 insertions, 1123 deletions
diff --git a/test/functional/example_test.py b/test/functional/example_test.py
index 12be685ecf..05d1c1bf4e 100755
--- a/test/functional/example_test.py
+++ b/test/functional/example_test.py
@@ -38,7 +38,7 @@ class BaseNode(P2PInterface):
def __init__(self):
"""Initialize the P2PInterface
- Used to inialize custom properties for the Node that aren't
+ Used to initialize custom properties for the Node that aren't
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
diff --git a/test/functional/feature_bip9_softforks.py b/test/functional/feature_bip9_softforks.py
index 71d3d04002..ac6176e976 100755
--- a/test/functional/feature_bip9_softforks.py
+++ b/test/functional/feature_bip9_softforks.py
@@ -241,7 +241,7 @@ class BIP9SoftForksTest(ComparisonTestFramework):
self.test.clear_all_connections()
self.stop_nodes()
self.nodes = []
- shutil.rmtree(self.options.tmpdir + "/node0")
+ shutil.rmtree(get_datadir_path(self.options.tmpdir, 0))
self.setup_chain()
self.setup_network()
self.test.add_all_connections(self.nodes)
diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py
index fe9bbda14b..181c7f3369 100755
--- a/test/functional/feature_block.py
+++ b/test/functional/feature_block.py
@@ -2,36 +2,59 @@
# 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.
-
-This reimplements tests from the bitcoinj/FullBlockTestGenerator used
-by the pull-tester.
-
-We use the testing framework in which we expect a particular answer from
-each test.
-"""
-
-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 *
+"""Test block processing."""
+import copy
+import struct
import time
+
+from test_framework.blocktools import create_block, create_coinbase, create_transaction, get_legacy_sigopcount_block
from test_framework.key import CECKey
-from test_framework.script import *
-from test_framework.mininode import network_thread_start
-import struct
+from test_framework.messages import (
+ CBlock,
+ COIN,
+ COutPoint,
+ CTransaction,
+ CTxIn,
+ CTxOut,
+ MAX_BLOCK_BASE_SIZE,
+ uint256_from_compact,
+ uint256_from_str,
+)
+from test_framework.mininode import P2PDataStore, network_thread_start, network_thread_join
+from test_framework.script import (
+ CScript,
+ MAX_SCRIPT_ELEMENT_SIZE,
+ OP_2DUP,
+ OP_CHECKMULTISIG,
+ OP_CHECKMULTISIGVERIFY,
+ OP_CHECKSIG,
+ OP_CHECKSIGVERIFY,
+ OP_ELSE,
+ OP_ENDIF,
+ OP_EQUAL,
+ OP_FALSE,
+ OP_HASH160,
+ OP_IF,
+ OP_INVALIDOPCODE,
+ OP_RETURN,
+ OP_TRUE,
+ SIGHASH_ALL,
+ SignatureHash,
+ hash160,
+)
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+
+MAX_BLOCK_SIGOPS = 20000
class PreviousSpendableOutput():
- def __init__(self, tx = CTransaction(), n = -1):
+ def __init__(self, tx=CTransaction(), n=-1):
self.tx = tx
self.n = n # the output we're spending
# Use this class for tests that require behavior other than normal "mininode" behavior.
# For now, it is used to serialize a bloated varint (b64).
class CBrokenBlock(CBlock):
- def __init__(self, header=None):
- super(CBrokenBlock, self).__init__(header)
-
def initialize(self, base_block):
self.vtx = copy.deepcopy(base_block.vtx)
self.hashMerkleRoot = self.calc_merkle_root()
@@ -48,381 +71,272 @@ class CBrokenBlock(CBlock):
return r
def normal_serialize(self):
- r = b""
- r += super(CBrokenBlock, self).serialize()
- return r
+ return super().serialize()
-class FullBlockTest(ComparisonTestFramework):
- # Can either run this test as 1 node with expected answers, or two and compare them.
- # Change the "outcome" variable from each TestInstance object to only do the comparison.
+class FullBlockTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
+ self.extra_args = [[]]
+
+ def run_test(self):
+ node = self.nodes[0] # convenience reference to the node
+
+ # reconnect_p2p() expects the network thread to be running
+ network_thread_start()
+
+ self.reconnect_p2p()
+
self.block_heights = {}
self.coinbase_key = CECKey()
self.coinbase_key.set_secretbytes(b"horsebattery")
self.coinbase_pubkey = self.coinbase_key.get_pubkey()
self.tip = None
self.blocks = {}
-
- def add_options(self, parser):
- super().add_options(parser)
- parser.add_option("--runbarelyexpensive", dest="runbarelyexpensive", default=True)
-
- def run_test(self):
- self.test = TestManager(self, self.options.tmpdir)
- self.test.add_all_connections(self.nodes)
- network_thread_start()
- self.test.run()
-
- def add_transactions_to_block(self, block, tx_list):
- [ tx.rehash() for tx in tx_list ]
- block.vtx.extend(tx_list)
-
- # this is a little handier to use than the version in blocktools.py
- def create_tx(self, spend_tx, n, value, script=CScript([OP_TRUE])):
- tx = create_transaction(spend_tx, n, b"", value, script)
- return tx
-
- # sign a transaction, using the key we know about
- # this signs input 0 in tx, which is assumed to be spending output n in spend_tx
- def sign_tx(self, tx, spend_tx, n):
- scriptPubKey = bytearray(spend_tx.vout[n].scriptPubKey)
- if (scriptPubKey[0] == OP_TRUE): # an anyone-can-spend
- tx.vin[0].scriptSig = CScript()
- return
- (sighash, err) = SignatureHash(spend_tx.vout[n].scriptPubKey, tx, 0, SIGHASH_ALL)
- tx.vin[0].scriptSig = CScript([self.coinbase_key.sign(sighash) + bytes(bytearray([SIGHASH_ALL]))])
-
- def create_and_sign_transaction(self, spend_tx, n, value, script=CScript([OP_TRUE])):
- tx = self.create_tx(spend_tx, n, value, script)
- self.sign_tx(tx, spend_tx, n)
- tx.rehash()
- return tx
-
- def next_block(self, number, spend=None, additional_coinbase_value=0, script=CScript([OP_TRUE]), solve=True):
- if self.tip == None:
- base_block_hash = self.genesis_hash
- block_time = int(time.time())+1
- else:
- base_block_hash = self.tip.sha256
- block_time = self.tip.nTime + 1
- # First create the coinbase
- height = self.block_heights[base_block_hash] + 1
- coinbase = create_coinbase(height, self.coinbase_pubkey)
- coinbase.vout[0].nValue += additional_coinbase_value
- coinbase.rehash()
- if spend == None:
- block = create_block(base_block_hash, coinbase, block_time)
- else:
- coinbase.vout[0].nValue += spend.tx.vout[spend.n].nValue - 1 # all but one satoshi to fees
- coinbase.rehash()
- block = create_block(base_block_hash, coinbase, block_time)
- tx = create_transaction(spend.tx, spend.n, b"", 1, script) # spend 1 satoshi
- self.sign_tx(tx, spend.tx, spend.n)
- self.add_transactions_to_block(block, [tx])
- block.hashMerkleRoot = block.calc_merkle_root()
- if solve:
- block.solve()
- self.tip = block
- self.block_heights[block.sha256] = height
- assert number not in self.blocks
- self.blocks[number] = block
- return block
-
- def get_tests(self):
self.genesis_hash = int(self.nodes[0].getbestblockhash(), 16)
self.block_heights[self.genesis_hash] = 0
- spendable_outputs = []
-
- # save the current tip so it can be spent by a later block
- def save_spendable_output():
- spendable_outputs.append(self.tip)
-
- # get an output that we previously marked as spendable
- def get_spendable_output():
- return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0)
-
- # returns a test case that asserts that the current tip was accepted
- def accepted():
- return TestInstance([[self.tip, True]])
-
- # returns a test case that asserts that the current tip was rejected
- def rejected(reject = None):
- if reject is None:
- return TestInstance([[self.tip, False]])
- else:
- return TestInstance([[self.tip, reject]])
-
- # move the tip back to a previous block
- def tip(number):
- self.tip = self.blocks[number]
-
- # adds transactions to the block and updates state
- def update_block(block_number, new_transactions):
- block = self.blocks[block_number]
- self.add_transactions_to_block(block, new_transactions)
- old_sha256 = block.sha256
- block.hashMerkleRoot = block.calc_merkle_root()
- block.solve()
- # Update the internal state just like in next_block
- self.tip = block
- if block.sha256 != old_sha256:
- self.block_heights[block.sha256] = self.block_heights[old_sha256]
- del self.block_heights[old_sha256]
- self.blocks[block_number] = block
- return block
-
- # shorthand for functions
- block = self.next_block
- create_tx = self.create_tx
- create_and_sign_tx = self.create_and_sign_transaction
-
- # these must be updated if consensus changes
- MAX_BLOCK_SIGOPS = 20000
-
+ self.spendable_outputs = []
# Create a new block
- block(0)
- save_spendable_output()
- yield accepted()
-
+ b0 = self.next_block(0)
+ self.save_spendable_output()
+ self.sync_blocks([b0])
- # Now we need that block to mature so we can spend the coinbase.
- test = TestInstance(sync_every_block=False)
+ # Allow the block to mature
+ blocks = []
for i in range(99):
- block(5000 + i)
- test.blocks_and_transactions.append([self.tip, True])
- save_spendable_output()
- yield test
+ blocks.append(self.next_block(5000 + i))
+ self.save_spendable_output()
+ self.sync_blocks(blocks)
# collect spendable outputs now to avoid cluttering the code later on
out = []
for i in range(33):
- out.append(get_spendable_output())
+ out.append(self.get_spendable_output())
# Start by building a couple of blocks on top (which output is spent is
# in parentheses):
# genesis -> b1 (0) -> b2 (1)
- block(1, spend=out[0])
- save_spendable_output()
- yield accepted()
+ b1 = self.next_block(1, spend=out[0])
+ self.save_spendable_output()
- block(2, spend=out[1])
- yield accepted()
- save_spendable_output()
+ b2 = self.next_block(2, spend=out[1])
+ self.save_spendable_output()
- # so fork like this:
+ self.sync_blocks([b1, b2])
+
+ # Fork like this:
#
# genesis -> b1 (0) -> b2 (1)
# \-> b3 (1)
#
# Nothing should happen at this point. We saw b2 first so it takes priority.
- tip(1)
- b3 = block(3, spend=out[1])
+ self.log.info("Don't reorg to a chain of the same length")
+ self.move_tip(1)
+ b3 = self.next_block(3, spend=out[1])
txout_b3 = PreviousSpendableOutput(b3.vtx[1], 0)
- yield rejected()
-
+ self.sync_blocks([b3], False)
# Now we add another block to make the alternative chain longer.
#
# genesis -> b1 (0) -> b2 (1)
# \-> b3 (1) -> b4 (2)
- block(4, spend=out[2])
- yield accepted()
-
+ self.log.info("Reorg to a longer chain")
+ b4 = self.next_block(4, spend=out[2])
+ self.sync_blocks([b4])
# ... and back to the first chain.
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b3 (1) -> b4 (2)
- tip(2)
- block(5, spend=out[2])
- save_spendable_output()
- yield rejected()
+ self.move_tip(2)
+ b5 = self.next_block(5, spend=out[2])
+ self.save_spendable_output()
+ self.sync_blocks([b5], False)
- block(6, spend=out[3])
- yield accepted()
+ self.log.info("Reorg back to the original chain")
+ b6 = self.next_block(6, spend=out[3])
+ self.sync_blocks([b6], True)
# Try to create a fork that double-spends
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b7 (2) -> b8 (4)
# \-> b3 (1) -> b4 (2)
- tip(5)
- block(7, spend=out[2])
- yield rejected()
+ self.log.info("Reject a chain with a double spend, even if it is longer")
+ self.move_tip(5)
+ b7 = self.next_block(7, spend=out[2])
+ self.sync_blocks([b7], False)
- block(8, spend=out[4])
- yield rejected()
+ b8 = self.next_block(8, spend=out[4])
+ self.sync_blocks([b8], False, reconnect=True)
# Try to create a block that has too much fee
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b9 (4)
# \-> b3 (1) -> b4 (2)
- tip(6)
- block(9, spend=out[4], additional_coinbase_value=1)
- yield rejected(RejectResult(16, b'bad-cb-amount'))
+ self.log.info("Reject a block where the miner creates too much coinbase reward")
+ self.move_tip(6)
+ b9 = self.next_block(9, spend=out[4], additional_coinbase_value=1)
+ self.sync_blocks([b9], False, 16, b'bad-cb-amount', reconnect=True)
# Create a fork that ends in a block with too much fee (the one that causes the reorg)
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b10 (3) -> b11 (4)
# \-> b3 (1) -> b4 (2)
- tip(5)
- block(10, spend=out[3])
- yield rejected()
-
- block(11, spend=out[4], additional_coinbase_value=1)
- yield rejected(RejectResult(16, b'bad-cb-amount'))
+ self.log.info("Reject a chain where the miner creates too much coinbase reward, even if the chain is longer")
+ self.move_tip(5)
+ b10 = self.next_block(10, spend=out[3])
+ self.sync_blocks([b10], False)
+ b11 = self.next_block(11, spend=out[4], additional_coinbase_value=1)
+ self.sync_blocks([b11], False, 16, b'bad-cb-amount', reconnect=True)
# Try again, but with a valid fork first
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b12 (3) -> b13 (4) -> b14 (5)
- # (b12 added last)
# \-> b3 (1) -> b4 (2)
- tip(5)
- b12 = block(12, spend=out[3])
- save_spendable_output()
- b13 = block(13, spend=out[4])
- # Deliver the block header for b12, and the block b13.
- # b13 should be accepted but the tip won't advance until b12 is delivered.
- yield TestInstance([[CBlockHeader(b12), None], [b13, False]])
-
- save_spendable_output()
- # b14 is invalid, but the node won't know that until it tries to connect
- # Tip still can't advance because b12 is missing
- block(14, spend=out[5], additional_coinbase_value=1)
- yield rejected()
-
- yield TestInstance([[b12, True, b13.sha256]]) # New tip should be b13.
+ self.log.info("Reject a chain where the miner creates too much coinbase reward, even if the chain is longer (on a forked chain)")
+ self.move_tip(5)
+ b12 = self.next_block(12, spend=out[3])
+ self.save_spendable_output()
+ b13 = self.next_block(13, spend=out[4])
+ self.save_spendable_output()
+ b14 = self.next_block(14, spend=out[5], additional_coinbase_value=1)
+ self.sync_blocks([b12, b13, b14], False, 16, b'bad-cb-amount', reconnect=True)
+
+ # New tip should be b13.
+ assert_equal(node.getbestblockhash(), b13.hash)
# Add a block with MAX_BLOCK_SIGOPS and one with one more sigop
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b12 (3) -> b13 (4) -> b15 (5) -> b16 (6)
# \-> b3 (1) -> b4 (2)
-
- # Test that a block with a lot of checksigs is okay
+ self.log.info("Accept a block with lots of checksigs")
lots_of_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS - 1))
- tip(13)
- block(15, spend=out[5], script=lots_of_checksigs)
- yield accepted()
- save_spendable_output()
+ self.move_tip(13)
+ b15 = self.next_block(15, spend=out[5], script=lots_of_checksigs)
+ self.save_spendable_output()
+ self.sync_blocks([b15], True)
-
- # Test that a block with too many checksigs is rejected
+ self.log.info("Reject a block with too many checksigs")
too_many_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS))
- block(16, spend=out[6], script=too_many_checksigs)
- yield rejected(RejectResult(16, b'bad-blk-sigops'))
-
+ b16 = self.next_block(16, spend=out[6], script=too_many_checksigs)
+ self.sync_blocks([b16], False, 16, b'bad-blk-sigops', reconnect=True)
# Attempt to spend a transaction created on a different fork
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b12 (3) -> b13 (4) -> b15 (5) -> b17 (b3.vtx[1])
# \-> b3 (1) -> b4 (2)
- tip(15)
- block(17, spend=txout_b3)
- yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent'))
+ self.log.info("Reject a block with a spend from a re-org'ed out tx")
+ self.move_tip(15)
+ b17 = self.next_block(17, spend=txout_b3)
+ self.sync_blocks([b17], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True)
# Attempt to spend a transaction created on a different fork (on a fork this time)
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b12 (3) -> b13 (4) -> b15 (5)
# \-> b18 (b3.vtx[1]) -> b19 (6)
# \-> b3 (1) -> b4 (2)
- tip(13)
- block(18, spend=txout_b3)
- yield rejected()
+ self.log.info("Reject a block with a spend from a re-org'ed out tx (on a forked chain)")
+ self.move_tip(13)
+ b18 = self.next_block(18, spend=txout_b3)
+ self.sync_blocks([b18], False)
- block(19, spend=out[6])
- yield rejected()
+ b19 = self.next_block(19, spend=out[6])
+ self.sync_blocks([b19], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True)
# Attempt to spend a coinbase at depth too low
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b12 (3) -> b13 (4) -> b15 (5) -> b20 (7)
# \-> b3 (1) -> b4 (2)
- tip(15)
- block(20, spend=out[7])
- yield rejected(RejectResult(16, b'bad-txns-premature-spend-of-coinbase'))
+ self.log.info("Reject a block spending an immature coinbase.")
+ self.move_tip(15)
+ b20 = self.next_block(20, spend=out[7])
+ self.sync_blocks([b20], False, 16, b'bad-txns-premature-spend-of-coinbase')
# Attempt to spend a coinbase at depth too low (on a fork this time)
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b12 (3) -> b13 (4) -> b15 (5)
# \-> b21 (6) -> b22 (5)
# \-> b3 (1) -> b4 (2)
- tip(13)
- block(21, spend=out[6])
- yield rejected()
+ self.log.info("Reject a block spending an immature coinbase (on a forked chain)")
+ self.move_tip(13)
+ b21 = self.next_block(21, spend=out[6])
+ self.sync_blocks([b21], False)
- block(22, spend=out[5])
- yield rejected()
+ b22 = self.next_block(22, spend=out[5])
+ self.sync_blocks([b22], False, 16, b'bad-txns-premature-spend-of-coinbase')
# Create a block on either side of MAX_BLOCK_BASE_SIZE and make sure its accepted/rejected
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6)
# \-> b24 (6) -> b25 (7)
# \-> b3 (1) -> b4 (2)
- tip(15)
- b23 = block(23, spend=out[6])
+ self.log.info("Accept a block of size MAX_BLOCK_BASE_SIZE")
+ self.move_tip(15)
+ b23 = self.next_block(23, spend=out[6])
tx = CTransaction()
script_length = MAX_BLOCK_BASE_SIZE - len(b23.serialize()) - 69
script_output = CScript([b'\x00' * script_length])
tx.vout.append(CTxOut(0, script_output))
tx.vin.append(CTxIn(COutPoint(b23.vtx[1].sha256, 0)))
- b23 = update_block(23, [tx])
+ b23 = self.update_block(23, [tx])
# Make sure the math above worked out to produce a max-sized block
assert_equal(len(b23.serialize()), MAX_BLOCK_BASE_SIZE)
- yield accepted()
- save_spendable_output()
+ self.sync_blocks([b23], True)
+ self.save_spendable_output()
- # Make the next block one byte bigger and check that it fails
- tip(15)
- b24 = block(24, spend=out[6])
+ self.log.info("Reject a block of size MAX_BLOCK_BASE_SIZE + 1")
+ self.move_tip(15)
+ b24 = self.next_block(24, spend=out[6])
script_length = MAX_BLOCK_BASE_SIZE - len(b24.serialize()) - 69
- script_output = CScript([b'\x00' * (script_length+1)])
+ script_output = CScript([b'\x00' * (script_length + 1)])
tx.vout = [CTxOut(0, script_output)]
- b24 = update_block(24, [tx])
- assert_equal(len(b24.serialize()), MAX_BLOCK_BASE_SIZE+1)
- yield rejected(RejectResult(16, b'bad-blk-length'))
+ b24 = self.update_block(24, [tx])
+ assert_equal(len(b24.serialize()), MAX_BLOCK_BASE_SIZE + 1)
+ self.sync_blocks([b24], False, 16, b'bad-blk-length', reconnect=True)
- block(25, spend=out[7])
- yield rejected()
+ b25 = self.next_block(25, spend=out[7])
+ self.sync_blocks([b25], False)
# Create blocks with a coinbase input script size out of range
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6) -> b30 (7)
# \-> ... (6) -> ... (7)
# \-> b3 (1) -> b4 (2)
- tip(15)
- b26 = block(26, spend=out[6])
+ self.log.info("Reject a block with coinbase input script size out of range")
+ self.move_tip(15)
+ b26 = self.next_block(26, spend=out[6])
b26.vtx[0].vin[0].scriptSig = b'\x00'
b26.vtx[0].rehash()
# update_block causes the merkle root to get updated, even with no new
# transactions, and updates the required state.
- b26 = update_block(26, [])
- yield rejected(RejectResult(16, b'bad-cb-length'))
+ b26 = self.update_block(26, [])
+ self.sync_blocks([b26], False, 16, b'bad-cb-length', reconnect=True)
# Extend the b26 chain to make sure bitcoind isn't accepting b26
- block(27, spend=out[7])
- yield rejected(False)
+ b27 = self.next_block(27, spend=out[7])
+ self.sync_blocks([b27], False)
# Now try a too-large-coinbase script
- tip(15)
- b28 = block(28, spend=out[6])
+ self.move_tip(15)
+ b28 = self.next_block(28, spend=out[6])
b28.vtx[0].vin[0].scriptSig = b'\x00' * 101
b28.vtx[0].rehash()
- b28 = update_block(28, [])
- yield rejected(RejectResult(16, b'bad-cb-length'))
+ b28 = self.update_block(28, [])
+ self.sync_blocks([b28], False, 16, b'bad-cb-length', reconnect=True)
# Extend the b28 chain to make sure bitcoind isn't accepting b28
- block(29, spend=out[7])
- yield rejected(False)
+ b29 = self.next_block(29, spend=out[7])
+ self.sync_blocks([b29], False)
# b30 has a max-sized coinbase scriptSig.
- tip(23)
- b30 = block(30)
+ self.move_tip(23)
+ b30 = self.next_block(30)
b30.vtx[0].vin[0].scriptSig = b'\x00' * 100
b30.vtx[0].rehash()
- b30 = update_block(30, [])
- yield accepted()
- save_spendable_output()
+ b30 = self.update_block(30, [])
+ self.sync_blocks([b30], True)
+ self.save_spendable_output()
# b31 - b35 - check sigops of OP_CHECKMULTISIG / OP_CHECKMULTISIGVERIFY / OP_CHECKSIGVERIFY
#
@@ -433,42 +347,45 @@ class FullBlockTest(ComparisonTestFramework):
#
# MULTISIG: each op code counts as 20 sigops. To create the edge case, pack another 19 sigops at the end.
- lots_of_multisigs = CScript([OP_CHECKMULTISIG] * ((MAX_BLOCK_SIGOPS-1) // 20) + [OP_CHECKSIG] * 19)
- b31 = block(31, spend=out[8], script=lots_of_multisigs)
+ self.log.info("Accept a block with the max number of OP_CHECKMULTISIG sigops")
+ lots_of_multisigs = CScript([OP_CHECKMULTISIG] * ((MAX_BLOCK_SIGOPS - 1) // 20) + [OP_CHECKSIG] * 19)
+ b31 = self.next_block(31, spend=out[8], script=lots_of_multisigs)
assert_equal(get_legacy_sigopcount_block(b31), MAX_BLOCK_SIGOPS)
- yield accepted()
- save_spendable_output()
+ self.sync_blocks([b31], True)
+ self.save_spendable_output()
# this goes over the limit because the coinbase has one sigop
+ self.log.info("Reject a block with too many OP_CHECKMULTISIG sigops")
too_many_multisigs = CScript([OP_CHECKMULTISIG] * (MAX_BLOCK_SIGOPS // 20))
- b32 = block(32, spend=out[9], script=too_many_multisigs)
+ b32 = self.next_block(32, spend=out[9], script=too_many_multisigs)
assert_equal(get_legacy_sigopcount_block(b32), MAX_BLOCK_SIGOPS + 1)
- yield rejected(RejectResult(16, b'bad-blk-sigops'))
-
+ self.sync_blocks([b32], False, 16, b'bad-blk-sigops', reconnect=True)
# CHECKMULTISIGVERIFY
- tip(31)
- lots_of_multisigs = CScript([OP_CHECKMULTISIGVERIFY] * ((MAX_BLOCK_SIGOPS-1) // 20) + [OP_CHECKSIG] * 19)
- block(33, spend=out[9], script=lots_of_multisigs)
- yield accepted()
- save_spendable_output()
-
+ self.log.info("Accept a block with the max number of OP_CHECKMULTISIGVERIFY sigops")
+ self.move_tip(31)
+ lots_of_multisigs = CScript([OP_CHECKMULTISIGVERIFY] * ((MAX_BLOCK_SIGOPS - 1) // 20) + [OP_CHECKSIG] * 19)
+ b33 = self.next_block(33, spend=out[9], script=lots_of_multisigs)
+ self.sync_blocks([b33], True)
+ self.save_spendable_output()
+
+ self.log.info("Reject a block with too many OP_CHECKMULTISIGVERIFY sigops")
too_many_multisigs = CScript([OP_CHECKMULTISIGVERIFY] * (MAX_BLOCK_SIGOPS // 20))
- block(34, spend=out[10], script=too_many_multisigs)
- yield rejected(RejectResult(16, b'bad-blk-sigops'))
-
+ b34 = self.next_block(34, spend=out[10], script=too_many_multisigs)
+ self.sync_blocks([b34], False, 16, b'bad-blk-sigops', reconnect=True)
# CHECKSIGVERIFY
- tip(33)
+ self.log.info("Accept a block with the max number of OP_CHECKSIGVERIFY sigops")
+ self.move_tip(33)
lots_of_checksigs = CScript([OP_CHECKSIGVERIFY] * (MAX_BLOCK_SIGOPS - 1))
- b35 = block(35, spend=out[10], script=lots_of_checksigs)
- yield accepted()
- save_spendable_output()
+ b35 = self.next_block(35, spend=out[10], script=lots_of_checksigs)
+ self.sync_blocks([b35], True)
+ self.save_spendable_output()
+ self.log.info("Reject a block with too many OP_CHECKSIGVERIFY sigops")
too_many_checksigs = CScript([OP_CHECKSIGVERIFY] * (MAX_BLOCK_SIGOPS))
- block(36, spend=out[11], script=too_many_checksigs)
- yield rejected(RejectResult(16, b'bad-blk-sigops'))
-
+ b36 = self.next_block(36, spend=out[11], script=too_many_checksigs)
+ self.sync_blocks([b36], False, 16, b'bad-blk-sigops', reconnect=True)
# Check spending of a transaction in a block which failed to connect
#
@@ -479,17 +396,18 @@ class FullBlockTest(ComparisonTestFramework):
#
# save 37's spendable output, but then double-spend out11 to invalidate the block
- tip(35)
- b37 = block(37, spend=out[11])
+ self.log.info("Reject a block spending transaction from a block which failed to connect")
+ self.move_tip(35)
+ b37 = self.next_block(37, spend=out[11])
txout_b37 = PreviousSpendableOutput(b37.vtx[1], 0)
- tx = create_and_sign_tx(out[11].tx, out[11].n, 0)
- b37 = update_block(37, [tx])
- yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent'))
+ tx = self.create_and_sign_transaction(out[11].tx, out[11].n, 0)
+ b37 = self.update_block(37, [tx])
+ self.sync_blocks([b37], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True)
# attempt to spend b37's first non-coinbase tx, at which point b37 was still considered valid
- tip(35)
- block(38, spend=txout_b37)
- yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent'))
+ self.move_tip(35)
+ b38 = self.next_block(38, spend=txout_b37)
+ self.sync_blocks([b38], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True)
# Check P2SH SigOp counting
#
@@ -502,45 +420,45 @@ class FullBlockTest(ComparisonTestFramework):
# redeem_script = COINBASE_PUBKEY, (OP_2DUP+OP_CHECKSIGVERIFY) * 5, OP_CHECKSIG
# p2sh_script = OP_HASH160, ripemd160(sha256(script)), OP_EQUAL
#
- tip(35)
- b39 = block(39)
+ self.log.info("Check P2SH SIGOPS are correctly counted")
+ self.move_tip(35)
+ b39 = self.next_block(39)
b39_outputs = 0
b39_sigops_per_output = 6
# Build the redeem script, hash it, use hash to create the p2sh script
- redeem_script = CScript([self.coinbase_pubkey] + [OP_2DUP, OP_CHECKSIGVERIFY]*5 + [OP_CHECKSIG])
+ redeem_script = CScript([self.coinbase_pubkey] + [OP_2DUP, OP_CHECKSIGVERIFY] * 5 + [OP_CHECKSIG])
redeem_script_hash = hash160(redeem_script)
p2sh_script = CScript([OP_HASH160, redeem_script_hash, OP_EQUAL])
# Create a transaction that spends one satoshi to the p2sh_script, the rest to OP_TRUE
# This must be signed because it is spending a coinbase
spend = out[11]
- tx = create_tx(spend.tx, spend.n, 1, p2sh_script)
+ tx = self.create_tx(spend.tx, spend.n, 1, p2sh_script)
tx.vout.append(CTxOut(spend.tx.vout[spend.n].nValue - 1, CScript([OP_TRUE])))
self.sign_tx(tx, spend.tx, spend.n)
tx.rehash()
- b39 = update_block(39, [tx])
+ b39 = self.update_block(39, [tx])
b39_outputs += 1
# Until block is full, add tx's with 1 satoshi to p2sh_script, the rest to OP_TRUE
tx_new = None
tx_last = tx
- total_size=len(b39.serialize())
+ total_size = len(b39.serialize())
while(total_size < MAX_BLOCK_BASE_SIZE):
- tx_new = create_tx(tx_last, 1, 1, p2sh_script)
+ tx_new = self.create_tx(tx_last, 1, 1, p2sh_script)
tx_new.vout.append(CTxOut(tx_last.vout[1].nValue - 1, CScript([OP_TRUE])))
tx_new.rehash()
total_size += len(tx_new.serialize())
if total_size >= MAX_BLOCK_BASE_SIZE:
break
- b39.vtx.append(tx_new) # add tx to block
+ b39.vtx.append(tx_new) # add tx to block
tx_last = tx_new
b39_outputs += 1
- b39 = update_block(39, [])
- yield accepted()
- save_spendable_output()
-
+ b39 = self.update_block(39, [])
+ self.sync_blocks([b39], True)
+ self.save_spendable_output()
# Test sigops in P2SH redeem scripts
#
@@ -549,15 +467,16 @@ class FullBlockTest(ComparisonTestFramework):
#
# b41 does the same, less one, so it has the maximum sigops permitted.
#
- tip(39)
- b40 = block(40, spend=out[12])
+ self.log.info("Reject a block with too many P2SH sigops")
+ self.move_tip(39)
+ b40 = self.next_block(40, spend=out[12])
sigops = get_legacy_sigopcount_block(b40)
numTxes = (MAX_BLOCK_SIGOPS - sigops) // b39_sigops_per_output
assert_equal(numTxes <= b39_outputs, True)
lastOutpoint = COutPoint(b40.vtx[1].sha256, 0)
new_txs = []
- for i in range(1, numTxes+1):
+ for i in range(1, numTxes + 1):
tx = CTransaction()
tx.vout.append(CTxOut(1, CScript([OP_TRUE])))
tx.vin.append(CTxIn(lastOutpoint, b''))
@@ -579,35 +498,34 @@ class FullBlockTest(ComparisonTestFramework):
tx.vout.append(CTxOut(1, CScript([OP_CHECKSIG] * b40_sigops_to_fill)))
tx.rehash()
new_txs.append(tx)
- update_block(40, new_txs)
- yield rejected(RejectResult(16, b'bad-blk-sigops'))
+ self.update_block(40, new_txs)
+ self.sync_blocks([b40], False, 16, b'bad-blk-sigops', reconnect=True)
# same as b40, but one less sigop
- tip(39)
- block(41, spend=None)
- update_block(41, b40.vtx[1:-1])
+ self.log.info("Accept a block with the max number of P2SH sigops")
+ self.move_tip(39)
+ b41 = self.next_block(41, spend=None)
+ self.update_block(41, b40.vtx[1:-1])
b41_sigops_to_fill = b40_sigops_to_fill - 1
tx = CTransaction()
tx.vin.append(CTxIn(lastOutpoint, b''))
tx.vout.append(CTxOut(1, CScript([OP_CHECKSIG] * b41_sigops_to_fill)))
tx.rehash()
- update_block(41, [tx])
- yield accepted()
+ self.update_block(41, [tx])
+ self.sync_blocks([b41], True)
# Fork off of b39 to create a constant base again
#
# b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13)
# \-> b41 (12)
#
- tip(39)
- block(42, spend=out[12])
- yield rejected()
- save_spendable_output()
-
- block(43, spend=out[13])
- yield accepted()
- save_spendable_output()
+ self.move_tip(39)
+ b42 = self.next_block(42, spend=out[12])
+ self.save_spendable_output()
+ b43 = self.next_block(43, spend=out[13])
+ self.save_spendable_output()
+ self.sync_blocks([b42, b43], True)
# Test a number of really invalid scenarios
#
@@ -616,6 +534,7 @@ class FullBlockTest(ComparisonTestFramework):
# The next few blocks are going to be created "by hand" since they'll do funky things, such as having
# the first transaction be non-coinbase, etc. The purpose of b44 is to make sure this works.
+ self.log.info("Build block 44 manually")
height = self.block_heights[self.tip.sha256] + 1
coinbase = create_coinbase(height, self.coinbase_pubkey)
b44 = CBlock()
@@ -628,10 +547,10 @@ class FullBlockTest(ComparisonTestFramework):
self.tip = b44
self.block_heights[b44.sha256] = height
self.blocks[44] = b44
- yield accepted()
+ self.sync_blocks([b44], True)
- # A block with a non-coinbase as the first tx
- non_coinbase = create_tx(out[15].tx, out[15].n, 1)
+ self.log.info("Reject a block with a non-coinbase as the first tx")
+ non_coinbase = self.create_tx(out[15].tx, out[15].n, 1)
b45 = CBlock()
b45.nTime = self.tip.nTime + 1
b45.hashPrevBlock = self.tip.sha256
@@ -640,104 +559,102 @@ class FullBlockTest(ComparisonTestFramework):
b45.hashMerkleRoot = b45.calc_merkle_root()
b45.calc_sha256()
b45.solve()
- self.block_heights[b45.sha256] = self.block_heights[self.tip.sha256]+1
+ self.block_heights[b45.sha256] = self.block_heights[self.tip.sha256] + 1
self.tip = b45
self.blocks[45] = b45
- yield rejected(RejectResult(16, b'bad-cb-missing'))
+ self.sync_blocks([b45], False, 16, b'bad-cb-missing', reconnect=True)
- # A block with no txns
- tip(44)
+ self.log.info("Reject a block with no transactions")
+ self.move_tip(44)
b46 = CBlock()
- b46.nTime = b44.nTime+1
+ b46.nTime = b44.nTime + 1
b46.hashPrevBlock = b44.sha256
b46.nBits = 0x207fffff
b46.vtx = []
b46.hashMerkleRoot = 0
b46.solve()
- self.block_heights[b46.sha256] = self.block_heights[b44.sha256]+1
+ self.block_heights[b46.sha256] = self.block_heights[b44.sha256] + 1
self.tip = b46
assert 46 not in self.blocks
self.blocks[46] = b46
- s = ser_uint256(b46.hashMerkleRoot)
- yield rejected(RejectResult(16, b'bad-blk-length'))
+ self.sync_blocks([b46], False, 16, b'bad-blk-length', reconnect=True)
- # A block with invalid work
- tip(44)
- b47 = block(47, solve=False)
+ self.log.info("Reject a block with invalid work")
+ self.move_tip(44)
+ b47 = self.next_block(47, solve=False)
target = uint256_from_compact(b47.nBits)
- while b47.sha256 < target: #changed > to <
+ while b47.sha256 < target:
b47.nNonce += 1
b47.rehash()
- yield rejected(RejectResult(16, b'high-hash'))
+ self.sync_blocks([b47], False, request_block=False)
- # A block with timestamp > 2 hrs in the future
- tip(44)
- b48 = block(48, solve=False)
+ self.log.info("Reject a block with a timestamp >2 hours in the future")
+ self.move_tip(44)
+ b48 = self.next_block(48, solve=False)
b48.nTime = int(time.time()) + 60 * 60 * 3
b48.solve()
- yield rejected(RejectResult(16, b'time-too-new'))
+ self.sync_blocks([b48], False, request_block=False)
- # A block with an invalid merkle hash
- tip(44)
- b49 = block(49)
+ self.log.info("Reject a block with invalid merkle hash")
+ self.move_tip(44)
+ b49 = self.next_block(49)
b49.hashMerkleRoot += 1
b49.solve()
- yield rejected(RejectResult(16, b'bad-txnmrklroot'))
+ self.sync_blocks([b49], False, 16, b'bad-txnmrklroot', reconnect=True)
- # A block with an incorrect POW limit
- tip(44)
- b50 = block(50)
+ self.log.info("Reject a block with incorrect POW limit")
+ self.move_tip(44)
+ b50 = self.next_block(50)
b50.nBits = b50.nBits - 1
b50.solve()
- yield rejected(RejectResult(16, b'bad-diffbits'))
+ self.sync_blocks([b50], False, request_block=False, reconnect=True)
- # A block with two coinbase txns
- tip(44)
- b51 = block(51)
+ self.log.info("Reject a block with two coinbase transactions")
+ self.move_tip(44)
+ b51 = self.next_block(51)
cb2 = create_coinbase(51, self.coinbase_pubkey)
- b51 = update_block(51, [cb2])
- yield rejected(RejectResult(16, b'bad-cb-multiple'))
+ b51 = self.update_block(51, [cb2])
+ self.sync_blocks([b51], False, 16, b'bad-cb-multiple', reconnect=True)
- # A block w/ duplicate txns
+ self.log.info("Reject a block with duplicate transactions")
# Note: txns have to be in the right position in the merkle tree to trigger this error
- tip(44)
- b52 = block(52, spend=out[15])
- tx = create_tx(b52.vtx[1], 0, 1)
- b52 = update_block(52, [tx, tx])
- yield rejected(RejectResult(16, b'bad-txns-duplicate'))
+ self.move_tip(44)
+ b52 = self.next_block(52, spend=out[15])
+ tx = self.create_tx(b52.vtx[1], 0, 1)
+ b52 = self.update_block(52, [tx, tx])
+ self.sync_blocks([b52], False, 16, b'bad-txns-duplicate', reconnect=True)
# Test block timestamps
# -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15)
# \-> b54 (15)
#
- tip(43)
- block(53, spend=out[14])
- yield rejected() # rejected since b44 is at same height
- save_spendable_output()
+ self.move_tip(43)
+ b53 = self.next_block(53, spend=out[14])
+ self.sync_blocks([b53], False)
+ self.save_spendable_output()
- # invalid timestamp (b35 is 5 blocks back, so its time is MedianTimePast)
- b54 = block(54, spend=out[15])
+ self.log.info("Reject a block with timestamp before MedianTimePast")
+ b54 = self.next_block(54, spend=out[15])
b54.nTime = b35.nTime - 1
b54.solve()
- yield rejected(RejectResult(16, b'time-too-old'))
+ self.sync_blocks([b54], False, request_block=False)
# valid timestamp
- tip(53)
- b55 = block(55, spend=out[15])
+ self.move_tip(53)
+ b55 = self.next_block(55, spend=out[15])
b55.nTime = b35.nTime
- update_block(55, [])
- yield accepted()
- save_spendable_output()
+ self.update_block(55, [])
+ self.sync_blocks([b55], True)
+ self.save_spendable_output()
-
- # Test CVE-2012-2459
+ # Test Merkle tree malleability
#
# -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57p2 (16)
# \-> b57 (16)
# \-> b56p2 (16)
# \-> b56 (16)
#
- # Merkle tree malleability (CVE-2012-2459): repeating sequences of transactions in a block without
+ # Merkle tree malleability (CVE-2012-2459): repeating sequences of transactions in a block without
# affecting the merkle root of a block, while still invalidating it.
# See: src/consensus/merkle.h
#
@@ -758,46 +675,48 @@ class FullBlockTest(ComparisonTestFramework):
# that the error was caught early, avoiding a DOS vulnerability.)
# b57 - a good block with 2 txs, don't submit until end
- tip(55)
- b57 = block(57)
- tx = create_and_sign_tx(out[16].tx, out[16].n, 1)
- tx1 = create_tx(tx, 0, 1)
- b57 = update_block(57, [tx, tx1])
+ self.move_tip(55)
+ b57 = self.next_block(57)
+ tx = self.create_and_sign_transaction(out[16].tx, out[16].n, 1)
+ tx1 = self.create_tx(tx, 0, 1)
+ b57 = self.update_block(57, [tx, tx1])
# b56 - copy b57, add a duplicate tx
- tip(55)
+ self.log.info("Reject a block with a duplicate transaction in the Merkle Tree (but with a valid Merkle Root)")
+ self.move_tip(55)
b56 = copy.deepcopy(b57)
self.blocks[56] = b56
- assert_equal(len(b56.vtx),3)
- b56 = update_block(56, [tx1])
+ assert_equal(len(b56.vtx), 3)
+ b56 = self.update_block(56, [tx1])
assert_equal(b56.hash, b57.hash)
- yield rejected(RejectResult(16, b'bad-txns-duplicate'))
+ self.sync_blocks([b56], False, 16, b'bad-txns-duplicate', reconnect=True)
# b57p2 - a good block with 6 tx'es, don't submit until end
- tip(55)
- b57p2 = block("57p2")
- tx = create_and_sign_tx(out[16].tx, out[16].n, 1)
- tx1 = create_tx(tx, 0, 1)
- tx2 = create_tx(tx1, 0, 1)
- tx3 = create_tx(tx2, 0, 1)
- tx4 = create_tx(tx3, 0, 1)
- b57p2 = update_block("57p2", [tx, tx1, tx2, tx3, tx4])
+ self.move_tip(55)
+ b57p2 = self.next_block("57p2")
+ tx = self.create_and_sign_transaction(out[16].tx, out[16].n, 1)
+ tx1 = self.create_tx(tx, 0, 1)
+ tx2 = self.create_tx(tx1, 0, 1)
+ tx3 = self.create_tx(tx2, 0, 1)
+ tx4 = self.create_tx(tx3, 0, 1)
+ b57p2 = self.update_block("57p2", [tx, tx1, tx2, tx3, tx4])
# b56p2 - copy b57p2, duplicate two non-consecutive tx's
- tip(55)
+ self.log.info("Reject a block with two duplicate transactions in the Merkle Tree (but with a valid Merkle Root)")
+ self.move_tip(55)
b56p2 = copy.deepcopy(b57p2)
self.blocks["b56p2"] = b56p2
assert_equal(b56p2.hash, b57p2.hash)
- assert_equal(len(b56p2.vtx),6)
- b56p2 = update_block("b56p2", [tx3, tx4])
- yield rejected(RejectResult(16, b'bad-txns-duplicate'))
+ assert_equal(len(b56p2.vtx), 6)
+ b56p2 = self.update_block("b56p2", [tx3, tx4])
+ self.sync_blocks([b56p2], False, 16, b'bad-txns-duplicate', reconnect=True)
- tip("57p2")
- yield accepted()
+ self.move_tip("57p2")
+ self.sync_blocks([b57p2], True)
- tip(57)
- yield rejected() #rejected because 57p2 seen first
- save_spendable_output()
+ self.move_tip(57)
+ self.sync_blocks([b57], False) # The tip is not updated because 57p2 seen first
+ self.save_spendable_output()
# Test a few invalid tx types
#
@@ -806,28 +725,30 @@ class FullBlockTest(ComparisonTestFramework):
#
# tx with prevout.n out of range
- tip(57)
- b58 = block(58, spend=out[17])
+ self.log.info("Reject a block with a transaction with prevout.n out of range")
+ self.move_tip(57)
+ b58 = self.next_block(58, spend=out[17])
tx = CTransaction()
assert(len(out[17].tx.vout) < 42)
tx.vin.append(CTxIn(COutPoint(out[17].tx.sha256, 42), CScript([OP_TRUE]), 0xffffffff))
tx.vout.append(CTxOut(0, b""))
tx.calc_sha256()
- b58 = update_block(58, [tx])
- yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent'))
+ b58 = self.update_block(58, [tx])
+ self.sync_blocks([b58], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True)
- # tx with output value > input value out of range
- tip(57)
- b59 = block(59)
- tx = create_and_sign_tx(out[17].tx, out[17].n, 51*COIN)
- b59 = update_block(59, [tx])
- yield rejected(RejectResult(16, b'bad-txns-in-belowout'))
+ # tx with output value > input value
+ self.log.info("Reject a block with a transaction with outputs > inputs")
+ self.move_tip(57)
+ b59 = self.next_block(59)
+ tx = self.create_and_sign_transaction(out[17].tx, out[17].n, 51 * COIN)
+ b59 = self.update_block(59, [tx])
+ self.sync_blocks([b59], False, 16, b'bad-txns-in-belowout', reconnect=True)
# reset to good chain
- tip(57)
- b60 = block(60, spend=out[17])
- yield accepted()
- save_spendable_output()
+ self.move_tip(57)
+ b60 = self.next_block(60, spend=out[17])
+ self.sync_blocks([b60], True)
+ self.save_spendable_output()
# Test BIP30
#
@@ -838,46 +759,46 @@ class FullBlockTest(ComparisonTestFramework):
# not-fully-spent transaction in the same chain. To test, make identical coinbases;
# the second one should be rejected.
#
- tip(60)
- b61 = block(61, spend=out[18])
- b61.vtx[0].vin[0].scriptSig = b60.vtx[0].vin[0].scriptSig #equalize the coinbases
+ self.log.info("Reject a block with a transaction with a duplicate hash of a previous transaction (BIP30)")
+ self.move_tip(60)
+ b61 = self.next_block(61, spend=out[18])
+ b61.vtx[0].vin[0].scriptSig = b60.vtx[0].vin[0].scriptSig # Equalize the coinbases
b61.vtx[0].rehash()
- b61 = update_block(61, [])
+ b61 = self.update_block(61, [])
assert_equal(b60.vtx[0].serialize(), b61.vtx[0].serialize())
- yield rejected(RejectResult(16, b'bad-txns-BIP30'))
-
+ self.sync_blocks([b61], False, 16, b'bad-txns-BIP30', reconnect=True)
# Test tx.isFinal is properly rejected (not an exhaustive tx.isFinal test, that should be in data-driven transaction tests)
#
# -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17)
# \-> b62 (18)
#
- tip(60)
- b62 = block(62)
+ self.log.info("Reject a block with a transaction with a nonfinal locktime")
+ self.move_tip(60)
+ b62 = self.next_block(62)
tx = CTransaction()
- tx.nLockTime = 0xffffffff #this locktime is non-final
+ tx.nLockTime = 0xffffffff # this locktime is non-final
assert(out[18].n < len(out[18].tx.vout))
- tx.vin.append(CTxIn(COutPoint(out[18].tx.sha256, out[18].n))) # don't set nSequence
+ tx.vin.append(CTxIn(COutPoint(out[18].tx.sha256, out[18].n))) # don't set nSequence
tx.vout.append(CTxOut(0, CScript([OP_TRUE])))
assert(tx.vin[0].nSequence < 0xffffffff)
tx.calc_sha256()
- b62 = update_block(62, [tx])
- yield rejected(RejectResult(16, b'bad-txns-nonfinal'))
-
+ b62 = self.update_block(62, [tx])
+ self.sync_blocks([b62], False, 16, b'bad-txns-nonfinal')
# Test a non-final coinbase is also rejected
#
# -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17)
# \-> b63 (-)
#
- tip(60)
- b63 = block(63)
+ self.log.info("Reject a block with a coinbase transaction with a nonfinal locktime")
+ self.move_tip(60)
+ b63 = self.next_block(63)
b63.vtx[0].nLockTime = 0xffffffff
b63.vtx[0].vin[0].nSequence = 0xDEADBEEF
b63.vtx[0].rehash()
- b63 = update_block(63, [])
- yield rejected(RejectResult(16, b'bad-txns-nonfinal'))
-
+ b63 = self.update_block(63, [])
+ self.sync_blocks([b63], False, 16, b'bad-txns-nonfinal')
# This checks that a block with a bloated VARINT between the block_header and the array of tx such that
# the block is > MAX_BLOCK_BASE_SIZE with the bloated varint, but <= MAX_BLOCK_BASE_SIZE without the bloated varint,
@@ -893,8 +814,9 @@ class FullBlockTest(ComparisonTestFramework):
# b64a is a bloated block (non-canonical varint)
# b64 is a good block (same as b64 but w/ canonical varint)
#
- tip(60)
- regular_block = block("64a", spend=out[18])
+ self.log.info("Accept a valid block even if a bloated version of the block has previously been sent")
+ self.move_tip(60)
+ regular_block = self.next_block("64a", spend=out[18])
# make it a "broken_block," with non-canonical serialization
b64a = CBrokenBlock(regular_block)
@@ -908,45 +830,51 @@ class FullBlockTest(ComparisonTestFramework):
script_output = CScript([b'\x00' * script_length])
tx.vout.append(CTxOut(0, script_output))
tx.vin.append(CTxIn(COutPoint(b64a.vtx[1].sha256, 0)))
- b64a = update_block("64a", [tx])
+ b64a = self.update_block("64a", [tx])
assert_equal(len(b64a.serialize()), MAX_BLOCK_BASE_SIZE + 8)
- yield TestInstance([[self.tip, None]])
+ self.sync_blocks([b64a], False, 1, b'error parsing message')
- # comptool workaround: to make sure b64 is delivered, manually erase b64a from blockstore
- self.test.block_store.erase(b64a.sha256)
+ # bitcoind doesn't disconnect us for sending a bloated block, but if we subsequently
+ # resend the header message, it won't send us the getdata message again. Just
+ # disconnect and reconnect and then call sync_blocks.
+ # TODO: improve this test to be less dependent on P2P DOS behaviour.
+ node.disconnect_p2ps()
+ self.reconnect_p2p()
- tip(60)
+ self.move_tip(60)
b64 = CBlock(b64a)
b64.vtx = copy.deepcopy(b64a.vtx)
assert_equal(b64.hash, b64a.hash)
assert_equal(len(b64.serialize()), MAX_BLOCK_BASE_SIZE)
self.blocks[64] = b64
- update_block(64, [])
- yield accepted()
- save_spendable_output()
+ b64 = self.update_block(64, [])
+ self.sync_blocks([b64], True)
+ self.save_spendable_output()
# Spend an output created in the block itself
#
# -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19)
#
- tip(64)
- block(65)
- tx1 = create_and_sign_tx(out[19].tx, out[19].n, out[19].tx.vout[0].nValue)
- tx2 = create_and_sign_tx(tx1, 0, 0)
- update_block(65, [tx1, tx2])
- yield accepted()
- save_spendable_output()
+ self.log.info("Accept a block with a transaction spending an output created in the same block")
+ self.move_tip(64)
+ b65 = self.next_block(65)
+ tx1 = self.create_and_sign_transaction(out[19].tx, out[19].n, out[19].tx.vout[0].nValue)
+ tx2 = self.create_and_sign_transaction(tx1, 0, 0)
+ b65 = self.update_block(65, [tx1, tx2])
+ self.sync_blocks([b65], True)
+ self.save_spendable_output()
# Attempt to spend an output created later in the same block
#
# -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19)
# \-> b66 (20)
- tip(65)
- block(66)
- tx1 = create_and_sign_tx(out[20].tx, out[20].n, out[20].tx.vout[0].nValue)
- tx2 = create_and_sign_tx(tx1, 0, 1)
- update_block(66, [tx2, tx1])
- yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent'))
+ self.log.info("Reject a block with a transaction spending an output created later in the same block")
+ self.move_tip(65)
+ b66 = self.next_block(66)
+ tx1 = self.create_and_sign_transaction(out[20].tx, out[20].n, out[20].tx.vout[0].nValue)
+ tx2 = self.create_and_sign_transaction(tx1, 0, 1)
+ b66 = self.update_block(66, [tx2, tx1])
+ self.sync_blocks([b66], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True)
# Attempt to double-spend a transaction created in a block
#
@@ -954,13 +882,14 @@ class FullBlockTest(ComparisonTestFramework):
# \-> b67 (20)
#
#
- tip(65)
- block(67)
- tx1 = create_and_sign_tx(out[20].tx, out[20].n, out[20].tx.vout[0].nValue)
- tx2 = create_and_sign_tx(tx1, 0, 1)
- tx3 = create_and_sign_tx(tx1, 0, 2)
- update_block(67, [tx1, tx2, tx3])
- yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent'))
+ self.log.info("Reject a block with a transaction double spending a transaction creted in the same block")
+ self.move_tip(65)
+ b67 = self.next_block(67)
+ tx1 = self.create_and_sign_transaction(out[20].tx, out[20].n, out[20].tx.vout[0].nValue)
+ tx2 = self.create_and_sign_transaction(tx1, 0, 1)
+ tx3 = self.create_and_sign_transaction(tx1, 0, 2)
+ b67 = self.update_block(67, [tx1, tx2, tx3])
+ self.sync_blocks([b67], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True)
# More tests of block subsidy
#
@@ -974,34 +903,36 @@ class FullBlockTest(ComparisonTestFramework):
# b69 - coinbase with extra 10 satoshis, and a tx that gives a 10 satoshi fee
# this succeeds
#
- tip(65)
- block(68, additional_coinbase_value=10)
- tx = create_and_sign_tx(out[20].tx, out[20].n, out[20].tx.vout[0].nValue-9)
- update_block(68, [tx])
- yield rejected(RejectResult(16, b'bad-cb-amount'))
-
- tip(65)
- b69 = block(69, additional_coinbase_value=10)
- tx = create_and_sign_tx(out[20].tx, out[20].n, out[20].tx.vout[0].nValue-10)
- update_block(69, [tx])
- yield accepted()
- save_spendable_output()
+ self.log.info("Reject a block trying to claim too much subsidy in the coinbase transaction")
+ self.move_tip(65)
+ b68 = self.next_block(68, additional_coinbase_value=10)
+ tx = self.create_and_sign_transaction(out[20].tx, out[20].n, out[20].tx.vout[0].nValue - 9)
+ b68 = self.update_block(68, [tx])
+ self.sync_blocks([b68], False, 16, b'bad-cb-amount', reconnect=True)
+
+ self.log.info("Accept a block claiming the correct subsidy in the coinbase transaction")
+ self.move_tip(65)
+ b69 = self.next_block(69, additional_coinbase_value=10)
+ tx = self.create_and_sign_transaction(out[20].tx, out[20].n, out[20].tx.vout[0].nValue - 10)
+ self.update_block(69, [tx])
+ self.sync_blocks([b69], True)
+ self.save_spendable_output()
# Test spending the outpoint of a non-existent transaction
#
# -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20)
# \-> b70 (21)
#
- tip(69)
- block(70, spend=out[21])
+ self.log.info("Reject a block containing a transaction spending from a non-existent input")
+ self.move_tip(69)
+ b70 = self.next_block(70, spend=out[21])
bogus_tx = CTransaction()
bogus_tx.sha256 = uint256_from_str(b"23c70ed7c0506e9178fc1a987f40a33946d4ad4c962b5ae3a52546da53af0c5c")
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(bogus_tx.sha256, 0), b"", 0xffffffff))
tx.vout.append(CTxOut(1, b""))
- update_block(70, [tx])
- yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent'))
-
+ b70 = self.update_block(70, [tx])
+ self.sync_blocks([b70], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True)
# Test accepting an invalid block which has the same hash as a valid one (via merkle tree tricks)
#
@@ -1009,13 +940,13 @@ class FullBlockTest(ComparisonTestFramework):
# \-> b71 (21)
#
# b72 is a good block.
- # b71 is a copy of 72, but re-adds one of its transactions. However, it has the same hash as b71.
- #
- tip(69)
- b72 = block(72)
- tx1 = create_and_sign_tx(out[21].tx, out[21].n, 2)
- tx2 = create_and_sign_tx(tx1, 0, 1)
- b72 = update_block(72, [tx1, tx2]) # now tip is 72
+ # b71 is a copy of 72, but re-adds one of its transactions. However, it has the same hash as b72.
+ self.log.info("Reject a block containing a duplicate transaction but with the same Merkle root (Merkle tree malleability")
+ self.move_tip(69)
+ b72 = self.next_block(72)
+ tx1 = self.create_and_sign_transaction(out[21].tx, out[21].n, 2)
+ tx2 = self.create_and_sign_transaction(tx1, 0, 1)
+ b72 = self.update_block(72, [tx1, tx2]) # now tip is 72
b71 = copy.deepcopy(b72)
b71.vtx.append(tx2) # add duplicate tx2
self.block_heights[b71.sha256] = self.block_heights[b69.sha256] + 1 # b71 builds off b69
@@ -1025,12 +956,12 @@ class FullBlockTest(ComparisonTestFramework):
assert_equal(len(b72.vtx), 3)
assert_equal(b72.sha256, b71.sha256)
- tip(71)
- yield rejected(RejectResult(16, b'bad-txns-duplicate'))
- tip(72)
- yield accepted()
- save_spendable_output()
+ self.move_tip(71)
+ self.sync_blocks([b71], False, 16, b'bad-txns-duplicate', reconnect=True)
+ self.move_tip(72)
+ self.sync_blocks([b72], True)
+ self.save_spendable_output()
# Test some invalid scripts and MAX_BLOCK_SIGOPS
#
@@ -1048,23 +979,23 @@ class FullBlockTest(ComparisonTestFramework):
# bytearray[20,000-20,003]: 521 (max_script_element_size+1, in little-endian format)
# bytearray[20,004-20,525]: unread data (script_element)
# bytearray[20,526] : OP_CHECKSIG (this puts us over the limit)
- #
- tip(72)
- b73 = block(73)
+ self.log.info("Reject a block containing too many sigops after a large script element")
+ self.move_tip(72)
+ b73 = self.next_block(73)
size = MAX_BLOCK_SIGOPS - 1 + MAX_SCRIPT_ELEMENT_SIZE + 1 + 5 + 1
a = bytearray([OP_CHECKSIG] * size)
- a[MAX_BLOCK_SIGOPS - 1] = int("4e",16) # OP_PUSHDATA4
+ a[MAX_BLOCK_SIGOPS - 1] = int("4e", 16) # OP_PUSHDATA4
element_size = MAX_SCRIPT_ELEMENT_SIZE + 1
a[MAX_BLOCK_SIGOPS] = element_size % 256
- a[MAX_BLOCK_SIGOPS+1] = element_size // 256
- a[MAX_BLOCK_SIGOPS+2] = 0
- a[MAX_BLOCK_SIGOPS+3] = 0
+ a[MAX_BLOCK_SIGOPS + 1] = element_size // 256
+ a[MAX_BLOCK_SIGOPS + 2] = 0
+ a[MAX_BLOCK_SIGOPS + 3] = 0
- tx = create_and_sign_tx(out[22].tx, 0, 1, CScript(a))
- b73 = update_block(73, [tx])
- assert_equal(get_legacy_sigopcount_block(b73), MAX_BLOCK_SIGOPS+1)
- yield rejected(RejectResult(16, b'bad-blk-sigops'))
+ tx = self.create_and_sign_transaction(out[22].tx, 0, 1, CScript(a))
+ b73 = self.update_block(73, [tx])
+ assert_equal(get_legacy_sigopcount_block(b73), MAX_BLOCK_SIGOPS + 1)
+ self.sync_blocks([b73], False, 16, b'bad-blk-sigops', reconnect=True)
# b74/75 - if we push an invalid script element, all prevous sigops are counted,
# but sigops after the element are not counted.
@@ -1076,45 +1007,44 @@ class FullBlockTest(ComparisonTestFramework):
#
# b74 fails because we put MAX_BLOCK_SIGOPS+1 before the element
# b75 succeeds because we put MAX_BLOCK_SIGOPS before the element
- #
- #
- tip(72)
- b74 = block(74)
- size = MAX_BLOCK_SIGOPS - 1 + MAX_SCRIPT_ELEMENT_SIZE + 42 # total = 20,561
+ self.log.info("Check sigops are counted correctly after an invalid script element")
+ self.move_tip(72)
+ b74 = self.next_block(74)
+ size = MAX_BLOCK_SIGOPS - 1 + MAX_SCRIPT_ELEMENT_SIZE + 42 # total = 20,561
a = bytearray([OP_CHECKSIG] * size)
a[MAX_BLOCK_SIGOPS] = 0x4e
- a[MAX_BLOCK_SIGOPS+1] = 0xfe
- a[MAX_BLOCK_SIGOPS+2] = 0xff
- a[MAX_BLOCK_SIGOPS+3] = 0xff
- a[MAX_BLOCK_SIGOPS+4] = 0xff
- tx = create_and_sign_tx(out[22].tx, 0, 1, CScript(a))
- b74 = update_block(74, [tx])
- yield rejected(RejectResult(16, b'bad-blk-sigops'))
-
- tip(72)
- b75 = block(75)
+ a[MAX_BLOCK_SIGOPS + 1] = 0xfe
+ a[MAX_BLOCK_SIGOPS + 2] = 0xff
+ a[MAX_BLOCK_SIGOPS + 3] = 0xff
+ a[MAX_BLOCK_SIGOPS + 4] = 0xff
+ tx = self.create_and_sign_transaction(out[22].tx, 0, 1, CScript(a))
+ b74 = self.update_block(74, [tx])
+ self.sync_blocks([b74], False, 16, b'bad-blk-sigops', reconnect=True)
+
+ self.move_tip(72)
+ b75 = self.next_block(75)
size = MAX_BLOCK_SIGOPS - 1 + MAX_SCRIPT_ELEMENT_SIZE + 42
a = bytearray([OP_CHECKSIG] * size)
- a[MAX_BLOCK_SIGOPS-1] = 0x4e
+ a[MAX_BLOCK_SIGOPS - 1] = 0x4e
a[MAX_BLOCK_SIGOPS] = 0xff
- a[MAX_BLOCK_SIGOPS+1] = 0xff
- a[MAX_BLOCK_SIGOPS+2] = 0xff
- a[MAX_BLOCK_SIGOPS+3] = 0xff
- tx = create_and_sign_tx(out[22].tx, 0, 1, CScript(a))
- b75 = update_block(75, [tx])
- yield accepted()
- save_spendable_output()
+ a[MAX_BLOCK_SIGOPS + 1] = 0xff
+ a[MAX_BLOCK_SIGOPS + 2] = 0xff
+ a[MAX_BLOCK_SIGOPS + 3] = 0xff
+ tx = self.create_and_sign_transaction(out[22].tx, 0, 1, CScript(a))
+ b75 = self.update_block(75, [tx])
+ self.sync_blocks([b75], True)
+ self.save_spendable_output()
# Check that if we push an element filled with CHECKSIGs, they are not counted
- tip(75)
- b76 = block(76)
+ self.move_tip(75)
+ b76 = self.next_block(76)
size = MAX_BLOCK_SIGOPS - 1 + MAX_SCRIPT_ELEMENT_SIZE + 1 + 5
a = bytearray([OP_CHECKSIG] * size)
- a[MAX_BLOCK_SIGOPS-1] = 0x4e # PUSHDATA4, but leave the following bytes as just checksigs
- tx = create_and_sign_tx(out[23].tx, 0, 1, CScript(a))
- b76 = update_block(76, [tx])
- yield accepted()
- save_spendable_output()
+ a[MAX_BLOCK_SIGOPS - 1] = 0x4e # PUSHDATA4, but leave the following bytes as just checksigs
+ tx = self.create_and_sign_transaction(out[23].tx, 0, 1, CScript(a))
+ b76 = self.update_block(76, [tx])
+ self.sync_blocks([b76], True)
+ self.save_spendable_output()
# Test transaction resurrection
#
@@ -1133,39 +1063,39 @@ class FullBlockTest(ComparisonTestFramework):
# To get around this issue, we construct transactions which are not signed and which
# spend to OP_TRUE. If the standard-ness rules change, this test would need to be
# updated. (Perhaps to spend to a P2SH OP_TRUE script)
- #
- tip(76)
- block(77)
- tx77 = create_and_sign_tx(out[24].tx, out[24].n, 10*COIN)
- update_block(77, [tx77])
- yield accepted()
- save_spendable_output()
-
- block(78)
- tx78 = create_tx(tx77, 0, 9*COIN)
- update_block(78, [tx78])
- yield accepted()
-
- block(79)
- tx79 = create_tx(tx78, 0, 8*COIN)
- update_block(79, [tx79])
- yield accepted()
+ self.log.info("Test transaction resurrection during a re-org")
+ self.move_tip(76)
+ b77 = self.next_block(77)
+ tx77 = self.create_and_sign_transaction(out[24].tx, out[24].n, 10 * COIN)
+ b77 = self.update_block(77, [tx77])
+ self.sync_blocks([b77], True)
+ self.save_spendable_output()
+
+ b78 = self.next_block(78)
+ tx78 = self.create_tx(tx77, 0, 9 * COIN)
+ b78 = self.update_block(78, [tx78])
+ self.sync_blocks([b78], True)
+
+ b79 = self.next_block(79)
+ tx79 = self.create_tx(tx78, 0, 8 * COIN)
+ b79 = self.update_block(79, [tx79])
+ self.sync_blocks([b79], True)
# mempool should be empty
assert_equal(len(self.nodes[0].getrawmempool()), 0)
- tip(77)
- block(80, spend=out[25])
- yield rejected()
- save_spendable_output()
+ self.move_tip(77)
+ b80 = self.next_block(80, spend=out[25])
+ self.sync_blocks([b80], False, request_block=False)
+ self.save_spendable_output()
- block(81, spend=out[26])
- yield rejected() # other chain is same length
- save_spendable_output()
+ b81 = self.next_block(81, spend=out[26])
+ self.sync_blocks([b81], False, request_block=False) # other chain is same length
+ self.save_spendable_output()
- block(82, spend=out[27])
- yield accepted() # now this chain is longer, triggers re-org
- save_spendable_output()
+ b82 = self.next_block(82, spend=out[27])
+ self.sync_blocks([b82], True) # now this chain is longer, triggers re-org
+ self.save_spendable_output()
# now check that tx78 and tx79 have been put back into the peer's mempool
mempool = self.nodes[0].getrawmempool()
@@ -1173,33 +1103,32 @@ class FullBlockTest(ComparisonTestFramework):
assert(tx78.hash in mempool)
assert(tx79.hash in mempool)
-
# Test invalid opcodes in dead execution paths.
#
# -> b81 (26) -> b82 (27) -> b83 (28)
#
- block(83)
+ self.log.info("Accept a block with invalid opcodes in dead execution paths")
+ b83 = self.next_block(83)
op_codes = [OP_IF, OP_INVALIDOPCODE, OP_ELSE, OP_TRUE, OP_ENDIF]
script = CScript(op_codes)
- tx1 = create_and_sign_tx(out[28].tx, out[28].n, out[28].tx.vout[0].nValue, script)
+ tx1 = self.create_and_sign_transaction(out[28].tx, out[28].n, out[28].tx.vout[0].nValue, script)
- tx2 = create_and_sign_tx(tx1, 0, 0, CScript([OP_TRUE]))
+ tx2 = self.create_and_sign_transaction(tx1, 0, 0, CScript([OP_TRUE]))
tx2.vin[0].scriptSig = CScript([OP_FALSE])
tx2.rehash()
- update_block(83, [tx1, tx2])
- yield accepted()
- save_spendable_output()
-
+ b83 = self.update_block(83, [tx1, tx2])
+ self.sync_blocks([b83], True)
+ self.save_spendable_output()
# Reorg on/off blocks that have OP_RETURN in them (and try to spend them)
#
# -> b81 (26) -> b82 (27) -> b83 (28) -> b84 (29) -> b87 (30) -> b88 (31)
# \-> b85 (29) -> b86 (30) \-> b89a (32)
#
- #
- block(84)
- tx1 = create_tx(out[29].tx, out[29].n, 0, CScript([OP_RETURN]))
+ self.log.info("Test re-orging blocks with OP_RETURN in them")
+ b84 = self.next_block(84)
+ tx1 = self.create_tx(out[29].tx, out[29].n, 0, CScript([OP_RETURN]))
tx1.vout.append(CTxOut(0, CScript([OP_TRUE])))
tx1.vout.append(CTxOut(0, CScript([OP_TRUE])))
tx1.vout.append(CTxOut(0, CScript([OP_TRUE])))
@@ -1207,87 +1136,186 @@ class FullBlockTest(ComparisonTestFramework):
tx1.calc_sha256()
self.sign_tx(tx1, out[29].tx, out[29].n)
tx1.rehash()
- tx2 = create_tx(tx1, 1, 0, CScript([OP_RETURN]))
+ tx2 = self.create_tx(tx1, 1, 0, CScript([OP_RETURN]))
tx2.vout.append(CTxOut(0, CScript([OP_RETURN])))
- tx3 = create_tx(tx1, 2, 0, CScript([OP_RETURN]))
+ tx3 = self.create_tx(tx1, 2, 0, CScript([OP_RETURN]))
tx3.vout.append(CTxOut(0, CScript([OP_TRUE])))
- tx4 = create_tx(tx1, 3, 0, CScript([OP_TRUE]))
+ tx4 = self.create_tx(tx1, 3, 0, CScript([OP_TRUE]))
tx4.vout.append(CTxOut(0, CScript([OP_RETURN])))
- tx5 = create_tx(tx1, 4, 0, CScript([OP_RETURN]))
+ tx5 = self.create_tx(tx1, 4, 0, CScript([OP_RETURN]))
- update_block(84, [tx1,tx2,tx3,tx4,tx5])
- yield accepted()
- save_spendable_output()
+ b84 = self.update_block(84, [tx1, tx2, tx3, tx4, tx5])
+ self.sync_blocks([b84], True)
+ self.save_spendable_output()
- tip(83)
- block(85, spend=out[29])
- yield rejected()
+ self.move_tip(83)
+ b85 = self.next_block(85, spend=out[29])
+ self.sync_blocks([b85], False) # other chain is same length
- block(86, spend=out[30])
- yield accepted()
+ b86 = self.next_block(86, spend=out[30])
+ self.sync_blocks([b86], True)
- tip(84)
- block(87, spend=out[30])
- yield rejected()
- save_spendable_output()
+ self.move_tip(84)
+ b87 = self.next_block(87, spend=out[30])
+ self.sync_blocks([b87], False) # other chain is same length
+ self.save_spendable_output()
- block(88, spend=out[31])
- yield accepted()
- save_spendable_output()
+ b88 = self.next_block(88, spend=out[31])
+ self.sync_blocks([b88], True)
+ self.save_spendable_output()
# trying to spend the OP_RETURN output is rejected
- block("89a", spend=out[32])
- tx = create_tx(tx1, 0, 0, CScript([OP_TRUE]))
- update_block("89a", [tx])
- yield rejected()
-
-
- # Test re-org of a week's worth of blocks (1088 blocks)
- # This test takes a minute or two and can be accomplished in memory
- #
- if self.options.runbarelyexpensive:
- tip(88)
- LARGE_REORG_SIZE = 1088
- test1 = TestInstance(sync_every_block=False)
- spend=out[32]
- for i in range(89, LARGE_REORG_SIZE + 89):
- b = block(i, spend)
- tx = CTransaction()
- script_length = MAX_BLOCK_BASE_SIZE - len(b.serialize()) - 69
- script_output = CScript([b'\x00' * script_length])
- tx.vout.append(CTxOut(0, script_output))
- tx.vin.append(CTxIn(COutPoint(b.vtx[1].sha256, 0)))
- b = update_block(i, [tx])
- assert_equal(len(b.serialize()), MAX_BLOCK_BASE_SIZE)
- test1.blocks_and_transactions.append([self.tip, True])
- save_spendable_output()
- spend = get_spendable_output()
-
- yield test1
- chain1_tip = i
-
- # now create alt chain of same length
- tip(88)
- test2 = TestInstance(sync_every_block=False)
- for i in range(89, LARGE_REORG_SIZE + 89):
- block("alt"+str(i))
- test2.blocks_and_transactions.append([self.tip, False])
- yield test2
-
- # extend alt chain to trigger re-org
- block("alt" + str(chain1_tip + 1))
- yield accepted()
-
- # ... and re-org back to the first chain
- tip(chain1_tip)
- block(chain1_tip + 1)
- yield rejected()
- block(chain1_tip + 2)
- yield accepted()
-
- chain1_tip += 2
+ b89a = self.next_block("89a", spend=out[32])
+ tx = self.create_tx(tx1, 0, 0, CScript([OP_TRUE]))
+ b89a = self.update_block("89a", [tx])
+ self.sync_blocks([b89a], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True)
+
+ self.log.info("Test a re-org of one week's worth of blocks (1088 blocks)")
+
+ self.move_tip(88)
+ LARGE_REORG_SIZE = 1088
+ blocks = []
+ spend = out[32]
+ for i in range(89, LARGE_REORG_SIZE + 89):
+ b = self.next_block(i, spend)
+ tx = CTransaction()
+ script_length = MAX_BLOCK_BASE_SIZE - len(b.serialize()) - 69
+ script_output = CScript([b'\x00' * script_length])
+ tx.vout.append(CTxOut(0, script_output))
+ tx.vin.append(CTxIn(COutPoint(b.vtx[1].sha256, 0)))
+ b = self.update_block(i, [tx])
+ assert_equal(len(b.serialize()), MAX_BLOCK_BASE_SIZE)
+ blocks.append(b)
+ self.save_spendable_output()
+ spend = self.get_spendable_output()
+
+ self.sync_blocks(blocks, True, timeout=180)
+ chain1_tip = i
+
+ # now create alt chain of same length
+ self.move_tip(88)
+ blocks2 = []
+ for i in range(89, LARGE_REORG_SIZE + 89):
+ blocks2.append(self.next_block("alt" + str(i)))
+ self.sync_blocks(blocks2, False, request_block=False)
+
+ # extend alt chain to trigger re-org
+ block = self.next_block("alt" + str(chain1_tip + 1))
+ self.sync_blocks([block], True, timeout=180)
+
+ # ... and re-org back to the first chain
+ self.move_tip(chain1_tip)
+ block = self.next_block(chain1_tip + 1)
+ self.sync_blocks([block], False, request_block=False)
+ block = self.next_block(chain1_tip + 2)
+ self.sync_blocks([block], True, timeout=180)
+
+ # Helper methods
+ ################
+
+ def add_transactions_to_block(self, block, tx_list):
+ [tx.rehash() for tx in tx_list]
+ block.vtx.extend(tx_list)
+
+ # this is a little handier to use than the version in blocktools.py
+ def create_tx(self, spend_tx, n, value, script=CScript([OP_TRUE])):
+ return create_transaction(spend_tx, n, b"", value, script)
+
+ # sign a transaction, using the key we know about
+ # this signs input 0 in tx, which is assumed to be spending output n in spend_tx
+ def sign_tx(self, tx, spend_tx, n):
+ scriptPubKey = bytearray(spend_tx.vout[n].scriptPubKey)
+ if (scriptPubKey[0] == OP_TRUE): # an anyone-can-spend
+ tx.vin[0].scriptSig = CScript()
+ return
+ (sighash, err) = SignatureHash(spend_tx.vout[n].scriptPubKey, tx, 0, SIGHASH_ALL)
+ tx.vin[0].scriptSig = CScript([self.coinbase_key.sign(sighash) + bytes(bytearray([SIGHASH_ALL]))])
+
+ def create_and_sign_transaction(self, spend_tx, n, value, script=CScript([OP_TRUE])):
+ tx = self.create_tx(spend_tx, n, value, script)
+ self.sign_tx(tx, spend_tx, n)
+ tx.rehash()
+ return tx
+
+ def next_block(self, number, spend=None, additional_coinbase_value=0, script=CScript([OP_TRUE]), solve=True):
+ if self.tip is None:
+ base_block_hash = self.genesis_hash
+ block_time = int(time.time()) + 1
+ else:
+ base_block_hash = self.tip.sha256
+ block_time = self.tip.nTime + 1
+ # First create the coinbase
+ height = self.block_heights[base_block_hash] + 1
+ coinbase = create_coinbase(height, self.coinbase_pubkey)
+ coinbase.vout[0].nValue += additional_coinbase_value
+ coinbase.rehash()
+ if spend is None:
+ block = create_block(base_block_hash, coinbase, block_time)
+ else:
+ coinbase.vout[0].nValue += spend.tx.vout[spend.n].nValue - 1 # all but one satoshi to fees
+ coinbase.rehash()
+ block = create_block(base_block_hash, coinbase, block_time)
+ tx = create_transaction(spend.tx, spend.n, b"", 1, script) # spend 1 satoshi
+ self.sign_tx(tx, spend.tx, spend.n)
+ self.add_transactions_to_block(block, [tx])
+ block.hashMerkleRoot = block.calc_merkle_root()
+ if solve:
+ block.solve()
+ self.tip = block
+ self.block_heights[block.sha256] = height
+ assert number not in self.blocks
+ self.blocks[number] = block
+ return block
+
+ # save the current tip so it can be spent by a later block
+ def save_spendable_output(self):
+ self.log.debug("saving spendable output %s" % self.tip.vtx[0])
+ self.spendable_outputs.append(self.tip)
+
+ # get an output that we previously marked as spendable
+ def get_spendable_output(self):
+ self.log.debug("getting spendable output %s" % self.spendable_outputs[0].vtx[0])
+ return PreviousSpendableOutput(self.spendable_outputs.pop(0).vtx[0], 0)
+
+ # move the tip back to a previous block
+ def move_tip(self, number):
+ self.tip = self.blocks[number]
+
+ # adds transactions to the block and updates state
+ def update_block(self, block_number, new_transactions):
+ block = self.blocks[block_number]
+ self.add_transactions_to_block(block, new_transactions)
+ old_sha256 = block.sha256
+ block.hashMerkleRoot = block.calc_merkle_root()
+ block.solve()
+ # Update the internal state just like in next_block
+ self.tip = block
+ if block.sha256 != old_sha256:
+ self.block_heights[block.sha256] = self.block_heights[old_sha256]
+ del self.block_heights[old_sha256]
+ self.blocks[block_number] = block
+ return block
+
+ def reconnect_p2p(self):
+ """Add a P2P connection to the node.
+
+ The node gets disconnected several times in this test. This helper
+ method reconnects the p2p and restarts the network thread."""
+
+ network_thread_join()
+ self.nodes[0].disconnect_p2ps()
+ self.nodes[0].add_p2p_connection(P2PDataStore())
+ network_thread_start()
+ self.nodes[0].p2p.wait_for_verack()
+
+ def sync_blocks(self, blocks, success=True, reject_code=None, reject_reason=None, request_block=True, reconnect=False, timeout=60):
+ """Sends blocks to test node. Syncs and verifies that tip has advanced to most recent block.
+ Call with success = False if the tip shouldn't advance to the most recent block."""
+ self.nodes[0].p2p.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_code=reject_code, reject_reason=reject_reason, request_block=request_block, timeout=timeout)
+ if reconnect:
+ self.reconnect_p2p()
if __name__ == '__main__':
FullBlockTest().main()
diff --git a/test/functional/feature_blocksdir.py b/test/functional/feature_blocksdir.py
new file mode 100755
index 0000000000..a77014a524
--- /dev/null
+++ b/test/functional/feature_blocksdir.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python3
+# Copyright (c) 2018 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test the blocksdir option.
+"""
+
+import os
+import re
+import shutil
+
+from test_framework.test_framework import BitcoinTestFramework, initialize_datadir
+
+
+class BlocksdirTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 1
+
+ def run_test(self):
+ self.stop_node(0)
+ shutil.rmtree(self.nodes[0].datadir)
+ initialize_datadir(self.options.tmpdir, 0)
+ self.log.info("Starting with non exiting blocksdir ...")
+ blocksdir_path = os.path.join(self.options.tmpdir, 'blocksdir')
+ self.nodes[0].assert_start_raises_init_error(["-blocksdir=" + blocksdir_path], re.escape('Error: Specified blocks directory "{}" does not exist.'.format(blocksdir_path)))
+ os.mkdir(blocksdir_path)
+ self.log.info("Starting with exiting blocksdir ...")
+ self.start_node(0, ["-blocksdir=" + blocksdir_path])
+ self.log.info("mining blocks..")
+ self.nodes[0].generate(10)
+ assert os.path.isfile(os.path.join(blocksdir_path, "regtest", "blocks", "blk00000.dat"))
+ assert os.path.isdir(os.path.join(self.nodes[0].datadir, "regtest", "blocks", "index"))
+
+
+if __name__ == '__main__':
+ BlocksdirTest().main()
diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py
index c6cec0596b..6b1e473aa2 100755
--- a/test/functional/feature_config_args.py
+++ b/test/functional/feature_config_args.py
@@ -5,9 +5,10 @@
"""Test various command line arguments and configuration file parameters."""
import os
+import re
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import get_datadir_path
+
class ConfArgsTest(BitcoinTestFramework):
def set_test_params(self):
@@ -19,19 +20,19 @@ class ConfArgsTest(BitcoinTestFramework):
# 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)
+ default_data_dir = self.nodes[0].datadir
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.')
+ self.nodes[0].assert_start_raises_init_error(['-datadir=' + new_data_dir], 'Error: Specified data directory "' + re.escape(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.')
+ self.nodes[0].assert_start_raises_init_error(['-conf=' + conf_file], 'Error reading configuration file: specified data directory "' + re.escape(new_data_dir) + '" does not exist.')
# Create the directory and ensure the config file now works
os.mkdir(new_data_dir)
diff --git a/test/functional/feature_fee_estimation.py b/test/functional/feature_fee_estimation.py
index 8a56d3eefa..32a6bd5d59 100755
--- a/test/functional/feature_fee_estimation.py
+++ b/test/functional/feature_fee_estimation.py
@@ -99,7 +99,7 @@ def split_inputs(from_node, txins, txouts, initial_split=False):
txouts.append({"txid": txid, "vout": 0, "amount": half_change})
txouts.append({"txid": txid, "vout": 1, "amount": rem_change})
-def check_estimates(node, fees_seen, max_invalid):
+def check_estimates(node, fees_seen):
"""Call estimatesmartfee and verify that the estimates meet certain invariants."""
delta = 1.0e-6 # account for rounding error
@@ -133,12 +133,12 @@ class EstimateFeeTest(BitcoinTestFramework):
which we will use to generate our transactions.
"""
self.add_nodes(3, extra_args=[["-maxorphantx=1000", "-whitelist=127.0.0.1"],
- ["-blockmaxsize=17000", "-maxorphantx=1000"],
- ["-blockmaxsize=8000", "-maxorphantx=1000"]])
+ ["-blockmaxweight=68000", "-maxorphantx=1000"],
+ ["-blockmaxweight=32000", "-maxorphantx=1000"]])
# Use node0 to mine blocks for input splitting
# Node1 mines small blocks but that are bigger than the expected transaction rate.
- # NOTE: the CreateNewBlock code starts counting block size at 1,000 bytes,
- # (17k is room enough for 110 or so transactions)
+ # NOTE: the CreateNewBlock code starts counting block weight at 4,000 weight,
+ # (68k weight is room enough for 120 or so transactions)
# Node2 is a stingy miner, that
# produces too small blocks (room for only 55 or so transactions)
@@ -219,13 +219,13 @@ class EstimateFeeTest(BitcoinTestFramework):
self.log.info("Creating transactions and mining them with a block size that can't keep up")
# Create transactions and mine 10 small blocks with node 2, but create txs faster than we can mine
self.transact_and_mine(10, self.nodes[2])
- check_estimates(self.nodes[1], self.fees_per_kb, 14)
+ check_estimates(self.nodes[1], self.fees_per_kb)
self.log.info("Creating transactions and mining them at a block size that is just big enough")
# Generate transactions while mining 10 more blocks, this time with node1
# which mines blocks with capacity just above the rate that transactions are being created
self.transact_and_mine(10, self.nodes[1])
- check_estimates(self.nodes[1], self.fees_per_kb, 2)
+ check_estimates(self.nodes[1], self.fees_per_kb)
# Finish by mining a normal-sized block:
while len(self.nodes[1].getrawmempool()) > 0:
@@ -233,7 +233,7 @@ class EstimateFeeTest(BitcoinTestFramework):
sync_blocks(self.nodes[0:3], wait=.1)
self.log.info("Final estimates after emptying mempools")
- check_estimates(self.nodes[1], self.fees_per_kb, 2)
+ check_estimates(self.nodes[1], self.fees_per_kb)
if __name__ == '__main__':
EstimateFeeTest().main()
diff --git a/test/functional/feature_logging.py b/test/functional/feature_logging.py
index da4e7b0398..a4ebc7cca3 100755
--- a/test/functional/feature_logging.py
+++ b/test/functional/feature_logging.py
@@ -30,8 +30,8 @@ class LoggingTest(BitcoinTestFramework):
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")
+ exp_stderr = "Error: Could not open debug log file \S+$"
+ self.nodes[0].assert_start_raises_init_error(["-debuglogfile=%s" % (invalidname)], exp_stderr)
assert not os.path.isfile(os.path.join(invdir, "foo.log"))
# check that invalid log (relative) works after path exists
@@ -44,8 +44,7 @@ class LoggingTest(BitcoinTestFramework):
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")
+ self.nodes[0].assert_start_raises_init_error(["-debuglogfile=%s" % invalidname], exp_stderr)
assert not os.path.isfile(os.path.join(invdir, "foo.log"))
# check that invalid log (absolute) works after path exists
diff --git a/test/functional/feature_maxuploadtarget.py b/test/functional/feature_maxuploadtarget.py
index 45336ee801..ce6ec76c61 100755
--- a/test/functional/feature_maxuploadtarget.py
+++ b/test/functional/feature_maxuploadtarget.py
@@ -6,7 +6,7 @@
* Verify that getdata requests for old blocks (>1week) are dropped
if uploadtarget has been reached.
-* Verify that getdata requests for recent blocks are respecteved even
+* Verify that getdata requests for recent blocks are respected even
if uploadtarget has been reached.
* Verify that the upload counters are reset after 24 hours.
"""
@@ -17,7 +17,7 @@ from test_framework.mininode import *
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
-class TestNode(P2PInterface):
+class TestP2PConn(P2PInterface):
def __init__(self):
super().__init__()
self.block_receive_map = defaultdict(int)
@@ -34,7 +34,7 @@ class MaxUploadTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
- self.extra_args = [["-maxuploadtarget=800", "-blockmaxsize=999000"]]
+ self.extra_args = [["-maxuploadtarget=800"]]
# Cache for utxos, as the listunspent may take a long time later in the test
self.utxo_cache = []
@@ -55,7 +55,7 @@ class MaxUploadTest(BitcoinTestFramework):
p2p_conns = []
for _ in range(3):
- p2p_conns.append(self.nodes[0].add_p2p_connection(TestNode()))
+ p2p_conns.append(self.nodes[0].add_p2p_connection(TestP2PConn()))
network_thread_start()
for p2pc in p2p_conns:
@@ -144,10 +144,10 @@ class MaxUploadTest(BitcoinTestFramework):
#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"])
+ self.start_node(0, ["-whitelist=127.0.0.1", "-maxuploadtarget=1"])
# Reconnect to self.nodes[0]
- self.nodes[0].add_p2p_connection(TestNode())
+ self.nodes[0].add_p2p_connection(TestP2PConn())
network_thread_start()
self.nodes[0].p2p.wait_for_verack()
diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py
index 49ad7f838c..3adde8dd73 100755
--- a/test/functional/feature_pruning.py
+++ b/test/functional/feature_pruning.py
@@ -11,7 +11,6 @@ This test takes 30 mins or more (up to 2 hours)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
-import time
import os
MIN_BLOCKS_TO_KEEP = 288
@@ -23,7 +22,7 @@ TIMESTAMP_WINDOW = 2 * 60 * 60
def calc_usage(blockdir):
- return sum(os.path.getsize(blockdir+f) for f in os.listdir(blockdir) if os.path.isfile(blockdir+f)) / (1024. * 1024.)
+ return sum(os.path.getsize(blockdir+f) for f in os.listdir(blockdir) if os.path.isfile(os.path.join(blockdir, f))) / (1024. * 1024.)
class PruneTest(BitcoinTestFramework):
def set_test_params(self):
@@ -32,20 +31,20 @@ class PruneTest(BitcoinTestFramework):
# Create nodes 0 and 1 to mine.
# Create node 2 to test pruning.
- self.full_node_default_args = ["-maxreceivebuffer=20000","-blockmaxsize=999000", "-checkblocks=5", "-limitdescendantcount=100", "-limitdescendantsize=5000", "-limitancestorcount=100", "-limitancestorsize=5000" ]
+ self.full_node_default_args = ["-maxreceivebuffer=20000", "-checkblocks=5", "-limitdescendantcount=100", "-limitdescendantsize=5000", "-limitancestorcount=100", "-limitancestorsize=5000" ]
# Create nodes 3 and 4 to test manual pruning (they will be re-started with manual pruning later)
# Create nodes 5 to test wallet in prune mode, but do not connect
self.extra_args = [self.full_node_default_args,
self.full_node_default_args,
["-maxreceivebuffer=20000", "-prune=550"],
- ["-maxreceivebuffer=20000", "-blockmaxsize=999000"],
- ["-maxreceivebuffer=20000", "-blockmaxsize=999000"],
+ ["-maxreceivebuffer=20000"],
+ ["-maxreceivebuffer=20000"],
["-prune=550"]]
def setup_network(self):
self.setup_nodes()
- self.prunedir = self.options.tmpdir + "/node2/regtest/blocks/"
+ self.prunedir = os.path.join(self.nodes[2].datadir, 'regtest', 'blocks', '')
connect_nodes(self.nodes[0], 1)
connect_nodes(self.nodes[1], 2)
@@ -70,7 +69,7 @@ class PruneTest(BitcoinTestFramework):
sync_blocks(self.nodes[0:5])
def test_height_min(self):
- if not os.path.isfile(self.prunedir+"blk00000.dat"):
+ if not os.path.isfile(os.path.join(self.prunedir, "blk00000.dat")):
raise AssertionError("blk00000.dat is missing, pruning too early")
self.log.info("Success")
self.log.info("Though we're already using more than 550MiB, current usage: %d" % calc_usage(self.prunedir))
@@ -79,11 +78,8 @@ class PruneTest(BitcoinTestFramework):
for i in range(25):
mine_large_block(self.nodes[0], self.utxo_cache_0)
- waitstart = time.time()
- while os.path.isfile(self.prunedir+"blk00000.dat"):
- time.sleep(0.1)
- if time.time() - waitstart > 30:
- raise AssertionError("blk00000.dat not pruned when it should be")
+ # Wait for blk00000.dat to be pruned
+ wait_until(lambda: not os.path.isfile(os.path.join(self.prunedir, "blk00000.dat")), timeout=30)
self.log.info("Success")
usage = calc_usage(self.prunedir)
@@ -128,7 +124,7 @@ class PruneTest(BitcoinTestFramework):
# Reboot node 1 to clear its mempool (hopefully make the invalidate faster)
# Lower the block max size so we don't keep mining all our big mempool transactions (from disconnected blocks)
self.stop_node(1)
- self.start_node(1, extra_args=["-maxreceivebuffer=20000","-blockmaxsize=5000", "-checkblocks=5", "-disablesafemode"])
+ self.start_node(1, extra_args=["-maxreceivebuffer=20000","-blockmaxweight=20000", "-checkblocks=5", "-disablesafemode"])
height = self.nodes[1].getblockcount()
self.log.info("Current block height: %d" % height)
@@ -151,7 +147,7 @@ class PruneTest(BitcoinTestFramework):
# Reboot node1 to clear those giant tx's from mempool
self.stop_node(1)
- self.start_node(1, extra_args=["-maxreceivebuffer=20000","-blockmaxsize=5000", "-checkblocks=5", "-disablesafemode"])
+ self.start_node(1, extra_args=["-maxreceivebuffer=20000","-blockmaxweight=20000", "-checkblocks=5", "-disablesafemode"])
self.log.info("Generating new longer chain of 300 more blocks")
self.nodes[1].generate(300)
@@ -190,8 +186,8 @@ class PruneTest(BitcoinTestFramework):
# Verify that we have enough history to reorg back to the fork point
# Although this is more than 288 blocks, because this chain was written more recently
- # and only its other 299 small and 220 large block are in the block files after it,
- # its expected to still be retained
+ # and only its other 299 small and 220 large blocks are in the block files after it,
+ # it is expected to still be retained
self.nodes[2].getblock(self.nodes[2].getblockhash(self.forkheight))
first_reorg_height = self.nodes[2].getblockcount()
@@ -218,11 +214,8 @@ class PruneTest(BitcoinTestFramework):
goalbestheight = first_reorg_height + 1
self.log.info("Verify node 2 reorged back to the main chain, some blocks of which it had to redownload")
- waitstart = time.time()
- while self.nodes[2].getblockcount() < goalbestheight:
- time.sleep(0.1)
- if time.time() - waitstart > 900:
- raise AssertionError("Node 2 didn't reorg to proper height")
+ # Wait for Node 2 to reorg to proper height
+ wait_until(lambda: self.nodes[2].getblockcount() >= goalbestheight, timeout=900)
assert(self.nodes[2].getbestblockhash() == goalbesthash)
# Verify we can now have the data for a block previously pruned
assert(self.nodes[2].getblock(self.forkhash)["height"] == self.forkheight)
@@ -262,7 +255,7 @@ class PruneTest(BitcoinTestFramework):
assert_equal(ret, expected_ret)
def has_block(index):
- return os.path.isfile(self.options.tmpdir + "/node{}/regtest/blocks/blk{:05}.dat".format(node_number, index))
+ return os.path.isfile(os.path.join(self.nodes[node_number].datadir, "regtest", "blocks", "blk{:05}.dat".format(index)))
# should not prune because chain tip of node 3 (995) < PruneAfterHeight (1000)
assert_raises_rpc_error(-1, "Blockchain is too short for pruning", node.pruneblockchain, height(500))
diff --git a/test/functional/feature_reindex.py b/test/functional/feature_reindex.py
index ac67e6e9ba..d1d3f1d7f1 100755
--- a/test/functional/feature_reindex.py
+++ b/test/functional/feature_reindex.py
@@ -10,8 +10,7 @@
"""
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal
-import time
+from test_framework.util import wait_until
class ReindexTest(BitcoinTestFramework):
@@ -25,9 +24,7 @@ class ReindexTest(BitcoinTestFramework):
self.stop_nodes()
extra_args = [["-reindex-chainstate" if justchainstate else "-reindex", "-checkblockindex=1"]]
self.start_nodes(extra_args)
- while self.nodes[0].getblockcount() < blockcount:
- time.sleep(0.1)
- assert_equal(self.nodes[0].getblockcount(), blockcount)
+ wait_until(lambda: self.nodes[0].getblockcount() == blockcount)
self.log.info("Success")
def run_test(self):
diff --git a/test/functional/feature_uacomment.py b/test/functional/feature_uacomment.py
index bc3791508a..c73bdcfbb8 100755
--- a/test/functional/feature_uacomment.py
+++ b/test/functional/feature_uacomment.py
@@ -4,9 +4,12 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the -uacomment option."""
+import re
+
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
@@ -23,13 +26,14 @@ class UacommentTest(BitcoinTestFramework):
self.log.info("test -uacomment max length")
self.stop_node(0)
- expected = "exceeds maximum length (256). Reduce the number or size of uacomments."
- self.assert_start_raises_init_error(0, ["-uacomment=" + 'a' * 256], expected)
+ expected = "Error: Total length of network version string \([0-9]+\) exceeds maximum length \(256\). Reduce the number or size of uacomments."
+ self.nodes[0].assert_start_raises_init_error(["-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)
+ expected = "Error: User Agent comment \(" + re.escape(unsafe_char) + "\) contains unsafe characters."
+ self.nodes[0].assert_start_raises_init_error(["-uacomment=" + unsafe_char], expected)
+
if __name__ == '__main__':
UacommentTest().main()
diff --git a/test/functional/interface_bitcoin_cli.py b/test/functional/interface_bitcoin_cli.py
index d8c80ab34f..e29fdc84e7 100755
--- a/test/functional/interface_bitcoin_cli.py
+++ b/test/functional/interface_bitcoin_cli.py
@@ -29,11 +29,17 @@ class TestBitcoinCli(BitcoinTestFramework):
self.log.info("Test -stdinrpcpass option")
assert_equal(0, self.nodes[0].cli('-rpcuser=%s' % user, '-stdinrpcpass', input=password).getblockcount())
- assert_raises_process_error(1, "incorrect rpcuser or rpcpassword", self.nodes[0].cli('-rpcuser=%s' % user, '-stdinrpcpass', input="foo").echo)
+ assert_raises_process_error(1, "Incorrect rpcuser or rpcpassword", self.nodes[0].cli('-rpcuser=%s' % user, '-stdinrpcpass', input="foo").echo)
self.log.info("Test -stdin and -stdinrpcpass")
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)
+ assert_raises_process_error(1, "Incorrect rpcuser or rpcpassword", self.nodes[0].cli('-rpcuser=%s' % user, '-stdin', '-stdinrpcpass', input="foo").echo)
+
+ self.log.info("Test connecting to a non-existing server")
+ assert_raises_process_error(1, "Could not connect to the server", self.nodes[0].cli('-rpcport=1').echo)
+
+ self.log.info("Test connecting with non-existing RPC cookie file")
+ assert_raises_process_error(1, "Could not locate RPC credentials", self.nodes[0].cli('-rpccookiefile=does-not-exist', '-rpcpassword=').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)
diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py
index 8440f13a0d..6f585f6825 100755
--- a/test/functional/interface_rest.py
+++ b/test/functional/interface_rest.py
@@ -85,7 +85,7 @@ class RESTTest (BitcoinTestFramework):
#######################################
# GETUTXOS: query an unspent outpoint #
#######################################
- json_request = '/checkmempool/'+txid+'-'+str(n)
+ json_request = '/'+txid+'-'+str(n)
json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json')
json_obj = json.loads(json_string)
@@ -100,14 +100,14 @@ class RESTTest (BitcoinTestFramework):
#################################################
# GETUTXOS: now query an already spent outpoint #
#################################################
- json_request = '/checkmempool/'+vintx+'-0'
+ json_request = '/'+vintx+'-0'
json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json')
json_obj = json.loads(json_string)
#check chainTip response
assert_equal(json_obj['chaintipHash'], bb_hash)
- #make sure there is no utox in the response because this oupoint has been spent
+ #make sure there is no utxo in the response because this oupoint has been spent
assert_equal(len(json_obj['utxos']), 0)
#check bitmap
@@ -117,7 +117,7 @@ class RESTTest (BitcoinTestFramework):
##################################################
# GETUTXOS: now check both with the same request #
##################################################
- json_request = '/checkmempool/'+txid+'-'+str(n)+'/'+vintx+'-0'
+ json_request = '/'+txid+'-'+str(n)+'/'+vintx+'-0'
json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json')
json_obj = json.loads(json_string)
assert_equal(len(json_obj['utxos']), 1)
@@ -151,23 +151,48 @@ class RESTTest (BitcoinTestFramework):
txid = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.1)
json_string = http_get_call(url.hostname, url.port, '/rest/tx/'+txid+self.FORMAT_SEPARATOR+"json")
json_obj = json.loads(json_string)
- vintx = json_obj['vin'][0]['txid'] # get the vin to later check for utxo (should be spent by then)
+ # get the spent output to later check for utxo (should be spent by then)
+ spent = '{}-{}'.format(json_obj['vin'][0]['txid'], json_obj['vin'][0]['vout'])
# get n of 0.1 outpoint
n = 0
for vout in json_obj['vout']:
if vout['value'] == 0.1:
n = vout['n']
+ spending = '{}-{}'.format(txid, n)
- json_request = '/'+txid+'-'+str(n)
+ json_request = '/'+spending
json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json')
json_obj = json.loads(json_string)
- assert_equal(len(json_obj['utxos']), 0) #there should be an outpoint because it has just added to the mempool
+ assert_equal(len(json_obj['utxos']), 0) #there should be no outpoint because it has just added to the mempool
- json_request = '/checkmempool/'+txid+'-'+str(n)
+ json_request = '/checkmempool/'+spending
json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json')
json_obj = json.loads(json_string)
assert_equal(len(json_obj['utxos']), 1) #there should be an outpoint because it has just added to the mempool
+ json_request = '/'+spent
+ json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json')
+ json_obj = json.loads(json_string)
+ assert_equal(len(json_obj['utxos']), 1) #there should be an outpoint because its spending tx is not confirmed
+
+ json_request = '/checkmempool/'+spent
+ json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json')
+ json_obj = json.loads(json_string)
+ assert_equal(len(json_obj['utxos']), 0) #there should be no outpoint because it has just spent (by mempool tx)
+
+ self.nodes[0].generate(1)
+ self.sync_all()
+
+ json_request = '/'+spending
+ json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json')
+ json_obj = json.loads(json_string)
+ assert_equal(len(json_obj['utxos']), 1) #there should be an outpoint because it was mined
+
+ json_request = '/checkmempool/'+spending
+ json_string = http_get_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json')
+ json_obj = json.loads(json_string)
+ assert_equal(len(json_obj['utxos']), 1) #there should be an outpoint because it was mined
+
#do some invalid requests
json_request = '{"checkmempool'
response = http_post_call(url.hostname, url.port, '/rest/getutxos'+self.FORMAT_SEPARATOR+'json', json_request, True)
diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py
index 53748df915..75eb9b1784 100755
--- a/test/functional/mempool_persist.py
+++ b/test/functional/mempool_persist.py
@@ -29,7 +29,7 @@ Test is as follows:
transactions in its mempool. This tests that -persistmempool=0
does not overwrite a previously valid mempool stored on disk.
- Remove node0 mempool.dat and verify savemempool RPC recreates it
- and verify that node1 can load it and has 5 transaction in its
+ and verify that node1 can load it and has 5 transactions in its
mempool.
- Verify that savemempool throws when the RPC is called if
node1 can't write to disk.
@@ -93,8 +93,8 @@ class MempoolPersistTest(BitcoinTestFramework):
self.start_node(0)
wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5)
- mempooldat0 = os.path.join(self.options.tmpdir, 'node0', 'regtest', 'mempool.dat')
- mempooldat1 = os.path.join(self.options.tmpdir, 'node1', 'regtest', 'mempool.dat')
+ mempooldat0 = os.path.join(self.nodes[0].datadir, 'regtest', 'mempool.dat')
+ mempooldat1 = os.path.join(self.nodes[1].datadir, 'regtest', 'mempool.dat')
self.log.debug("Remove the mempool.dat file. Verify that savemempool to disk via RPC re-creates it")
os.remove(mempooldat0)
self.nodes[0].savemempool()
diff --git a/test/functional/mining_prioritisetransaction.py b/test/functional/mining_prioritisetransaction.py
index 32e2b47fc9..e754dd31ad 100755
--- a/test/functional/mining_prioritisetransaction.py
+++ b/test/functional/mining_prioritisetransaction.py
@@ -124,7 +124,7 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
assert(tx_id not in self.nodes[0].getrawmempool())
# This is a less than 1000-byte transaction, so just set the fee
- # to be the minimum for a 1000 byte transaction and check that it is
+ # to be the minimum for a 1000-byte transaction and check that it is
# accepted.
self.nodes[0].prioritisetransaction(txid=tx_id, fee_delta=int(self.relayfee*COIN))
diff --git a/test/functional/p2p_compactblocks.py b/test/functional/p2p_compactblocks.py
index d9f461a049..1657d97281 100755
--- a/test/functional/p2p_compactblocks.py
+++ b/test/functional/p2p_compactblocks.py
@@ -14,8 +14,8 @@ from test_framework.util import *
from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment
from test_framework.script import CScript, OP_TRUE
-# TestNode: A peer we use to send messages to bitcoind, and store responses.
-class TestNode(P2PInterface):
+# TestP2PConn: A peer we use to send messages to bitcoind, and store responses.
+class TestP2PConn(P2PInterface):
def __init__(self):
super().__init__()
self.last_sendcmpct = []
@@ -548,7 +548,7 @@ class CompactBlocksTest(BitcoinTestFramework):
# Note that it's possible for bitcoind to be smart enough to know we're
# lying, since it could check to see if the shortid matches what we're
# sending, and eg disconnect us for misbehavior. If that behavior
- # change were made, we could just modify this test by having a
+ # change was made, we could just modify this test by having a
# different peer provide the block further down, so that we're still
# verifying that the block isn't marked bad permanently. This is good
# enough for now.
@@ -788,9 +788,9 @@ class CompactBlocksTest(BitcoinTestFramework):
def run_test(self):
# Setup the p2p connections and start up the network thread.
- 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)
+ self.test_node = self.nodes[0].add_p2p_connection(TestP2PConn())
+ self.segwit_node = self.nodes[1].add_p2p_connection(TestP2PConn(), services=NODE_NETWORK|NODE_WITNESS)
+ self.old_node = self.nodes[1].add_p2p_connection(TestP2PConn(), services=NODE_NETWORK)
network_thread_start()
diff --git a/test/functional/p2p_feefilter.py b/test/functional/p2p_feefilter.py
index 47d9c55160..7c954cdca2 100755
--- a/test/functional/p2p_feefilter.py
+++ b/test/functional/p2p_feefilter.py
@@ -22,7 +22,7 @@ def allInvsMatch(invsExpected, testnode):
time.sleep(1)
return False
-class TestNode(P2PInterface):
+class TestP2PConn(P2PInterface):
def __init__(self):
super().__init__()
self.txinvs = []
@@ -48,7 +48,7 @@ class FeeFilterTest(BitcoinTestFramework):
sync_blocks(self.nodes)
# Setup the p2p connections and start up the network thread.
- self.nodes[0].add_p2p_connection(TestNode())
+ self.nodes[0].add_p2p_connection(TestP2PConn())
network_thread_start()
self.nodes[0].p2p.wait_for_verack()
diff --git a/test/functional/p2p_leak.py b/test/functional/p2p_leak.py
index ce4e6e9144..198dcc1490 100755
--- a/test/functional/p2p_leak.py
+++ b/test/functional/p2p_leak.py
@@ -7,7 +7,7 @@
A node should never send anything other than VERSION/VERACK/REJECT until it's
received a VERACK.
-This test connects to a node and sends it a few messages, trying to intice it
+This test connects to a node and sends it a few messages, trying to entice it
into sending us something it shouldn't.
Also test that nodes that send unsupported service bits to bitcoind are disconnected
diff --git a/test/functional/p2p_node_network_limited.py b/test/functional/p2p_node_network_limited.py
index 81a41d6a97..301d8c181a 100755
--- a/test/functional/p2p_node_network_limited.py
+++ b/test/functional/p2p_node_network_limited.py
@@ -64,7 +64,7 @@ class NodeNetworkLimitedTest(BitcoinTestFramework):
blocks = self.nodes[1].generate(292)
sync_blocks([self.nodes[0], self.nodes[1]])
- self.log.info("Make sure we can max retrive block at tip-288.")
+ self.log.info("Make sure we can max retrieve 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)
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index 20e4805df0..e6af35fc3d 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -59,7 +59,7 @@ def test_witness_block(rpc, p2p, block, accepted, with_witness=True):
p2p.sync_with_ping()
assert_equal(rpc.getbestblockhash() == block.hash, accepted)
-class TestNode(P2PInterface):
+class TestP2PConn(P2PInterface):
def __init__(self):
super().__init__()
self.getdataset = set()
@@ -425,7 +425,7 @@ class SegWitTest(BitcoinTestFramework):
assert(self.nodes[0].getbestblockhash() == block.hash)
- # Now make sure that malleating the witness nonce doesn't
+ # Now make sure that malleating the witness reserved value doesn't
# result in a block permanently marked bad.
block = self.build_next_block()
add_witness_commitment(block)
@@ -436,7 +436,7 @@ class SegWitTest(BitcoinTestFramework):
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ ser_uint256(1) ]
test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False)
- # Changing the witness nonce doesn't change the block hash
+ # Changing the witness reserved value doesn't change the block hash
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ ser_uint256(0) ]
test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
@@ -1511,7 +1511,7 @@ class SegWitTest(BitcoinTestFramework):
# Make sure that this peer thinks segwit has activated.
assert(get_bip9_status(self.nodes[node_id], 'segwit')['status'] == "active")
- # Make sure this peers blocks match those of node0.
+ # Make sure this peer's blocks match those of node0.
height = self.nodes[node_id].getblockcount()
while height >= 0:
block_hash = self.nodes[node_id].getblockhash(height)
@@ -1878,11 +1878,11 @@ class SegWitTest(BitcoinTestFramework):
def run_test(self):
# Setup the p2p connections and start up the network 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.test_node = self.nodes[0].add_p2p_connection(TestP2PConn(), 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.old_node = self.nodes[0].add_p2p_connection(TestP2PConn(), 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)
+ self.std_node = self.nodes[1].add_p2p_connection(TestP2PConn(), services=NODE_NETWORK|NODE_WITNESS)
network_thread_start()
diff --git a/test/functional/p2p_timeouts.py b/test/functional/p2p_timeouts.py
index 6d21095cc6..6a21b693b4 100755
--- a/test/functional/p2p_timeouts.py
+++ b/test/functional/p2p_timeouts.py
@@ -27,7 +27,7 @@ from test_framework.mininode import *
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
-class TestNode(P2PInterface):
+class TestP2PConn(P2PInterface):
def on_version(self, message):
# Don't send a verack in response
pass
@@ -39,9 +39,9 @@ class TimeoutsTest(BitcoinTestFramework):
def run_test(self):
# Setup the p2p connections and start up the network thread.
- 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)
+ no_verack_node = self.nodes[0].add_p2p_connection(TestP2PConn())
+ no_version_node = self.nodes[0].add_p2p_connection(TestP2PConn(), send_version=False)
+ no_send_node = self.nodes[0].add_p2p_connection(TestP2PConn(), send_version=False)
network_thread_start()
diff --git a/test/functional/p2p_unrequested_blocks.py b/test/functional/p2p_unrequested_blocks.py
index 672626f15b..53b2856eb5 100755
--- a/test/functional/p2p_unrequested_blocks.py
+++ b/test/functional/p2p_unrequested_blocks.py
@@ -166,7 +166,7 @@ class AcceptBlockTest(BitcoinTestFramework):
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)
+ # the last (height-too-high) on node (as long as it is not missing any headers)
tip = block_h3
all_blocks = []
for i in range(288):
diff --git a/test/functional/rpc_bind.py b/test/functional/rpc_bind.py
index d43c2cd5d0..5b50520d3f 100755
--- a/test/functional/rpc_bind.py
+++ b/test/functional/rpc_bind.py
@@ -48,14 +48,14 @@ class RPCBindTest(BitcoinTestFramework):
self.nodes[0].rpchost = None
self.start_nodes([base_args])
# connect to node through non-loopback interface
- node = get_rpc_proxy(rpc_url(get_datadir_path(self.options.tmpdir, 0), 0, "%s:%d" % (rpchost, rpcport)), 0, coveragedir=self.options.coveragedir)
+ node = get_rpc_proxy(rpc_url(self.nodes[0].datadir, 0, "%s:%d" % (rpchost, rpcport)), 0, coveragedir=self.options.coveragedir)
node.getnetworkinfo()
self.stop_nodes()
def run_test(self):
# due to OS-specific network stats queries, this test works only on Linux
if not sys.platform.startswith('linux'):
- raise SkipTest("This test can only be run on linux.")
+ raise SkipTest("This test can only be run on Linux.")
# find the first non-loopback interface for testing
non_loopback_ip = None
for name,ip in all_interfaces():
diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py
index 5f34b35bfb..72b5f4748f 100755
--- a/test/functional/rpc_net.py
+++ b/test/functional/rpc_net.py
@@ -7,11 +7,10 @@
Tests correspond to code in rpc/net.cpp.
"""
-import time
-
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
+ assert_greater_than_or_equal,
assert_raises_rpc_error,
connect_nodes_bi,
p2p_port,
@@ -35,26 +34,34 @@ class NetTest(BitcoinTestFramework):
assert_equal(self.nodes[0].getconnectioncount(), 2)
def _test_getnettotals(self):
- # check that getnettotals totalbytesrecv and totalbytessent
- # are consistent with getpeerinfo
+ # getnettotals totalbytesrecv and totalbytessent should be
+ # consistent with getpeerinfo. Since the RPC calls are not atomic,
+ # and messages might have been recvd or sent between RPC calls, call
+ # getnettotals before and after and verify that the returned values
+ # from getpeerinfo are bounded by those values.
+ net_totals_before = self.nodes[0].getnettotals()
peer_info = self.nodes[0].getpeerinfo()
+ net_totals_after = self.nodes[0].getnettotals()
assert_equal(len(peer_info), 2)
- net_totals = self.nodes[0].getnettotals()
- assert_equal(sum([peer['bytesrecv'] for peer in peer_info]),
- net_totals['totalbytesrecv'])
- assert_equal(sum([peer['bytessent'] for peer in peer_info]),
- net_totals['totalbytessent'])
+ peers_recv = sum([peer['bytesrecv'] for peer in peer_info])
+ peers_sent = sum([peer['bytessent'] for peer in peer_info])
+
+ assert_greater_than_or_equal(peers_recv, net_totals_before['totalbytesrecv'])
+ assert_greater_than_or_equal(net_totals_after['totalbytesrecv'], peers_recv)
+ assert_greater_than_or_equal(peers_sent, net_totals_before['totalbytessent'])
+ assert_greater_than_or_equal(net_totals_after['totalbytessent'], peers_sent)
+
# test getnettotals and getpeerinfo by doing a ping
# the bytes sent/received should change
# note ping and pong are 32 bytes each
self.nodes[0].ping()
- wait_until(lambda: (net_totals['totalbytessent'] + 32*2) == self.nodes[0].getnettotals()['totalbytessent'], timeout=1)
- wait_until(lambda: (net_totals['totalbytesrecv'] + 32*2) == self.nodes[0].getnettotals()['totalbytesrecv'], timeout=1)
+ wait_until(lambda: (self.nodes[0].getnettotals()['totalbytessent'] >= net_totals_after['totalbytessent'] + 32 * 2), timeout=1)
+ wait_until(lambda: (self.nodes[0].getnettotals()['totalbytesrecv'] >= net_totals_after['totalbytesrecv'] + 32 * 2), timeout=1)
peer_info_after_ping = self.nodes[0].getpeerinfo()
for before, after in zip(peer_info, peer_info_after_ping):
- assert_equal(before['bytesrecv_per_msg']['pong'] + 32, after['bytesrecv_per_msg']['pong'])
- assert_equal(before['bytessent_per_msg']['ping'] + 32, after['bytessent_per_msg']['ping'])
+ assert_greater_than_or_equal(after['bytesrecv_per_msg']['pong'], before['bytesrecv_per_msg']['pong'] + 32)
+ assert_greater_than_or_equal(after['bytessent_per_msg']['ping'], before['bytessent_per_msg']['ping'] + 32)
def _test_getnetworkinginfo(self):
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
@@ -62,12 +69,8 @@ class NetTest(BitcoinTestFramework):
self.nodes[0].setnetworkactive(False)
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], False)
- timeout = 3
- while self.nodes[0].getnetworkinfo()['connections'] != 0:
- # Wait a bit for all sockets to close
- assert timeout > 0, 'not all connections closed in time'
- timeout -= 0.1
- time.sleep(0.1)
+ # Wait a bit for all sockets to close
+ wait_until(lambda: self.nodes[0].getnetworkinfo()['connections'] == 0, timeout=3)
self.nodes[0].setnetworkactive(True)
connect_nodes_bi(self.nodes, 0, 1)
@@ -84,8 +87,7 @@ class NetTest(BitcoinTestFramework):
assert_equal(len(added_nodes), 1)
assert_equal(added_nodes[0]['addednode'], ip_port)
# check that a non-existent node returns an error
- assert_raises_rpc_error(-24, "Node has not been added",
- self.nodes[0].getaddednodeinfo, '1.1.1.1')
+ assert_raises_rpc_error(-24, "Node has not been added", self.nodes[0].getaddednodeinfo, '1.1.1.1')
def _test_getpeerinfo(self):
peer_info = [x.getpeerinfo() for x in self.nodes]
diff --git a/test/functional/rpc_users.py b/test/functional/rpc_users.py
index 01f68344ae..0ce412f74a 100755
--- a/test/functional/rpc_users.py
+++ b/test/functional/rpc_users.py
@@ -5,13 +5,18 @@
"""Test multiple RPC users."""
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import str_to_b64str, assert_equal
+from test_framework.util import (
+ assert_equal,
+ get_datadir_path,
+ str_to_b64str,
+)
import os
import http.client
import urllib.parse
-class HTTPBasicsTest (BitcoinTestFramework):
+
+class HTTPBasicsTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
@@ -22,10 +27,10 @@ class HTTPBasicsTest (BitcoinTestFramework):
rpcauth2 = "rpcauth=rt2:f8607b1a88861fac29dfccf9b52ff9f$ff36a0c23c8c62b4846112e50fa888416e94c17bfd4c42f88fd8f55ec6a3137e"
rpcuser = "rpcuser=rpcuser💻"
rpcpassword = "rpcpassword=rpcpassword🔑"
- with open(os.path.join(self.options.tmpdir+"/node0", "bitcoin.conf"), 'a', encoding='utf8') as f:
+ with open(os.path.join(get_datadir_path(self.options.tmpdir, 0), "bitcoin.conf"), 'a', encoding='utf8') as f:
f.write(rpcauth+"\n")
f.write(rpcauth2+"\n")
- with open(os.path.join(self.options.tmpdir+"/node1", "bitcoin.conf"), 'a', encoding='utf8') as f:
+ with open(os.path.join(get_datadir_path(self.options.tmpdir, 1), "bitcoin.conf"), 'a', encoding='utf8') as f:
f.write(rpcuser+"\n")
f.write(rpcpassword+"\n")
@@ -54,7 +59,7 @@ class HTTPBasicsTest (BitcoinTestFramework):
resp = conn.getresponse()
assert_equal(resp.status, 200)
conn.close()
-
+
#Use new authpair to confirm both work
headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)}
diff --git a/test/functional/test_framework/comptool.py b/test/functional/test_framework/comptool.py
index 61ea2280e2..e0ca78e5d1 100755
--- a/test/functional/test_framework/comptool.py
+++ b/test/functional/test_framework/comptool.py
@@ -8,7 +8,7 @@ To use, create a class that implements get_tests(), and pass it in
as the test generator to TestManager. get_tests() should be a python
generator that returns TestInstance objects. See below for definition.
-TestNode behaves as follows:
+TestP2PConn behaves as follows:
Configure with a BlockStore and TxStore
on_inv: log the message but don't request
on_headers: log the chain tip
@@ -39,7 +39,7 @@ class RejectResult():
def __repr__(self):
return '%i:%s' % (self.code,self.reason or '*')
-class TestNode(P2PInterface):
+class TestP2PConn(P2PInterface):
def __init__(self, block_store, tx_store):
super().__init__()
@@ -170,7 +170,7 @@ class TestManager():
def add_all_connections(self, nodes):
for i in range(len(nodes)):
# Create a p2p connection to each node
- node = TestNode(self.block_store, self.tx_store)
+ node = TestP2PConn(self.block_store, self.tx_store)
node.peer_connect('127.0.0.1', p2p_port(i))
self.p2p_connections.append(node)
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py
index e032be1337..ee573e01cc 100755
--- a/test/functional/test_framework/messages.py
+++ b/test/functional/test_framework/messages.py
@@ -4,7 +4,7 @@
# 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
+"""Bitcoin test framework primitive and message structures
CBlock, CTransaction, CBlockHeader, CTxIn, CTxOut, etc....:
data structures that should map to corresponding structures in
diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py
index 99d0abc3f9..f1f7d0c0cd 100755
--- a/test/functional/test_framework/mininode.py
+++ b/test/functional/test_framework/mininode.py
@@ -405,7 +405,7 @@ class P2PInterface(P2PConnection):
# Keep our own socket map for asyncore, so that we can track disconnects
-# ourselves (to workaround an issue with closing an asyncore socket when
+# ourselves (to work around an issue with closing an asyncore socket when
# using select)
mininode_socket_map = dict()
@@ -424,7 +424,7 @@ class NetworkThread(threading.Thread):
def run(self):
while mininode_socket_map:
# We check for whether to disconnect outside of the asyncore
- # loop to workaround the behavior of asyncore when using
+ # loop to work around the behavior of asyncore when using
# select
disconnected = []
for fd, obj in mininode_socket_map.items():
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 8efac9c475..d427f62856 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -228,7 +228,7 @@ class BitcoinTestFramework():
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, rpchost=rpchost, timewait=timewait, binary=binary[i], stderr=None, mocktime=self.mocktime, coverage_dir=self.options.coveragedir, extra_conf=extra_confs[i], extra_args=extra_args[i], use_cli=self.options.usecli))
+ self.nodes.append(TestNode(i, get_datadir_path(self.options.tmpdir, i), rpchost=rpchost, timewait=timewait, binary=binary[i], stderr=None, mocktime=self.mocktime, coverage_dir=self.options.coveragedir, extra_conf=extra_confs[i], extra_args=extra_args[i], use_cli=self.options.usecli))
def start_node(self, i, *args, **kwargs):
"""Start a bitcoind"""
@@ -281,27 +281,6 @@ class BitcoinTestFramework():
self.stop_node(i)
self.start_node(i, extra_args)
- def assert_start_raises_init_error(self, i, extra_args=None, expected_msg=None, *args, **kwargs):
- with tempfile.SpooledTemporaryFile(max_size=2**16) as log_stderr:
- try:
- self.start_node(i, extra_args, stderr=log_stderr, *args, **kwargs)
- self.stop_node(i)
- except Exception as e:
- assert 'bitcoind exited' in str(e) # node must have shutdown
- self.nodes[i].running = False
- self.nodes[i].process = None
- if expected_msg is not None:
- log_stderr.seek(0)
- stderr = log_stderr.read().decode('utf-8')
- if expected_msg not in stderr:
- raise AssertionError("Expected error \"" + expected_msg + "\" not found in:\n" + stderr)
- else:
- if expected_msg is None:
- assert_msg = "bitcoind should have exited with an error"
- else:
- assert_msg = "bitcoind should have exited with expected error " + expected_msg
- raise AssertionError(assert_msg)
-
def wait_for_node_exit(self, i, timeout):
self.nodes[i].process.wait(timeout)
@@ -335,7 +314,7 @@ class BitcoinTestFramework():
blockchain. If the cached version of the blockchain is used without
mocktime then the mempools will not sync due to IBD.
- For backwared compatibility of the python scripts with previous
+ For backward compatibility of the python scripts with previous
versions of the cache, this helper function sets mocktime to Jan 1,
2014 + (201 * 10 * 60)"""
self.mocktime = 1388534400 + (201 * 10 * 60)
@@ -400,7 +379,7 @@ class BitcoinTestFramework():
args = [os.getenv("BITCOIND", "bitcoind"), "-datadir=" + datadir]
if i > 0:
args.append("-connect=127.0.0.1:" + str(p2p_port(0)))
- self.nodes.append(TestNode(i, self.options.cachedir, extra_conf=["bind=127.0.0.1"], extra_args=[],rpchost=None, timewait=None, binary=None, stderr=None, mocktime=self.mocktime, coverage_dir=None))
+ self.nodes.append(TestNode(i, get_datadir_path(self.options.cachedir, i), extra_conf=["bind=127.0.0.1"], extra_args=[],rpchost=None, timewait=None, binary=None, stderr=None, mocktime=self.mocktime, coverage_dir=None))
self.nodes[i].args = args
self.start_node(i)
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 86e44e4c97..291ac3ee46 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -12,6 +12,7 @@ import logging
import os
import re
import subprocess
+import tempfile
import time
from .authproxy import JSONRPCException
@@ -29,6 +30,11 @@ JSONDecodeError = getattr(json, "JSONDecodeError", ValueError)
BITCOIND_PROC_WAIT_TIMEOUT = 60
+
+class FailedToStartError(Exception):
+ """Raised when a node fails to start correctly."""
+
+
class TestNode():
"""A class for representing a bitcoind node under test.
@@ -43,9 +49,9 @@ class TestNode():
To make things easier for the test writer, any unrecognised messages will
be dispatched to the RPC connection."""
- def __init__(self, i, dirname, rpchost, timewait, binary, stderr, mocktime, coverage_dir, extra_conf=None, extra_args=None, use_cli=False):
+ def __init__(self, i, datadir, rpchost, timewait, binary, stderr, mocktime, coverage_dir, extra_conf=None, extra_args=None, use_cli=False):
self.index = i
- self.datadir = os.path.join(dirname, "node" + str(i))
+ self.datadir = datadir
self.rpchost = rpchost
if timewait:
self.rpc_timeout = timewait
@@ -59,9 +65,9 @@ class TestNode():
self.stderr = stderr
self.coverage_dir = coverage_dir
if extra_conf != None:
- append_config(dirname, i, extra_conf)
+ append_config(datadir, extra_conf)
# Most callers will just need to add extra args to the standard list below.
- # For those callers that need more flexibity, they can just set the args property directly.
+ # For those callers that need more flexibility, they can just set the args property directly.
# Note that common args are set in the config file (see initialize_datadir)
self.extra_args = extra_args
self.args = [self.binary, "-datadir=" + self.datadir, "-logtimemicros", "-debug", "-debugexclude=libevent", "-debugexclude=leveldb", "-mocktime=" + str(mocktime), "-uacomment=testnode%d" % i]
@@ -101,7 +107,8 @@ class TestNode():
# Poll at a rate of four times per second
poll_per_s = 4
for _ in range(poll_per_s * self.rpc_timeout):
- assert self.process.poll() is None, "bitcoind exited with status %i during initialization" % self.process.returncode
+ if self.process.poll() is not None:
+ raise FailedToStartError('bitcoind exited with status {} during initialization'.format(self.process.returncode))
try:
self.rpc = get_rpc_proxy(rpc_url(self.datadir, self.index, self.rpchost), self.index, timeout=self.rpc_timeout, coveragedir=self.coverage_dir)
self.rpc.getblockcount()
@@ -165,6 +172,41 @@ class TestNode():
def wait_until_stopped(self, timeout=BITCOIND_PROC_WAIT_TIMEOUT):
wait_until(self.is_node_stopped, timeout=timeout)
+ def assert_start_raises_init_error(self, extra_args=None, expected_msg=None, partial_match=False, *args, **kwargs):
+ """Attempt to start the node and expect it to raise an error.
+
+ extra_args: extra arguments to pass through to bitcoind
+ expected_msg: regex that stderr should match when bitcoind fails
+
+ Will throw if bitcoind starts without an error.
+ Will throw if an expected_msg is provided and it does not match bitcoind's stdout."""
+ with tempfile.SpooledTemporaryFile(max_size=2**16) as log_stderr:
+ try:
+ self.start(extra_args, stderr=log_stderr, *args, **kwargs)
+ self.wait_for_rpc_connection()
+ self.stop_node()
+ self.wait_until_stopped()
+ except FailedToStartError as e:
+ self.log.debug('bitcoind failed to start: %s', e)
+ self.running = False
+ self.process = None
+ # Check stderr for expected message
+ if expected_msg is not None:
+ log_stderr.seek(0)
+ stderr = log_stderr.read().decode('utf-8').strip()
+ if partial_match:
+ if re.search(expected_msg, stderr, flags=re.MULTILINE) is None:
+ raise AssertionError('Expected message "{}" does not partially match stderr:\n"{}"'.format(expected_msg, stderr))
+ else:
+ if re.fullmatch(expected_msg, stderr) is None:
+ raise AssertionError('Expected message "{}" does not fully match stderr:\n"{}"'.format(expected_msg, stderr))
+ else:
+ if expected_msg is None:
+ assert_msg = "bitcoind should have exited with an error"
+ else:
+ assert_msg = "bitcoind should have exited with expected error " + expected_msg
+ raise AssertionError(assert_msg)
+
def node_encrypt_wallet(self, passphrase):
""""Encrypts the wallet.
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 7be695550b..a24a2ec4f5 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -8,6 +8,7 @@ from base64 import b64encode
from binascii import hexlify, unhexlify
from decimal import Decimal, ROUND_DOWN
import hashlib
+import inspect
import json
import logging
import os
@@ -204,9 +205,9 @@ def wait_until(predicate, *, attempts=float('inf'), timeout=float('inf'), lock=N
if attempts == float('inf') and timeout == float('inf'):
timeout = 60
attempt = 0
- timeout += time.time()
+ time_end = time.time() + timeout
- while attempt < attempts and time.time() < timeout:
+ while attempt < attempts and time.time() < time_end:
if lock:
with lock:
if predicate():
@@ -218,8 +219,12 @@ def wait_until(predicate, *, attempts=float('inf'), timeout=float('inf'), lock=N
time.sleep(0.05)
# Print the cause of the timeout
- assert_greater_than(attempts, attempt)
- assert_greater_than(timeout, time.time())
+ predicate_source = inspect.getsourcelines(predicate)
+ logger.error("wait_until() failed. Predicate: {}".format(predicate_source))
+ if attempt >= attempts:
+ raise AssertionError("Predicate {} not true after {} attempts".format(predicate_source, attempts))
+ elif time.time() >= time_end:
+ raise AssertionError("Predicate {} not true after {} seconds".format(predicate_source, timeout))
raise RuntimeError('Unreachable')
# RPC/P2P connection constants and functions
@@ -284,7 +289,7 @@ def rpc_url(datadir, i, rpchost=None):
################
def initialize_datadir(dirname, n):
- datadir = os.path.join(dirname, "node" + str(n))
+ datadir = get_datadir_path(dirname, n)
if not os.path.isdir(datadir):
os.makedirs(datadir)
with open(os.path.join(datadir, "bitcoin.conf"), 'w', encoding='utf8') as f:
@@ -300,8 +305,7 @@ def initialize_datadir(dirname, n):
def get_datadir_path(dirname, n):
return os.path.join(dirname, "node" + str(n))
-def append_config(dirname, n, options):
- datadir = get_datadir_path(dirname, n)
+def append_config(datadir, options):
with open(os.path.join(datadir, "bitcoin.conf"), 'a', encoding='utf8') as f:
for option in options:
f.write(option + "\n")
@@ -340,20 +344,15 @@ def disconnect_nodes(from_connection, node_num):
for peer_id in [peer['id'] for peer in from_connection.getpeerinfo() if "testnode%d" % node_num in peer['subver']]:
from_connection.disconnectnode(nodeid=peer_id)
- for _ in range(50):
- if [peer['id'] for peer in from_connection.getpeerinfo() if "testnode%d" % node_num in peer['subver']] == []:
- break
- time.sleep(0.1)
- else:
- raise AssertionError("timed out waiting for disconnect")
+ # wait to disconnect
+ wait_until(lambda: [peer['id'] for peer in from_connection.getpeerinfo() if "testnode%d" % node_num in peer['subver']] == [], timeout=5)
def connect_nodes(from_connection, node_num):
ip_port = "127.0.0.1:" + str(p2p_port(node_num))
from_connection.addnode(ip_port, "onetry")
# poll until version handshake complete to avoid race conditions
# with transaction relaying
- while any(peer['version'] == 0 for peer in from_connection.getpeerinfo()):
- time.sleep(0.1)
+ wait_until(lambda: all(peer['version'] != 0 for peer in from_connection.getpeerinfo()))
def connect_nodes_bi(nodes, a, b):
connect_nodes(nodes[a], b)
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 673cdb5066..39f1180a45 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -67,7 +67,7 @@ BASE_SCRIPTS= [
'feature_segwit.py',
# vv Tests less than 2m vv
'wallet_basic.py',
- 'wallet_accounts.py',
+ 'wallet_labels.py',
'p2p_segwit.py',
'wallet_dump.py',
'rpc_listtransactions.py',
@@ -136,6 +136,7 @@ BASE_SCRIPTS= [
'p2p_unrequested_blocks.py',
'feature_logging.py',
'p2p_node_network_limited.py',
+ 'feature_blocksdir.py',
'feature_config_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
@@ -366,7 +367,7 @@ def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_cove
def print_results(test_results, max_len_name, runtime):
results = "\n" + BOLD[1] + "%s | %s | %s\n\n" % ("TEST".ljust(max_len_name), "STATUS ", "DURATION") + BOLD[0]
- test_results.sort(key=lambda result: result.name.lower())
+ test_results.sort(key=TestResult.sort_key)
all_passed = True
time_sum = 0
@@ -377,7 +378,11 @@ def print_results(test_results, max_len_name, runtime):
results += str(test_result)
status = TICK + "Passed" if all_passed else CROSS + "Failed"
+ if not all_passed:
+ results += RED[1]
results += BOLD[1] + "\n%s | %s | %s s (accumulated) \n" % ("ALL".ljust(max_len_name), status.ljust(9), time_sum) + BOLD[0]
+ if not all_passed:
+ results += RED[0]
results += "Runtime: %s s\n" % (runtime)
print(results)
@@ -433,7 +438,7 @@ class TestHandler:
proc.send_signal(signal.SIGINT)
if proc.poll() is not None:
log_out.seek(0), log_err.seek(0)
- [stdout, stderr] = [file.read().decode('utf-8') for file in (log_out, log_err)]
+ [stdout, stderr] = [log_file.read().decode('utf-8') for log_file in (log_out, log_err)]
log_out.close(), log_err.close()
if proc.returncode == TEST_EXIT_PASSED and stderr == "":
status = "Passed"
@@ -454,6 +459,14 @@ class TestResult():
self.time = time
self.padding = 0
+ def sort_key(self):
+ if self.status == "Passed":
+ return 0, self.name.lower()
+ elif self.status == "Failed":
+ return 2, self.name.lower()
+ elif self.status == "Skipped":
+ return 1, self.name.lower()
+
def __repr__(self):
if self.status == "Passed":
color = BLUE
@@ -490,7 +503,7 @@ def check_script_list(src_dir):
Check that there are no scripts in the functional tests directory which are
not being run by pull-tester.py."""
script_dir = src_dir + '/test/functional/'
- python_files = set([file for file in os.listdir(script_dir) if file.endswith(".py")])
+ python_files = set([test_file for test_file in os.listdir(script_dir) if test_file.endswith(".py")])
missed_tests = list(python_files - set(map(lambda x: x.split()[0], ALL_SCRIPTS + NON_SCRIPTS)))
if len(missed_tests) != 0:
print("%sWARNING!%s The following scripts are not being run: %s. Check the test lists in test_runner.py." % (BOLD[1], BOLD[0], str(missed_tests)))
@@ -550,8 +563,8 @@ class RPCCoverage():
if not os.path.isfile(coverage_ref_filename):
raise RuntimeError("No coverage reference found")
- with open(coverage_ref_filename, 'r') as file:
- all_cmds.update([line.strip() for line in file.readlines()])
+ with open(coverage_ref_filename, 'r') as coverage_ref_file:
+ all_cmds.update([line.strip() for line in coverage_ref_file.readlines()])
for root, dirs, files in os.walk(self.dir):
for filename in files:
@@ -559,8 +572,8 @@ class RPCCoverage():
coverage_filenames.add(os.path.join(root, filename))
for filename in coverage_filenames:
- with open(filename, 'r') as file:
- covered_cmds.update([line.strip() for line in file.readlines()])
+ with open(filename, 'r') as coverage_file:
+ covered_cmds.update([line.strip() for line in coverage_file.readlines()])
return all_cmds - covered_cmds
diff --git a/test/functional/wallet_abandonconflict.py b/test/functional/wallet_abandonconflict.py
index 7e0635d80f..d5ef08d782 100755
--- a/test/functional/wallet_abandonconflict.py
+++ b/test/functional/wallet_abandonconflict.py
@@ -109,7 +109,7 @@ class AbandonConflictTest(BitcoinTestFramework):
assert_equal(len(self.nodes[0].getrawmempool()), 0)
assert_equal(self.nodes[0].getbalance(), balance)
- # But if its received again then it is unabandoned
+ # But if it is received again then it is unabandoned
# And since now in mempool, the change is available
# But its child tx remains abandoned
self.nodes[0].sendrawtransaction(signed["hex"])
@@ -117,7 +117,7 @@ class AbandonConflictTest(BitcoinTestFramework):
assert_equal(newbalance, balance - Decimal("20") + Decimal("14.99998"))
balance = newbalance
- # Send child tx again so its unabandoned
+ # Send child tx again so it is unabandoned
self.nodes[0].sendrawtransaction(signed2["hex"])
newbalance = self.nodes[0].getbalance()
assert_equal(newbalance, balance - Decimal("10") - Decimal("14.99998") + Decimal("24.9996"))
diff --git a/test/functional/wallet_accounts.py b/test/functional/wallet_accounts.py
deleted file mode 100755
index ecd1cfc82b..0000000000
--- a/test/functional/wallet_accounts.py
+++ /dev/null
@@ -1,206 +0,0 @@
-#!/usr/bin/env python3
-# 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.
-
-RPCs tested are:
- - getaccountaddress
- - getaddressesbyaccount
- - listaddressgroupings
- - setaccount
- - sendfrom (with account arguments)
- - move (with account arguments)
-"""
-
-from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal
-
-class WalletAccountsTest(BitcoinTestFramework):
- def set_test_params(self):
- self.setup_clean_chain = True
- self.num_nodes = 1
- self.extra_args = [[]]
-
- def run_test(self):
- node = self.nodes[0]
- # Check that there's no UTXO on any of the nodes
- assert_equal(len(node.listunspent()), 0)
-
- # Note each time we call generate, all generated coins go into
- # the same address, so we call twice to get two addresses w/50 each
- node.generate(1)
- node.generate(101)
- assert_equal(node.getbalance(), 100)
-
- # there should be 2 address groups
- # each with 1 address with a balance of 50 Bitcoins
- address_groups = node.listaddressgroupings()
- assert_equal(len(address_groups), 2)
- # the addresses aren't linked now, but will be after we send to the
- # common address
- linked_addresses = set()
- for address_group in address_groups:
- assert_equal(len(address_group), 1)
- assert_equal(len(address_group[0]), 2)
- assert_equal(address_group[0][1], 50)
- linked_addresses.add(address_group[0][0])
-
- # send 50 from each address to a third address not in this wallet
- # There's some fee that will come back to us when the miner reward
- # matures.
- common_address = "msf4WtN1YQKXvNtvdFYt9JBnUD2FB41kjr"
- txid = node.sendmany(
- fromaccount="",
- amounts={common_address: 100},
- subtractfeefrom=[common_address],
- minconf=1,
- )
- tx_details = node.gettransaction(txid)
- fee = -tx_details['details'][0]['fee']
- # there should be 1 address group, with the previously
- # unlinked addresses now linked (they both have 0 balance)
- address_groups = node.listaddressgroupings()
- assert_equal(len(address_groups), 1)
- assert_equal(len(address_groups[0]), 2)
- assert_equal(set([a[0] for a in address_groups[0]]), linked_addresses)
- assert_equal([a[1] for a in address_groups[0]], [0, 0])
-
- node.generate(1)
-
- # we want to reset so that the "" account has what's expected.
- # 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)
- amount_to_send = 1.0
-
- # 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:
- 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)
-
- # Check that sendfrom account reduces listaccounts balances.
- for i, account in enumerate(accounts):
- to_account = accounts[(i+1) % len(accounts)]
- node.sendfrom(account.name, to_account.receive_address, amount_to_send)
- node.generate(1)
- for account in accounts:
- 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.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.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.name)['address']
- 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.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_backup.py b/test/functional/wallet_backup.py
index b4be7debb5..46a72d7e28 100755
--- a/test/functional/wallet_backup.py
+++ b/test/functional/wallet_backup.py
@@ -90,9 +90,9 @@ class WalletBackupTest(BitcoinTestFramework):
self.stop_node(2)
def erase_three(self):
- 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")
+ os.remove(os.path.join(self.nodes[0].datadir, 'regtest', 'wallets', 'wallet.dat'))
+ os.remove(os.path.join(self.nodes[1].datadir, 'regtest', 'wallets', 'wallet.dat'))
+ os.remove(os.path.join(self.nodes[2].datadir, 'regtest', 'wallets', 'wallet.dat'))
def run_test(self):
self.log.info("Generating initial blockchain")
@@ -116,13 +116,13 @@ class WalletBackupTest(BitcoinTestFramework):
self.do_one_round()
self.log.info("Backing up")
- tmpdir = self.options.tmpdir
- self.nodes[0].backupwallet(tmpdir + "/node0/wallet.bak")
- self.nodes[0].dumpwallet(tmpdir + "/node0/wallet.dump")
- self.nodes[1].backupwallet(tmpdir + "/node1/wallet.bak")
- self.nodes[1].dumpwallet(tmpdir + "/node1/wallet.dump")
- self.nodes[2].backupwallet(tmpdir + "/node2/wallet.bak")
- self.nodes[2].dumpwallet(tmpdir + "/node2/wallet.dump")
+
+ self.nodes[0].backupwallet(os.path.join(self.nodes[0].datadir, 'wallet.bak'))
+ self.nodes[0].dumpwallet(os.path.join(self.nodes[0].datadir, 'wallet.dump'))
+ self.nodes[1].backupwallet(os.path.join(self.nodes[1].datadir, 'wallet.bak'))
+ self.nodes[1].dumpwallet(os.path.join(self.nodes[1].datadir, 'wallet.dump'))
+ self.nodes[2].backupwallet(os.path.join(self.nodes[2].datadir, 'wallet.bak'))
+ self.nodes[2].dumpwallet(os.path.join(self.nodes[2].datadir, 'wallet.dump'))
self.log.info("More transactions")
for i in range(5):
@@ -150,13 +150,13 @@ class WalletBackupTest(BitcoinTestFramework):
self.erase_three()
# Start node2 with no chain
- shutil.rmtree(self.options.tmpdir + "/node2/regtest/blocks")
- shutil.rmtree(self.options.tmpdir + "/node2/regtest/chainstate")
+ shutil.rmtree(os.path.join(self.nodes[2].datadir, 'regtest', 'blocks'))
+ shutil.rmtree(os.path.join(self.nodes[2].datadir, 'regtest', 'chainstate'))
# Restore wallets from backup
- 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")
+ shutil.copyfile(os.path.join(self.nodes[0].datadir, 'wallet.bak'), os.path.join(self.nodes[0].datadir, 'regtest', 'wallets', 'wallet.dat'))
+ shutil.copyfile(os.path.join(self.nodes[1].datadir, 'wallet.bak'), os.path.join(self.nodes[1].datadir, 'regtest', 'wallets', 'wallet.dat'))
+ shutil.copyfile(os.path.join(self.nodes[2].datadir, 'wallet.bak'), os.path.join(self.nodes[2].datadir, 'regtest', 'wallets', 'wallet.dat'))
self.log.info("Re-starting nodes")
self.start_three()
@@ -171,8 +171,8 @@ class WalletBackupTest(BitcoinTestFramework):
self.erase_three()
#start node2 with no chain
- shutil.rmtree(self.options.tmpdir + "/node2/regtest/blocks")
- shutil.rmtree(self.options.tmpdir + "/node2/regtest/chainstate")
+ shutil.rmtree(os.path.join(self.nodes[2].datadir, 'regtest', 'blocks'))
+ shutil.rmtree(os.path.join(self.nodes[2].datadir, 'regtest', 'chainstate'))
self.start_three()
@@ -180,9 +180,9 @@ class WalletBackupTest(BitcoinTestFramework):
assert_equal(self.nodes[1].getbalance(), 0)
assert_equal(self.nodes[2].getbalance(), 0)
- self.nodes[0].importwallet(tmpdir + "/node0/wallet.dump")
- self.nodes[1].importwallet(tmpdir + "/node1/wallet.dump")
- self.nodes[2].importwallet(tmpdir + "/node2/wallet.dump")
+ self.nodes[0].importwallet(os.path.join(self.nodes[0].datadir, 'wallet.dump'))
+ self.nodes[1].importwallet(os.path.join(self.nodes[1].datadir, 'wallet.dump'))
+ self.nodes[2].importwallet(os.path.join(self.nodes[2].datadir, 'wallet.dump'))
sync_blocks(self.nodes)
@@ -192,10 +192,10 @@ class WalletBackupTest(BitcoinTestFramework):
# 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"]
+ os.path.join(self.nodes[0].datadir, 'regtest', 'wallets', 'wallet.dat'),
+ os.path.join(self.nodes[0].datadir, 'regtest', '.', 'wallets', 'wallet.dat'),
+ os.path.join(self.nodes[0].datadir, 'regtest', 'wallets', ''),
+ os.path.join(self.nodes[0].datadir, 'regtest', 'wallets')]
for sourcePath in sourcePaths:
assert_raises_rpc_error(-4, "backup failed", self.nodes[0].backupwallet, sourcePath)
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index f686cb6ea5..0436aca6a4 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -283,7 +283,7 @@ class WalletTest(BitcoinTestFramework):
sync_blocks(self.nodes[0:3])
node_2_bal += 2
- #tx should be added to balance because after restarting the nodes tx should be broadcastet
+ #tx should be added to balance because after restarting the nodes tx should be broadcast
assert_equal(self.nodes[2].getbalance(), node_2_bal)
#send a tx with value in a string (PR#6380 +)
@@ -379,9 +379,9 @@ class WalletTest(BitcoinTestFramework):
self.start_node(0, [m, "-limitancestorcount="+str(chainlimit)])
self.start_node(1, [m, "-limitancestorcount="+str(chainlimit)])
self.start_node(2, [m, "-limitancestorcount="+str(chainlimit)])
- while m == '-reindex' and [block_count] * 3 != [self.nodes[i].getblockcount() for i in range(3)]:
+ if m == '-reindex':
# reindex will leave rpc warm up "early"; Wait for it to finish
- time.sleep(0.1)
+ wait_until(lambda: [block_count] * 3 == [self.nodes[i].getblockcount() for i in range(3)])
assert_equal(balance_nodes, [self.nodes[i].getbalance() for i in range(3)])
# Exercise listsinceblock with the last two blocks
diff --git a/test/functional/wallet_hd.py b/test/functional/wallet_hd.py
index 91f77dd5ba..eb6747c6f4 100755
--- a/test/functional/wallet_hd.py
+++ b/test/functional/wallet_hd.py
@@ -4,13 +4,15 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test Hierarchical Deterministic wallet function."""
+import os
+import shutil
+
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
connect_nodes_bi,
)
-import shutil
-import os
+
class WalletHDTest(BitcoinTestFramework):
def set_test_params(self):
@@ -18,12 +20,10 @@ class WalletHDTest(BitcoinTestFramework):
self.num_nodes = 2
self.extra_args = [[], ['-keypool=0']]
- def run_test (self):
- tmpdir = self.options.tmpdir
-
+ def run_test(self):
# Make sure can't switch off usehd after wallet creation
self.stop_node(1)
- self.assert_start_raises_init_error(1, ['-usehd=0'], 'already existing HD wallet')
+ self.nodes[1].assert_start_raises_init_error(['-usehd=0'], "Error: Error loading : You can't disable HD on an already existing HD wallet")
self.start_node(1)
connect_nodes_bi(self.nodes, 0, 1)
@@ -41,8 +41,8 @@ class WalletHDTest(BitcoinTestFramework):
self.nodes[1].importprivkey(self.nodes[0].dumpprivkey(non_hd_add))
# This should be enough to keep the master key and the non-HD key
- self.nodes[1].backupwallet(tmpdir + "/hd.bak")
- #self.nodes[1].dumpwallet(tmpdir + "/hd.dump")
+ self.nodes[1].backupwallet(os.path.join(self.nodes[1].datadir, "hd.bak"))
+ #self.nodes[1].dumpwallet(os.path.join(self.nodes[1].datadir, "hd.dump"))
# Derive some HD addresses and remember the last
# Also send funds to each add
@@ -71,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(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"))
+ shutil.rmtree(os.path.join(self.nodes[1].datadir, "regtest", "blocks"))
+ shutil.rmtree(os.path.join(self.nodes[1].datadir, "regtest", "chainstate"))
+ shutil.copyfile(os.path.join(self.nodes[1].datadir, "hd.bak"), os.path.join(self.nodes[1].datadir, "regtest", "wallets", "wallet.dat"))
self.start_node(1)
# Assert that derivation is deterministic
@@ -94,9 +94,9 @@ class WalletHDTest(BitcoinTestFramework):
# 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"))
+ shutil.rmtree(os.path.join(self.nodes[1].datadir, "regtest", "blocks"))
+ shutil.rmtree(os.path.join(self.nodes[1].datadir, "regtest", "chainstate"))
+ shutil.copyfile(os.path.join(self.nodes[1].datadir, "hd.bak"), os.path.join(self.nodes[1].datadir, "regtest", "wallets", "wallet.dat"))
self.start_node(1, extra_args=self.extra_args[1])
connect_nodes_bi(self.nodes, 0, 1)
self.sync_all()
diff --git a/test/functional/wallet_import_rescan.py b/test/functional/wallet_import_rescan.py
index bfd4638481..b66e9b5d91 100755
--- a/test/functional/wallet_import_rescan.py
+++ b/test/functional/wallet_import_rescan.py
@@ -78,7 +78,7 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")):
if txid is not None:
tx, = [tx for tx in txs if tx["txid"] == txid]
- assert_equal(tx["account"], self.label)
+ assert_equal(tx["label"], self.label)
assert_equal(tx["address"], self.address["address"])
assert_equal(tx["amount"], amount)
assert_equal(tx["category"], "receive")
diff --git a/test/functional/wallet_keypool_topup.py b/test/functional/wallet_keypool_topup.py
index e7b76dfaf2..30a0c9a760 100755
--- a/test/functional/wallet_keypool_topup.py
+++ b/test/functional/wallet_keypool_topup.py
@@ -10,6 +10,7 @@ Two nodes. Node1 is under test. Node0 is providing transactions and generating b
- Generate 110 keys (enough to drain the keypool). Store key 90 (in the initial keypool) and key 110 (beyond the initial keypool). Send funds to key 90 and key 110.
- Stop node1, clear the datadir, move wallet file back into the datadir and restart node1.
- connect node1 to node0. Verify that they sync and node1 receives its funds."""
+import os
import shutil
from test_framework.test_framework import BitcoinTestFramework
@@ -19,6 +20,7 @@ from test_framework.util import (
sync_blocks,
)
+
class KeypoolRestoreTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
@@ -26,26 +28,23 @@ class KeypoolRestoreTest(BitcoinTestFramework):
self.extra_args = [[], ['-keypool=100', '-keypoolmin=20']]
def run_test(self):
- self.tmpdir = self.options.tmpdir
+ wallet_path = os.path.join(self.nodes[1].datadir, "regtest", "wallets", "wallet.dat")
+ wallet_backup_path = os.path.join(self.nodes[1].datadir, "wallet.bak")
self.nodes[0].generate(101)
self.log.info("Make backup of wallet")
-
self.stop_node(1)
-
- shutil.copyfile(self.tmpdir + "/node1/regtest/wallets/wallet.dat", self.tmpdir + "/wallet.bak")
+ shutil.copyfile(wallet_path, wallet_backup_path)
self.start_node(1, self.extra_args[1])
connect_nodes_bi(self.nodes, 0, 1)
self.log.info("Generate keys for wallet")
-
for _ in range(90):
addr_oldpool = self.nodes[1].getnewaddress()
for _ in range(20):
addr_extpool = self.nodes[1].getnewaddress()
self.log.info("Send funds to wallet")
-
self.nodes[0].sendtoaddress(addr_oldpool, 10)
self.nodes[0].generate(1)
self.nodes[0].sendtoaddress(addr_extpool, 5)
@@ -53,22 +52,18 @@ class KeypoolRestoreTest(BitcoinTestFramework):
sync_blocks(self.nodes)
self.log.info("Restart node with wallet backup")
-
self.stop_node(1)
-
- shutil.copyfile(self.tmpdir + "/wallet.bak", self.tmpdir + "/node1/regtest/wallets/wallet.dat")
-
- self.log.info("Verify keypool is restored and balance is correct")
-
+ shutil.copyfile(wallet_backup_path, wallet_path)
self.start_node(1, self.extra_args[1])
connect_nodes_bi(self.nodes, 0, 1)
self.sync_all()
+ self.log.info("Verify keypool is restored and balance is correct")
assert_equal(self.nodes[1].getbalance(), 15)
assert_equal(self.nodes[1].listtransactions()[0]['category'], "receive")
-
# Check that we have marked all keys up to the used keypool key as used
assert_equal(self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress())['hdkeypath'], "m/0'/0'/110'")
+
if __name__ == '__main__':
KeypoolRestoreTest().main()
diff --git a/test/functional/wallet_labels.py b/test/functional/wallet_labels.py
new file mode 100755
index 0000000000..b2695e681f
--- /dev/null
+++ b/test/functional/wallet_labels.py
@@ -0,0 +1,206 @@
+#!/usr/bin/env python3
+# 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 label RPCs.
+
+RPCs tested are:
+ - getlabeladdress
+ - getaddressesbyaccount
+ - listaddressgroupings
+ - setlabel
+ - sendfrom (with account arguments)
+ - move (with account arguments)
+"""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+
+class WalletLabelsTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 1
+ self.extra_args = [[]]
+
+ def run_test(self):
+ node = self.nodes[0]
+ # Check that there's no UTXO on any of the nodes
+ assert_equal(len(node.listunspent()), 0)
+
+ # Note each time we call generate, all generated coins go into
+ # the same address, so we call twice to get two addresses w/50 each
+ node.generate(1)
+ node.generate(101)
+ assert_equal(node.getbalance(), 100)
+
+ # there should be 2 address groups
+ # each with 1 address with a balance of 50 Bitcoins
+ address_groups = node.listaddressgroupings()
+ assert_equal(len(address_groups), 2)
+ # the addresses aren't linked now, but will be after we send to the
+ # common address
+ linked_addresses = set()
+ for address_group in address_groups:
+ assert_equal(len(address_group), 1)
+ assert_equal(len(address_group[0]), 2)
+ assert_equal(address_group[0][1], 50)
+ linked_addresses.add(address_group[0][0])
+
+ # send 50 from each address to a third address not in this wallet
+ # There's some fee that will come back to us when the miner reward
+ # matures.
+ common_address = "msf4WtN1YQKXvNtvdFYt9JBnUD2FB41kjr"
+ txid = node.sendmany(
+ fromaccount="",
+ amounts={common_address: 100},
+ subtractfeefrom=[common_address],
+ minconf=1,
+ )
+ tx_details = node.gettransaction(txid)
+ fee = -tx_details['details'][0]['fee']
+ # there should be 1 address group, with the previously
+ # unlinked addresses now linked (they both have 0 balance)
+ address_groups = node.listaddressgroupings()
+ assert_equal(len(address_groups), 1)
+ assert_equal(len(address_groups[0]), 2)
+ assert_equal(set([a[0] for a in address_groups[0]]), linked_addresses)
+ assert_equal([a[1] for a in address_groups[0]], [0, 0])
+
+ node.generate(1)
+
+ # we want to reset so that the "" label has what's expected.
+ # 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)
+ amount_to_send = 1.0
+
+ # Create labels and make sure subsequent label API calls
+ # recognize the label/address associations.
+ labels = [Label(name) for name in ("a", "b", "c", "d", "e")]
+ for label in labels:
+ label.add_receive_address(node.getlabeladdress(label.name))
+ label.verify(node)
+
+ # Send a transaction to each label, and make sure this forces
+ # getlabeladdress to generate a new receiving address.
+ for label in labels:
+ node.sendtoaddress(label.receive_address, amount_to_send)
+ label.add_receive_address(node.getlabeladdress(label.name))
+ label.verify(node)
+
+ # Check the amounts received.
+ node.generate(1)
+ for label in labels:
+ assert_equal(
+ node.getreceivedbyaddress(label.addresses[0]), amount_to_send)
+ assert_equal(node.getreceivedbylabel(label.name), amount_to_send)
+
+ # Check that sendfrom label reduces listaccounts balances.
+ for i, label in enumerate(labels):
+ to_label = labels[(i+1) % len(labels)]
+ node.sendfrom(label.name, to_label.receive_address, amount_to_send)
+ node.generate(1)
+ for label in labels:
+ label.add_receive_address(node.getlabeladdress(label.name))
+ label.verify(node)
+ assert_equal(node.getreceivedbylabel(label.name), 2)
+ node.move(label.name, "", node.getbalance(label.name))
+ label.verify(node)
+ node.generate(101)
+ expected_account_balances = {"": 5200}
+ for label in labels:
+ expected_account_balances[label.name] = 0
+ assert_equal(node.listaccounts(), expected_account_balances)
+ assert_equal(node.getbalance(""), 5200)
+
+ # Check that setlabel can assign a label to a new unused address.
+ for label in labels:
+ address = node.getlabeladdress("")
+ node.setlabel(address, label.name)
+ label.add_address(address)
+ label.verify(node)
+ assert(address not in node.getaddressesbyaccount(""))
+
+ # Check that addmultisigaddress can assign labels.
+ for label in labels:
+ addresses = []
+ for x in range(10):
+ addresses.append(node.getnewaddress())
+ multisig_address = node.addmultisigaddress(5, addresses, label.name)['address']
+ label.add_address(multisig_address)
+ label.verify(node)
+ node.sendfrom("", multisig_address, 50)
+ node.generate(101)
+ for label in labels:
+ assert_equal(node.getbalance(label.name), 50)
+
+ # Check that setlabel can change the label of an address from a
+ # different label.
+ change_label(node, labels[0].addresses[0], labels[0], labels[1])
+
+ # Check that setlabel can change the label of an address which
+ # is the receiving address of a different label.
+ change_label(node, labels[0].receive_address, labels[0], labels[1])
+
+ # Check that setlabel can set the label of an address already
+ # in the label. This is a no-op.
+ change_label(node, labels[2].addresses[0], labels[2], labels[2])
+
+ # Check that setlabel can set the label of an address which is
+ # already the receiving address of the label. It would probably make
+ # sense for this to be a no-op, but right now it resets the receiving
+ # address, causing getlabeladdress to return a brand new address.
+ change_label(node, labels[2].receive_address, labels[2], labels[2])
+
+class Label:
+ def __init__(self, name):
+ # Label name
+ self.name = name
+ # Current receiving address associated with this label.
+ self.receive_address = None
+ # List of all addresses assigned with this label
+ 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.getlabeladdress(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_label(node, address, old_label, new_label):
+ assert_equal(address in old_label.addresses, True)
+ node.setlabel(address, new_label.name)
+
+ old_label.addresses.remove(address)
+ new_label.add_address(address)
+
+ # Calling setlabel on an address which was previously the receiving
+ # address of a different label should reset the receiving address of
+ # the old label, causing getlabeladdress to return a brand new
+ # address.
+ if address == old_label.receive_address:
+ new_address = node.getlabeladdress(old_label.name)
+ assert_equal(new_address not in old_label.addresses, True)
+ assert_equal(new_address not in new_label.addresses, True)
+ old_label.add_receive_address(new_address)
+
+ old_label.verify(node)
+ new_label.verify(node)
+
+
+if __name__ == '__main__':
+ WalletLabelsTest().main()
diff --git a/test/functional/wallet_listreceivedby.py b/test/functional/wallet_listreceivedby.py
index 01c9899c71..a4754852ed 100755
--- a/test/functional/wallet_listreceivedby.py
+++ b/test/functional/wallet_listreceivedby.py
@@ -36,11 +36,11 @@ class ReceivedByTest(BitcoinTestFramework):
self.sync_all()
assert_array_result(self.nodes[1].listreceivedbyaddress(),
{"address": addr},
- {"address": addr, "account": "", "amount": Decimal("0.1"), "confirmations": 10, "txids": [txid, ]})
+ {"address": addr, "label": "", "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, ]})
+ {"address": addr, "label": "", "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)
@@ -48,11 +48,11 @@ class ReceivedByTest(BitcoinTestFramework):
empty_addr = self.nodes[1].getnewaddress()
assert_array_result(self.nodes[1].listreceivedbyaddress(0, True),
{"address": empty_addr},
- {"address": empty_addr, "account": "", "amount": 0, "confirmations": 0, "txids": []})
+ {"address": empty_addr, "label": "", "amount": 0, "confirmations": 0, "txids": []})
#Test Address filtering
#Only on addr
- expected = {"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]}
+ expected = {"address":addr, "label":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]}
res = self.nodes[1].listreceivedbyaddress(minconf=0, include_empty=True, include_watchonly=True, address_filter=addr)
assert_array_result(res, {"address":addr}, expected)
assert_equal(len(res), 1)
@@ -66,12 +66,12 @@ class ReceivedByTest(BitcoinTestFramework):
self.nodes[0].generate(1)
self.sync_all()
#Same test as above should still pass
- expected = {"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":11, "txids":[txid,]}
+ expected = {"address":addr, "label":"", "amount":Decimal("0.1"), "confirmations":11, "txids":[txid,]}
res = self.nodes[1].listreceivedbyaddress(0, True, True, addr)
assert_array_result(res, {"address":addr}, expected)
assert_equal(len(res), 1)
#Same test as above but with other_addr should still pass
- expected = {"address":other_addr, "account":"", "amount":Decimal("0.1"), "confirmations":1, "txids":[txid2,]}
+ expected = {"address":other_addr, "label":"", "amount":Decimal("0.1"), "confirmations":1, "txids":[txid2,]}
res = self.nodes[1].listreceivedbyaddress(0, True, True, other_addr)
assert_array_result(res, {"address":other_addr}, expected)
assert_equal(len(res), 1)
@@ -108,46 +108,46 @@ class ReceivedByTest(BitcoinTestFramework):
# 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")
+ self.log.info("listreceivedbylabel + getreceivedbylabel Test")
# set pre-state
addrArr = self.nodes[1].getnewaddress()
- account = self.nodes[1].getaccount(addrArr)
- 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)
+ label = self.nodes[1].getaccount(addrArr)
+ received_by_label_json = [r for r in self.nodes[1].listreceivedbylabel() if r["label"] == label][0]
+ balance_by_label = self.nodes[1].getreceivedbylabel(label)
txid = self.nodes[0].sendtoaddress(addr, 0.1)
self.sync_all()
- # listreceivedbyaccount should return received_by_account_json because of 0 confirmations
- assert_array_result(self.nodes[1].listreceivedbyaccount(),
- {"account": account},
- received_by_account_json)
+ # listreceivedbylabel should return received_by_label_json because of 0 confirmations
+ assert_array_result(self.nodes[1].listreceivedbylabel(),
+ {"label": label},
+ received_by_label_json)
# getreceivedbyaddress should return same balance because of 0 confirmations
- balance = self.nodes[1].getreceivedbyaccount(account)
- assert_equal(balance, balance_by_account)
+ balance = self.nodes[1].getreceivedbylabel(label)
+ assert_equal(balance, balance_by_label)
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"))})
+ # listreceivedbylabel should return updated received list
+ assert_array_result(self.nodes[1].listreceivedbylabel(),
+ {"label": label},
+ {"label": received_by_label_json["label"], "amount": (received_by_label_json["amount"] + Decimal("0.1"))})
- # getreceivedbyaddress should return updates balance
- balance = self.nodes[1].getreceivedbyaccount(account)
- assert_equal(balance, balance_by_account + Decimal("0.1"))
+ # getreceivedbylabel should return updated receive total
+ balance = self.nodes[1].getreceivedbylabel(label)
+ assert_equal(balance, balance_by_label + Decimal("0.1"))
- # Create a new account named "mynewaccount" that has a 0 balance
- self.nodes[1].getaccountaddress("mynewaccount")
- received_by_account_json = [r for r in self.nodes[1].listreceivedbyaccount(0, True) if r["account"] == "mynewaccount"][0]
+ # Create a new label named "mynewlabel" that has a 0 balance
+ self.nodes[1].getlabeladdress("mynewlabel")
+ received_by_label_json = [r for r in self.nodes[1].listreceivedbylabel(0, True) if r["label"] == "mynewlabel"][0]
- # Test includeempty of listreceivedbyaccount
- assert_equal(received_by_account_json["amount"], Decimal("0.0"))
+ # Test includeempty of listreceivedbylabel
+ assert_equal(received_by_label_json["amount"], Decimal("0.0"))
- # Test getreceivedbyaccount for 0 amount accounts
- balance = self.nodes[1].getreceivedbyaccount("mynewaccount")
+ # Test getreceivedbylabel for 0 amount labels
+ balance = self.nodes[1].getreceivedbylabel("mynewlabel")
assert_equal(balance, Decimal("0.0"))
if __name__ == '__main__':
diff --git a/test/functional/wallet_listsinceblock.py b/test/functional/wallet_listsinceblock.py
index 25e2716661..0f2434ff0d 100755
--- a/test/functional/wallet_listsinceblock.py
+++ b/test/functional/wallet_listsinceblock.py
@@ -211,7 +211,7 @@ class ListSinceBlockTest (BitcoinTestFramework):
1. tx1 is listed in listsinceblock.
2. It is included in 'removed' as it was removed, even though it is now
present in a different block.
- 3. It is listed with a confirmations count of 2 (bb3, bb4), not
+ 3. It is listed with a confirmation count of 2 (bb3, bb4), not
3 (aa1, aa2, aa3).
'''
diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py
index 378c06ee59..0285263ef9 100755
--- a/test/functional/wallet_multiwallet.py
+++ b/test/functional/wallet_multiwallet.py
@@ -7,10 +7,15 @@
Verify that a bitcoind node can load multiple wallet files
"""
import os
+import re
import shutil
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_rpc_error
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+)
+
class MultiWalletTest(BitcoinTestFramework):
def set_test_params(self):
@@ -60,29 +65,31 @@ class MultiWalletTest(BitcoinTestFramework):
assert_equal(os.path.isfile(wallet_dir(wallet_name)), True)
# should not initialize if wallet path can't be created
- self.assert_start_raises_init_error(0, ['-wallet=wallet.dat/bad'], 'Not a directory')
+ exp_stderr = "boost::filesystem::create_directory: (The system cannot find the path specified|Not a directory):"
+ self.nodes[0].assert_start_raises_init_error(['-wallet=wallet.dat/bad'], exp_stderr, partial_match=True)
- self.assert_start_raises_init_error(0, ['-walletdir=wallets'], 'Error: Specified -walletdir "wallets" does not exist')
- self.assert_start_raises_init_error(0, ['-walletdir=wallets'], 'Error: Specified -walletdir "wallets" is a relative path', cwd=data_dir())
- self.assert_start_raises_init_error(0, ['-walletdir=debug.log'], 'Error: Specified -walletdir "debug.log" is not a directory', cwd=data_dir())
+ self.nodes[0].assert_start_raises_init_error(['-walletdir=wallets'], 'Error: Specified -walletdir "wallets" does not exist')
+ self.nodes[0].assert_start_raises_init_error(['-walletdir=wallets'], 'Error: Specified -walletdir "wallets" is a relative path', cwd=data_dir())
+ self.nodes[0].assert_start_raises_init_error(['-walletdir=debug.log'], 'Error: Specified -walletdir "debug.log" is not a directory', cwd=data_dir())
# should not initialize if there are duplicate wallets
- self.assert_start_raises_init_error(0, ['-wallet=w1', '-wallet=w1'], 'Error loading wallet w1. Duplicate -wallet filename specified.')
+ self.nodes[0].assert_start_raises_init_error(['-wallet=w1', '-wallet=w1'], 'Error: Error loading wallet w1. Duplicate -wallet filename specified.')
# should not initialize if one wallet is a copy of another
shutil.copyfile(wallet_dir('w8'), wallet_dir('w8_copy'))
- self.assert_start_raises_init_error(0, ['-wallet=w8', '-wallet=w8_copy'], 'duplicates fileid')
+ exp_stderr = "CDB: Can't open database w8_copy \(duplicates fileid \w+ from w8\)"
+ self.nodes[0].assert_start_raises_init_error(['-wallet=w8', '-wallet=w8_copy'], exp_stderr, partial_match=True)
# should not initialize if wallet file is a symlink
os.symlink('w8', wallet_dir('w8_symlink'))
- self.assert_start_raises_init_error(0, ['-wallet=w8_symlink'], 'Invalid -wallet path')
+ self.nodes[0].assert_start_raises_init_error(['-wallet=w8_symlink'], 'Error: Invalid -wallet path \'w8_symlink\'\. .*')
# 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')
+ self.nodes[0].assert_start_raises_init_error(['-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')
+ self.nodes[0].assert_start_raises_init_error(['-walletdir=' + not_a_dir], 'Error: Specified -walletdir "' + re.escape(not_a_dir) + '" is not a directory')
# if wallets/ doesn't exist, datadir should be the default wallet dir
wallet_dir2 = data_dir('walletdir')
@@ -102,8 +109,9 @@ class MultiWalletTest(BitcoinTestFramework):
competing_wallet_dir = os.path.join(self.options.tmpdir, 'competing_walletdir')
os.mkdir(competing_wallet_dir)
- self.restart_node(0, ['-walletdir='+competing_wallet_dir])
- self.assert_start_raises_init_error(1, ['-walletdir='+competing_wallet_dir], 'Error initializing wallet database environment')
+ self.restart_node(0, ['-walletdir=' + competing_wallet_dir])
+ exp_stderr = "Error: Error initializing wallet database environment \"\S+competing_walletdir\"!"
+ self.nodes[1].assert_start_raises_init_error(['-walletdir=' + competing_wallet_dir], exp_stderr, partial_match=True)
self.restart_node(0, extra_args)