aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rwxr-xr-xtest/functional/abandonconflict.py9
-rwxr-xr-xtest/functional/p2p-segwit.py21
-rw-r--r--test/functional/test_framework/authproxy.py7
-rwxr-xr-xtest/functional/test_framework/mininode.py7
-rwxr-xr-xtest/functional/test_framework/test_framework.py213
-rw-r--r--test/functional/test_framework/util.py81
6 files changed, 192 insertions, 146 deletions
diff --git a/test/functional/abandonconflict.py b/test/functional/abandonconflict.py
index 0439d168de..9748757641 100755
--- a/test/functional/abandonconflict.py
+++ b/test/functional/abandonconflict.py
@@ -10,10 +10,8 @@
which are not included in a block and are not currently in the mempool. It has
no effect on transactions which are already conflicted or abandoned.
"""
-
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
-import urllib.parse
class AbandonConflictTest(BitcoinTestFramework):
def __init__(self):
@@ -37,8 +35,8 @@ class AbandonConflictTest(BitcoinTestFramework):
assert(balance - newbalance < Decimal("0.001")) #no more than fees lost
balance = newbalance
- url = urllib.parse.urlparse(self.nodes[1].url)
- self.nodes[0].disconnectnode(url.hostname+":"+str(p2p_port(1)))
+ # Disconnect nodes so node0's transactions don't get into node1's mempool
+ disconnect_nodes(self.nodes[0], 1)
# Identify the 10btc outputs
nA = next(i for i, vout in enumerate(self.nodes[0].getrawtransaction(txA, 1)["vout"]) if vout["value"] == Decimal("10"))
@@ -78,8 +76,9 @@ class AbandonConflictTest(BitcoinTestFramework):
stop_node(self.nodes[0],0)
self.nodes[0]=start_node(0, self.options.tmpdir, ["-minrelaytxfee=0.0001"])
- # Verify txs no longer in mempool
+ # Verify txs no longer in either node's mempool
assert_equal(len(self.nodes[0].getrawmempool()), 0)
+ assert_equal(len(self.nodes[1].getrawmempool()), 0)
# Not in mempool txs from self should only reduce balance
# inputs are still spent, but change not received
diff --git a/test/functional/p2p-segwit.py b/test/functional/p2p-segwit.py
index 335777b2ab..24d4d37c42 100755
--- a/test/functional/p2p-segwit.py
+++ b/test/functional/p2p-segwit.py
@@ -61,16 +61,6 @@ class TestNode(NodeConnCB):
self.send_message(msg)
self.wait_for_getdata()
- def announce_block(self, block, use_header):
- with mininode_lock:
- self.last_message.pop("getdata", None)
- if use_header:
- msg = msg_headers()
- msg.headers = [ CBlockHeader(block) ]
- self.send_message(msg)
- else:
- self.send_message(msg_inv(inv=[CInv(2, block.sha256)]))
-
def request_block(self, blockhash, inv_type, timeout=60):
with mininode_lock:
self.last_message.pop("block", None)
@@ -926,9 +916,9 @@ class SegWitTest(BitcoinTestFramework):
tx3.wit.vtxinwit[0].scriptWitness.stack = [ witness_program ]
# Also check that old_node gets a tx announcement, even though this is
# a witness transaction.
- self.old_node.wait_for_inv(CInv(1, tx2.sha256)) # wait until tx2 was inv'ed
+ self.old_node.wait_for_inv([CInv(1, tx2.sha256)]) # wait until tx2 was inv'ed
self.test_node.test_transaction_acceptance(tx3, with_witness=True, accepted=True)
- self.old_node.wait_for_inv(CInv(1, tx3.sha256))
+ self.old_node.wait_for_inv([CInv(1, tx3.sha256)])
# Test that getrawtransaction returns correct witness information
# hash, size, vsize
@@ -1029,13 +1019,18 @@ class SegWitTest(BitcoinTestFramework):
block4 = self.build_next_block(nVersion=4)
block4.solve()
self.old_node.getdataset = set()
+
# Blocks can be requested via direct-fetch (immediately upon processing the announcement)
# or via parallel download (with an indeterminate delay from processing the announcement)
# so to test that a block is NOT requested, we could guess a time period to sleep for,
# and then check. We can avoid the sleep() by taking advantage of transaction getdata's
# being processed after block getdata's, and announce a transaction as well,
# and then check to see if that particular getdata has been received.
- self.old_node.announce_block(block4, use_header=False)
+ # Since 0.14, inv's will only be responded to with a getheaders, so send a header
+ # to announce this block.
+ msg = msg_headers()
+ msg.headers = [ CBlockHeader(block4) ]
+ self.old_node.send_message(msg)
self.old_node.announce_tx_and_wait_for_getdata(block4.vtx[0])
assert(block4.sha256 not in self.old_node.getdataset)
diff --git a/test/functional/test_framework/authproxy.py b/test/functional/test_framework/authproxy.py
index 9ab3094b06..dfcc524313 100644
--- a/test/functional/test_framework/authproxy.py
+++ b/test/functional/test_framework/authproxy.py
@@ -42,6 +42,7 @@ import decimal
import json
import logging
import socket
+import time
try:
import urllib.parse as urlparse
except ImportError:
@@ -163,6 +164,7 @@ class AuthServiceProxy(object):
return self._request('POST', self.__url.path, postdata.encode('utf-8'))
def _get_response(self):
+ req_start_time = time.time()
try:
http_response = self.__conn.getresponse()
except socket.timeout as e:
@@ -183,8 +185,9 @@ class AuthServiceProxy(object):
responsedata = http_response.read().decode('utf8')
response = json.loads(responsedata, parse_float=decimal.Decimal)
+ elapsed = time.time() - req_start_time
if "error" in response and response["error"] is None:
- log.debug("<-%s- %s"%(response["id"], json.dumps(response["result"], default=EncodeDecimal, ensure_ascii=self.ensure_ascii)))
+ log.debug("<-%s- [%.6f] %s"%(response["id"], elapsed, json.dumps(response["result"], default=EncodeDecimal, ensure_ascii=self.ensure_ascii)))
else:
- log.debug("<-- "+responsedata)
+ log.debug("<-- [%.6f] %s"%(elapsed,responsedata))
return response
diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py
index 03db0d1092..70bba566c7 100755
--- a/test/functional/test_framework/mininode.py
+++ b/test/functional/test_framework/mininode.py
@@ -1604,7 +1604,12 @@ class NodeConnCB(object):
assert wait_until(test_function, timeout=timeout)
def wait_for_inv(self, expected_inv, timeout=60):
- test_function = lambda: self.last_message.get("inv") and self.last_message["inv"] != expected_inv
+ """Waits for an INV message and checks that the first inv object in the message was as expected."""
+ if len(expected_inv) > 1:
+ raise NotImplementedError("wait_for_inv() will only verify the first inv object")
+ test_function = lambda: self.last_message.get("inv") and \
+ self.last_message["inv"].inv[0].type == expected_inv[0].type and \
+ self.last_message["inv"].inv[0].hash == expected_inv[0].hash
assert wait_until(test_function, timeout=timeout)
def wait_for_verack(self, timeout=60):
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 3832f04ecd..8d8139e4e4 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -8,28 +8,55 @@ from collections import deque
import logging
import optparse
import os
-import sys
import shutil
+import subprocess
+import sys
import tempfile
import time
from .util import (
- initialize_chain,
- start_nodes,
+ PortSeed,
+ MAX_NODES,
+ bitcoind_processes,
+ check_json_precision,
connect_nodes_bi,
+ disable_mocktime,
disconnect_nodes,
+ enable_coverage,
+ enable_mocktime,
+ get_mocktime,
+ get_rpc_proxy,
+ initialize_datadir,
+ log_filename,
+ p2p_port,
+ rpc_url,
+ set_node_times,
+ start_node,
+ start_nodes,
+ stop_node,
+ stop_nodes,
sync_blocks,
sync_mempools,
- stop_nodes,
- stop_node,
- enable_coverage,
- check_json_precision,
- initialize_chain_clean,
- PortSeed,
+ wait_for_bitcoind_start,
)
from .authproxy import JSONRPCException
class BitcoinTestFramework(object):
+ """Base class for a bitcoin test script.
+
+ Individual bitcoin test scripts should subclass this class and override the following methods:
+
+ - __init__()
+ - add_options()
+ - setup_chain()
+ - setup_network()
+ - run_test()
+
+ The main() method should not be overridden.
+
+ This class also contains various public and private helper methods."""
+
+ # Methods to override in subclass test scripts.
TEST_EXIT_PASSED = 0
TEST_EXIT_FAILED = 1
@@ -40,27 +67,15 @@ class BitcoinTestFramework(object):
self.setup_clean_chain = False
self.nodes = None
- def run_test(self):
- raise NotImplementedError
-
def add_options(self, parser):
pass
def setup_chain(self):
self.log.info("Initializing test directory "+self.options.tmpdir)
if self.setup_clean_chain:
- initialize_chain_clean(self.options.tmpdir, self.num_nodes)
+ self._initialize_chain_clean(self.options.tmpdir, self.num_nodes)
else:
- initialize_chain(self.options.tmpdir, self.num_nodes, self.options.cachedir)
-
- def stop_node(self, num_node):
- stop_node(self.nodes[num_node], num_node)
-
- def setup_nodes(self):
- extra_args = None
- if hasattr(self, "extra_args"):
- extra_args = self.extra_args
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args)
+ self._initialize_chain(self.options.tmpdir, self.num_nodes, self.options.cachedir)
def setup_network(self):
self.setup_nodes()
@@ -72,27 +87,16 @@ class BitcoinTestFramework(object):
connect_nodes_bi(self.nodes, i, i + 1)
self.sync_all()
- def split_network(self):
- """
- Split the network of four nodes into nodes 0/1 and 2/3.
- """
- disconnect_nodes(self.nodes[1], 2)
- disconnect_nodes(self.nodes[2], 1)
- self.sync_all([self.nodes[:2], self.nodes[2:]])
-
- def sync_all(self, node_groups=None):
- if not node_groups:
- node_groups = [self.nodes]
+ def setup_nodes(self):
+ extra_args = None
+ if hasattr(self, "extra_args"):
+ extra_args = self.extra_args
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args)
- [sync_blocks(group) for group in node_groups]
- [sync_mempools(group) for group in node_groups]
+ def run_test(self):
+ raise NotImplementedError
- def join_network(self):
- """
- Join the (previously split) network halves together.
- """
- connect_nodes_bi(self.nodes, 1, 2)
- self.sync_all()
+ # Main function. This should not be overridden by the subclass test scripts.
def main(self):
@@ -156,7 +160,7 @@ class BitcoinTestFramework(object):
if not self.options.noshutdown:
self.log.info("Stopping nodes")
- stop_nodes(self.nodes)
+ self.stop_nodes()
else:
self.log.info("Note: bitcoinds were not stopped and may still be running")
@@ -190,6 +194,45 @@ class BitcoinTestFramework(object):
logging.shutdown()
sys.exit(self.TEST_EXIT_FAILED)
+ # Public helper methods. These can be accessed by the subclass test scripts.
+
+ def start_node(self, i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None, stderr=None):
+ return start_node(i, dirname, extra_args, rpchost, timewait, binary, stderr)
+
+ def start_nodes(self, num_nodes, dirname, extra_args=None, rpchost=None, timewait=None, binary=None):
+ return start_nodes(num_nodes, dirname, extra_args, rpchost, timewait, binary)
+
+ def stop_node(self, num_node):
+ stop_node(self.nodes[num_node], num_node)
+
+ def stop_nodes(self):
+ stop_nodes(self.nodes)
+
+ def split_network(self):
+ """
+ Split the network of four nodes into nodes 0/1 and 2/3.
+ """
+ disconnect_nodes(self.nodes[1], 2)
+ disconnect_nodes(self.nodes[2], 1)
+ self.sync_all([self.nodes[:2], self.nodes[2:]])
+
+ def join_network(self):
+ """
+ Join the (previously split) network halves together.
+ """
+ connect_nodes_bi(self.nodes, 1, 2)
+ self.sync_all()
+
+ def sync_all(self, node_groups=None):
+ if not node_groups:
+ node_groups = [self.nodes]
+
+ for group in node_groups:
+ sync_blocks(group)
+ sync_mempools(group)
+
+ # Private helper methods. These should not be accessed by the subclass test scripts.
+
def _start_logging(self):
# Add logger and logging handlers
self.log = logging.getLogger('TestFramework')
@@ -218,6 +261,88 @@ class BitcoinTestFramework(object):
rpc_handler.setLevel(logging.DEBUG)
rpc_logger.addHandler(rpc_handler)
+ def _initialize_chain(self, test_dir, num_nodes, cachedir):
+ """Initialize a pre-mined blockchain for use by the test.
+
+ Create a cache of a 200-block-long chain (with wallet) for MAX_NODES
+ Afterward, create num_nodes copies from the cache."""
+
+ assert num_nodes <= MAX_NODES
+ create_cache = False
+ for i in range(MAX_NODES):
+ if not os.path.isdir(os.path.join(cachedir, 'node' + str(i))):
+ create_cache = True
+ break
+
+ if create_cache:
+ self.log.debug("Creating data directories from cached datadir")
+
+ # find and delete old cache directories if any exist
+ for i in range(MAX_NODES):
+ if os.path.isdir(os.path.join(cachedir, "node" + str(i))):
+ shutil.rmtree(os.path.join(cachedir, "node" + str(i)))
+
+ # Create cache directories, run bitcoinds:
+ for i in range(MAX_NODES):
+ datadir = initialize_datadir(cachedir, i)
+ args = [os.getenv("BITCOIND", "bitcoind"), "-server", "-keypool=1", "-datadir=" + datadir, "-discover=0"]
+ if i > 0:
+ args.append("-connect=127.0.0.1:" + str(p2p_port(0)))
+ bitcoind_processes[i] = subprocess.Popen(args)
+ self.log.debug("initialize_chain: bitcoind started, waiting for RPC to come up")
+ wait_for_bitcoind_start(bitcoind_processes[i], rpc_url(i), i)
+ self.log.debug("initialize_chain: RPC successfully started")
+
+ self.nodes = []
+ for i in range(MAX_NODES):
+ try:
+ self.nodes.append(get_rpc_proxy(rpc_url(i), i))
+ except:
+ self.log.exception("Error connecting to node %d" % i)
+ sys.exit(1)
+
+ # Create a 200-block-long chain; each of the 4 first nodes
+ # gets 25 mature blocks and 25 immature.
+ # Note: To preserve compatibility with older versions of
+ # initialize_chain, only 4 nodes will generate coins.
+ #
+ # blocks are created with timestamps 10 minutes apart
+ # starting from 2010 minutes in the past
+ enable_mocktime()
+ block_time = get_mocktime() - (201 * 10 * 60)
+ for i in range(2):
+ for peer in range(4):
+ for j in range(25):
+ set_node_times(self.nodes, block_time)
+ self.nodes[peer].generate(1)
+ block_time += 10 * 60
+ # Must sync before next peer starts generating blocks
+ sync_blocks(self.nodes)
+
+ # Shut them down, and clean up cache directories:
+ self.stop_nodes()
+ self.nodes = []
+ disable_mocktime()
+ for i in range(MAX_NODES):
+ os.remove(log_filename(cachedir, i, "debug.log"))
+ os.remove(log_filename(cachedir, i, "db.log"))
+ os.remove(log_filename(cachedir, i, "peers.dat"))
+ os.remove(log_filename(cachedir, i, "fee_estimates.dat"))
+
+ for i in range(num_nodes):
+ from_dir = os.path.join(cachedir, "node" + str(i))
+ to_dir = os.path.join(test_dir, "node" + str(i))
+ shutil.copytree(from_dir, to_dir)
+ initialize_datadir(test_dir, i) # Overwrite port/rpcport in bitcoin.conf
+
+ def _initialize_chain_clean(self, test_dir, num_nodes):
+ """Initialize empty blockchain for use by the test.
+
+ Create an empty blockchain and num_nodes wallets.
+ Useful if a test case wants complete control over initialization."""
+ for i in range(num_nodes):
+ initialize_datadir(test_dir, i)
+
# Test framework for doing p2p comparison testing, which sets up some bitcoind
# binaries:
# 1 binary: test binary
@@ -240,7 +365,7 @@ class ComparisonTestFramework(BitcoinTestFramework):
help="bitcoind binary to use for reference nodes (if any)")
def setup_network(self):
- self.nodes = start_nodes(
+ self.nodes = self.start_nodes(
self.num_nodes, self.options.tmpdir,
extra_args=[['-whitelist=127.0.0.1']] * self.num_nodes,
binary=[self.options.testbinary] +
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 9186c3cbe9..2b56fe8d62 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -226,87 +226,6 @@ def wait_for_bitcoind_start(process, url, i):
raise # unknown JSON RPC exception
time.sleep(0.25)
-def initialize_chain(test_dir, num_nodes, cachedir):
- """
- Create a cache of a 200-block-long chain (with wallet) for MAX_NODES
- Afterward, create num_nodes copies from the cache
- """
-
- assert num_nodes <= MAX_NODES
- create_cache = False
- for i in range(MAX_NODES):
- if not os.path.isdir(os.path.join(cachedir, 'node'+str(i))):
- create_cache = True
- break
-
- if create_cache:
- logger.debug("Creating data directories from cached datadir")
-
- #find and delete old cache directories if any exist
- for i in range(MAX_NODES):
- if os.path.isdir(os.path.join(cachedir,"node"+str(i))):
- shutil.rmtree(os.path.join(cachedir,"node"+str(i)))
-
- # Create cache directories, run bitcoinds:
- for i in range(MAX_NODES):
- datadir=initialize_datadir(cachedir, i)
- args = [ os.getenv("BITCOIND", "bitcoind"), "-server", "-keypool=1", "-datadir="+datadir, "-discover=0" ]
- if i > 0:
- args.append("-connect=127.0.0.1:"+str(p2p_port(0)))
- bitcoind_processes[i] = subprocess.Popen(args)
- logger.debug("initialize_chain: bitcoind started, waiting for RPC to come up")
- wait_for_bitcoind_start(bitcoind_processes[i], rpc_url(i), i)
- logger.debug("initialize_chain: RPC successfully started")
-
- rpcs = []
- for i in range(MAX_NODES):
- try:
- rpcs.append(get_rpc_proxy(rpc_url(i), i))
- except:
- sys.stderr.write("Error connecting to "+url+"\n")
- sys.exit(1)
-
- # Create a 200-block-long chain; each of the 4 first nodes
- # gets 25 mature blocks and 25 immature.
- # Note: To preserve compatibility with older versions of
- # initialize_chain, only 4 nodes will generate coins.
- #
- # blocks are created with timestamps 10 minutes apart
- # starting from 2010 minutes in the past
- enable_mocktime()
- block_time = get_mocktime() - (201 * 10 * 60)
- for i in range(2):
- for peer in range(4):
- for j in range(25):
- set_node_times(rpcs, block_time)
- rpcs[peer].generate(1)
- block_time += 10*60
- # Must sync before next peer starts generating blocks
- sync_blocks(rpcs)
-
- # Shut them down, and clean up cache directories:
- stop_nodes(rpcs)
- disable_mocktime()
- for i in range(MAX_NODES):
- os.remove(log_filename(cachedir, i, "debug.log"))
- os.remove(log_filename(cachedir, i, "db.log"))
- os.remove(log_filename(cachedir, i, "peers.dat"))
- os.remove(log_filename(cachedir, i, "fee_estimates.dat"))
-
- for i in range(num_nodes):
- from_dir = os.path.join(cachedir, "node"+str(i))
- to_dir = os.path.join(test_dir, "node"+str(i))
- shutil.copytree(from_dir, to_dir)
- initialize_datadir(test_dir, i) # Overwrite port/rpcport in bitcoin.conf
-
-def initialize_chain_clean(test_dir, num_nodes):
- """
- Create an empty blockchain and num_nodes wallets.
- Useful if a test case wants complete control over initialization.
- """
- for i in range(num_nodes):
- datadir=initialize_datadir(test_dir, i)
-
def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None, stderr=None):
"""