aboutsummaryrefslogtreecommitdiff
path: root/test/functional
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional')
-rwxr-xr-xtest/functional/assumevalid.py106
-rwxr-xr-xtest/functional/bip9-softforks.py10
-rwxr-xr-xtest/functional/bumpfee.py1
-rwxr-xr-xtest/functional/combine_logs.py7
-rwxr-xr-xtest/functional/forknotify.py15
-rwxr-xr-xtest/functional/fundrawtransaction.py40
-rwxr-xr-xtest/functional/importmulti.py2
-rwxr-xr-xtest/functional/keypool.py41
-rwxr-xr-xtest/functional/maxblocksinflight.py6
-rwxr-xr-xtest/functional/maxuploadtarget.py11
-rwxr-xr-xtest/functional/mempool_packages.py12
-rwxr-xr-xtest/functional/net.py54
-rwxr-xr-xtest/functional/p2p-acceptblock.py17
-rwxr-xr-xtest/functional/p2p-compactblocks.py27
-rwxr-xr-xtest/functional/p2p-feefilter.py4
-rwxr-xr-xtest/functional/p2p-leaktests.py2
-rwxr-xr-xtest/functional/p2p-mempool.py11
-rwxr-xr-xtest/functional/p2p-segwit.py24
-rwxr-xr-xtest/functional/p2p-timeouts.py4
-rwxr-xr-xtest/functional/p2p-versionbits-warning.py17
-rwxr-xr-xtest/functional/rpcbind_test.py18
-rwxr-xr-xtest/functional/sendheaders.py4
-rwxr-xr-xtest/functional/test_framework/comptool.py2
-rwxr-xr-xtest/functional/test_framework/mininode.py139
-rwxr-xr-xtest/functional/test_framework/test_framework.py25
-rw-r--r--test/functional/test_framework/util.py2
-rwxr-xr-xtest/functional/test_runner.py145
-rwxr-xr-xtest/functional/wallet-dump.py6
-rwxr-xr-xtest/functional/wallet-hd.py19
29 files changed, 490 insertions, 281 deletions
diff --git a/test/functional/assumevalid.py b/test/functional/assumevalid.py
index c60c8e6d1a..8e301c4379 100755
--- a/test/functional/assumevalid.py
+++ b/test/functional/assumevalid.py
@@ -15,7 +15,7 @@ transactions:
2-101: bury that block with 100 blocks so the coinbase transaction
output can be spent
102: a block containing a transaction spending the coinbase
- transaction output. The transaction has an invalid signature.
+ transaction output. The transaction has an invalid signature.
103-2202: bury the bad block with just over two weeks' worth of blocks
(2100 blocks)
@@ -29,40 +29,31 @@ Start three nodes:
block 200. node2 will reject block 102 since it's assumed valid, but it
isn't buried by at least two weeks' work.
"""
+import time
-from test_framework.mininode import *
-from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import *
-from test_framework.blocktools import create_block, create_coinbase
+from test_framework.blocktools import (create_block, create_coinbase)
from test_framework.key import CECKey
-from test_framework.script import *
-
-class BaseNode(SingleNodeConnCB):
- def __init__(self):
- SingleNodeConnCB.__init__(self)
- self.last_inv = None
- self.last_headers = None
- self.last_block = None
- self.last_getdata = None
- self.block_announced = False
- self.last_getheaders = None
- self.disconnected = False
- self.last_blockhash_announced = None
-
- def on_close(self, conn):
- self.disconnected = True
-
- def wait_for_disconnect(self, timeout=60):
- test_function = lambda: self.disconnected
- assert(wait_until(test_function, timeout=timeout))
- return
+from test_framework.mininode import (CBlockHeader,
+ COutPoint,
+ CTransaction,
+ CTxIn,
+ CTxOut,
+ NetworkThread,
+ NodeConn,
+ NodeConnCB,
+ msg_block,
+ msg_headers)
+from test_framework.script import (CScript, OP_TRUE)
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (start_node, p2p_port, assert_equal)
+class BaseNode(NodeConnCB):
def send_header_for_blocks(self, new_blocks):
headers_message = msg_headers()
- headers_message.headers = [ CBlockHeader(b) for b in new_blocks ]
+ headers_message.headers = [CBlockHeader(b) for b in new_blocks]
self.send_message(headers_message)
-class SendHeadersTest(BitcoinTestFramework):
+class AssumeValidTest(BitcoinTestFramework):
def __init__(self):
super().__init__()
self.setup_clean_chain = True
@@ -72,8 +63,34 @@ class SendHeadersTest(BitcoinTestFramework):
# Start node0. We don't start the other nodes yet since
# we need to pre-mine a block with an invalid transaction
# signature so we can pass in the block hash as assumevalid.
- self.nodes = []
- self.nodes.append(start_node(0, self.options.tmpdir))
+ self.nodes = [start_node(0, self.options.tmpdir)]
+
+ def send_blocks_until_disconnected(self, node):
+ """Keep sending blocks to the node until we're disconnected."""
+ for i in range(len(self.blocks)):
+ try:
+ node.send_message(msg_block(self.blocks[i]))
+ except IOError as e:
+ assert str(e) == 'Not connected, no pushbuf'
+ break
+
+ def assert_blockchain_height(self, node, height):
+ """Wait until the blockchain is no longer advancing and verify it's reached the expected height."""
+ last_height = node.getblock(node.getbestblockhash())['height']
+ timeout = 10
+ while True:
+ time.sleep(0.25)
+ current_height = node.getblock(node.getbestblockhash())['height']
+ if current_height != last_height:
+ last_height = current_height
+ if timeout < 0:
+ assert False, "blockchain too short after timeout: %d" % current_height
+ timeout - 0.25
+ continue
+ elif current_height > height:
+ assert False, "blockchain too long: %d" % current_height
+ elif current_height == height:
+ break
def run_test(self):
@@ -83,7 +100,7 @@ class SendHeadersTest(BitcoinTestFramework):
connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0))
node0.add_connection(connections[0])
- NetworkThread().start() # Start up network handling in another thread
+ NetworkThread().start() # Start up network handling in another thread
node0.wait_for_verack()
# Build the blockchain
@@ -120,7 +137,7 @@ class SendHeadersTest(BitcoinTestFramework):
# Create a transaction spending the coinbase output with an invalid (null) signature
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(self.block1.vtx[0].sha256, 0), scriptSig=b""))
- tx.vout.append(CTxOut(49*100000000, CScript([OP_TRUE])))
+ tx.vout.append(CTxOut(49 * 100000000, CScript([OP_TRUE])))
tx.calc_sha256()
block102 = create_block(self.tip, create_coinbase(height), self.block_time)
@@ -166,25 +183,20 @@ class SendHeadersTest(BitcoinTestFramework):
node1.send_header_for_blocks(self.blocks[2000:])
node2.send_header_for_blocks(self.blocks[0:200])
- # Send 102 blocks to node0. Block 102 will be rejected.
- for i in range(101):
- node0.send_message(msg_block(self.blocks[i]))
- node0.sync_with_ping() # make sure the most recent block is synced
- node0.send_message(msg_block(self.blocks[101]))
- assert_equal(self.nodes[0].getblock(self.nodes[0].getbestblockhash())['height'], 101)
+ # Send blocks to node0. Block 102 will be rejected.
+ self.send_blocks_until_disconnected(node0)
+ self.assert_blockchain_height(self.nodes[0], 101)
- # Send 3102 blocks to node1. All blocks will be accepted.
+ # Send all blocks to node1. All blocks will be accepted.
for i in range(2202):
node1.send_message(msg_block(self.blocks[i]))
- node1.sync_with_ping() # make sure the most recent block is synced
+ # Syncing 2200 blocks can take a while on slow systems. Give it plenty of time to sync.
+ node1.sync_with_ping(120)
assert_equal(self.nodes[1].getblock(self.nodes[1].getbestblockhash())['height'], 2202)
- # Send 102 blocks to node2. Block 102 will be rejected.
- for i in range(101):
- node2.send_message(msg_block(self.blocks[i]))
- node2.sync_with_ping() # make sure the most recent block is synced
- node2.send_message(msg_block(self.blocks[101]))
- assert_equal(self.nodes[2].getblock(self.nodes[2].getbestblockhash())['height'], 101)
+ # Send blocks to node2. Block 102 will be rejected.
+ self.send_blocks_until_disconnected(node2)
+ self.assert_blockchain_height(self.nodes[2], 101)
if __name__ == '__main__':
- SendHeadersTest().main()
+ AssumeValidTest().main()
diff --git a/test/functional/bip9-softforks.py b/test/functional/bip9-softforks.py
index 0dffd06e1a..60d262da81 100755
--- a/test/functional/bip9-softforks.py
+++ b/test/functional/bip9-softforks.py
@@ -200,16 +200,14 @@ class BIP9SoftForksTest(ComparisonTestFramework):
yield TestInstance([[block, False]])
# Restart all
- self.test.block_store.close()
+ self.test.clear_all_connections()
stop_nodes(self.nodes)
- shutil.rmtree(self.options.tmpdir)
+ shutil.rmtree(self.options.tmpdir + "/node0")
self.setup_chain()
self.setup_network()
- self.test.block_store = BlockStore(self.options.tmpdir)
- self.test.clear_all_connections()
self.test.add_all_connections(self.nodes)
- NetworkThread().start() # Start up network handling in another thread
-
+ NetworkThread().start()
+ self.test.test_nodes[0].wait_for_verack()
def get_tests(self):
for test in itertools.chain(
diff --git a/test/functional/bumpfee.py b/test/functional/bumpfee.py
index 172e414188..c51a75cc26 100755
--- a/test/functional/bumpfee.py
+++ b/test/functional/bumpfee.py
@@ -196,7 +196,6 @@ def test_dust_to_fee(rbf_node, dest_address):
def test_settxfee(rbf_node, dest_address):
# check that bumpfee reacts correctly to the use of settxfee (paytxfee)
rbfid = spend_one_input(rbf_node, dest_address)
- rbftx = rbf_node.gettransaction(rbfid)
requested_feerate = Decimal("0.00025000")
rbf_node.settxfee(requested_feerate)
bumped_tx = rbf_node.bumpfee(rbfid)
diff --git a/test/functional/combine_logs.py b/test/functional/combine_logs.py
index 0c2f60172f..3ca74ea35e 100755
--- a/test/functional/combine_logs.py
+++ b/test/functional/combine_logs.py
@@ -6,8 +6,8 @@ to write to an outputfile."""
import argparse
from collections import defaultdict, namedtuple
-import glob
import heapq
+import itertools
import os
import re
import sys
@@ -49,7 +49,10 @@ def read_logs(tmp_dir):
for each of the input log files."""
files = [("test", "%s/test_framework.log" % tmp_dir)]
- for i, logfile in enumerate(glob.glob("%s/node*/regtest/debug.log" % tmp_dir)):
+ for i in itertools.count():
+ logfile = "{}/node{}/regtest/debug.log".format(tmp_dir, i)
+ if not os.path.isfile(logfile):
+ break
files.append(("node%d" % i, logfile))
return heapq.merge(*[get_log_events(source, f) for source, f in files])
diff --git a/test/functional/forknotify.py b/test/functional/forknotify.py
index c2724ba5df..7a365438cc 100755
--- a/test/functional/forknotify.py
+++ b/test/functional/forknotify.py
@@ -3,6 +3,8 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the -alertnotify option."""
+import os
+import time
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
@@ -41,12 +43,19 @@ class ForkNotifyTest(BitcoinTestFramework):
self.nodes[1].generate(1)
self.sync_all()
+ # Give bitcoind 10 seconds to write the alert notification
+ timeout = 10.0
+ while timeout > 0:
+ if os.path.exists(self.alert_filename) and os.path.getsize(self.alert_filename):
+ break
+ time.sleep(0.1)
+ timeout -= 0.1
+ else:
+ assert False, "-alertnotify did not warn of up-version blocks"
+
with open(self.alert_filename, 'r', encoding='utf8') as f:
alert_text = f.read()
- if len(alert_text) == 0:
- raise AssertionError("-alertnotify did not warn of up-version blocks")
-
# Mine more up-version blocks, should not get more alerts:
self.nodes[1].generate(1)
self.sync_all()
diff --git a/test/functional/fundrawtransaction.py b/test/functional/fundrawtransaction.py
index 1dc00f2ba1..b86ea2d877 100755
--- a/test/functional/fundrawtransaction.py
+++ b/test/functional/fundrawtransaction.py
@@ -322,8 +322,8 @@ class RawTransactionsTest(BitcoinTestFramework):
#compare fee of a standard pubkeyhash transaction
inputs = []
outputs = {self.nodes[1].getnewaddress():1.1}
- rawTx = self.nodes[0].createrawtransaction(inputs, outputs)
- fundedTx = self.nodes[0].fundrawtransaction(rawTx)
+ rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
+ fundedTx = self.nodes[0].fundrawtransaction(rawtx)
#create same transaction over sendtoaddress
txId = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1.1)
@@ -338,8 +338,8 @@ class RawTransactionsTest(BitcoinTestFramework):
#compare fee of a standard pubkeyhash transaction with multiple outputs
inputs = []
outputs = {self.nodes[1].getnewaddress():1.1,self.nodes[1].getnewaddress():1.2,self.nodes[1].getnewaddress():0.1,self.nodes[1].getnewaddress():1.3,self.nodes[1].getnewaddress():0.2,self.nodes[1].getnewaddress():0.3}
- rawTx = self.nodes[0].createrawtransaction(inputs, outputs)
- fundedTx = self.nodes[0].fundrawtransaction(rawTx)
+ rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
+ fundedTx = self.nodes[0].fundrawtransaction(rawtx)
#create same transaction over sendtoaddress
txId = self.nodes[0].sendmany("", outputs)
signedFee = self.nodes[0].getrawmempool(True)[txId]['fee']
@@ -364,8 +364,8 @@ class RawTransactionsTest(BitcoinTestFramework):
inputs = []
outputs = {mSigObj:1.1}
- rawTx = self.nodes[0].createrawtransaction(inputs, outputs)
- fundedTx = self.nodes[0].fundrawtransaction(rawTx)
+ rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
+ fundedTx = self.nodes[0].fundrawtransaction(rawtx)
#create same transaction over sendtoaddress
txId = self.nodes[0].sendtoaddress(mSigObj, 1.1)
@@ -397,8 +397,8 @@ class RawTransactionsTest(BitcoinTestFramework):
inputs = []
outputs = {mSigObj:1.1}
- rawTx = self.nodes[0].createrawtransaction(inputs, outputs)
- fundedTx = self.nodes[0].fundrawtransaction(rawTx)
+ rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
+ fundedTx = self.nodes[0].fundrawtransaction(rawtx)
#create same transaction over sendtoaddress
txId = self.nodes[0].sendtoaddress(mSigObj, 1.1)
@@ -432,8 +432,8 @@ class RawTransactionsTest(BitcoinTestFramework):
oldBalance = self.nodes[1].getbalance()
inputs = []
outputs = {self.nodes[1].getnewaddress():1.1}
- rawTx = self.nodes[2].createrawtransaction(inputs, outputs)
- fundedTx = self.nodes[2].fundrawtransaction(rawTx)
+ rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
+ fundedTx = self.nodes[2].fundrawtransaction(rawtx)
signedTx = self.nodes[2].signrawtransaction(fundedTx['hex'])
txId = self.nodes[2].sendrawtransaction(signedTx['hex'])
@@ -467,15 +467,17 @@ class RawTransactionsTest(BitcoinTestFramework):
# drain the keypool
self.nodes[1].getnewaddress()
+ self.nodes[1].getrawchangeaddress()
inputs = []
outputs = {self.nodes[0].getnewaddress():1.1}
- rawTx = self.nodes[1].createrawtransaction(inputs, outputs)
+ rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
# fund a transaction that requires a new key for the change output
# creating the key must be impossible because the wallet is locked
- assert_raises_jsonrpc(-4, "Insufficient funds", self.nodes[1].fundrawtransaction, rawtx)
+ assert_raises_jsonrpc(-4, "Keypool ran out, please call keypoolrefill first", self.nodes[1].fundrawtransaction, rawtx)
#refill the keypool
self.nodes[1].walletpassphrase("test", 100)
+ self.nodes[1].keypoolrefill(8) #need to refill the keypool to get an internal change address
self.nodes[1].walletlock()
assert_raises_jsonrpc(-13, "walletpassphrase", self.nodes[1].sendtoaddress, self.nodes[0].getnewaddress(), 1.2)
@@ -484,8 +486,8 @@ class RawTransactionsTest(BitcoinTestFramework):
inputs = []
outputs = {self.nodes[0].getnewaddress():1.1}
- rawTx = self.nodes[1].createrawtransaction(inputs, outputs)
- fundedTx = self.nodes[1].fundrawtransaction(rawTx)
+ rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
+ fundedTx = self.nodes[1].fundrawtransaction(rawtx)
#now we need to unlock
self.nodes[1].walletpassphrase("test", 600)
@@ -516,8 +518,8 @@ class RawTransactionsTest(BitcoinTestFramework):
#fund a tx with ~20 small inputs
inputs = []
outputs = {self.nodes[0].getnewaddress():0.15,self.nodes[0].getnewaddress():0.04}
- rawTx = self.nodes[1].createrawtransaction(inputs, outputs)
- fundedTx = self.nodes[1].fundrawtransaction(rawTx)
+ rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
+ fundedTx = self.nodes[1].fundrawtransaction(rawtx)
#create same transaction over sendtoaddress
txId = self.nodes[1].sendmany("", outputs)
@@ -548,8 +550,8 @@ class RawTransactionsTest(BitcoinTestFramework):
inputs = []
outputs = {self.nodes[0].getnewaddress():0.15,self.nodes[0].getnewaddress():0.04}
- rawTx = self.nodes[1].createrawtransaction(inputs, outputs)
- fundedTx = self.nodes[1].fundrawtransaction(rawTx)
+ rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
+ fundedTx = self.nodes[1].fundrawtransaction(rawtx)
fundedAndSignedTx = self.nodes[1].signrawtransaction(fundedTx['hex'])
txId = self.nodes[1].sendrawtransaction(fundedAndSignedTx['hex'])
self.sync_all()
@@ -644,7 +646,7 @@ class RawTransactionsTest(BitcoinTestFramework):
if out['value'] > 1.0:
changeaddress += out['scriptPubKey']['addresses'][0]
assert(changeaddress != "")
- nextaddr = self.nodes[3].getnewaddress()
+ nextaddr = self.nodes[3].getrawchangeaddress()
# frt should not have removed the key from the keypool
assert(changeaddress == nextaddr)
diff --git a/test/functional/importmulti.py b/test/functional/importmulti.py
index aa03c6780a..e049302632 100755
--- a/test/functional/importmulti.py
+++ b/test/functional/importmulti.py
@@ -434,7 +434,7 @@ class ImportMultiTest (BitcoinTestFramework):
address_assert = self.nodes[1].validateaddress(watchonly_address)
assert_equal(address_assert['iswatchonly'], True)
assert_equal(address_assert['ismine'], False)
- assert_equal(address_assert['timestamp'], watchonly_timestamp);
+ assert_equal(address_assert['timestamp'], watchonly_timestamp)
# Bad or missing timestamps
self.log.info("Should throw on invalid or missing timestamp values")
diff --git a/test/functional/keypool.py b/test/functional/keypool.py
index cee58563f0..cb9ab688d1 100755
--- a/test/functional/keypool.py
+++ b/test/functional/keypool.py
@@ -27,28 +27,42 @@ class KeyPoolTest(BitcoinTestFramework):
wallet_info = nodes[0].getwalletinfo()
assert(addr_before_encrypting_data['hdmasterkeyid'] != wallet_info['hdmasterkeyid'])
assert(addr_data['hdmasterkeyid'] == wallet_info['hdmasterkeyid'])
-
assert_raises_jsonrpc(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress)
- # put three new keys in the keypool
+ # put six (plus 2) new keys in the keypool (100% external-, +100% internal-keys, 1 in min)
nodes[0].walletpassphrase('test', 12000)
- nodes[0].keypoolrefill(3)
+ nodes[0].keypoolrefill(6)
nodes[0].walletlock()
+ wi = nodes[0].getwalletinfo()
+ assert_equal(wi['keypoolsize_hd_internal'], 6)
+ assert_equal(wi['keypoolsize'], 6)
- # drain the keys
+ # drain the internal keys
+ nodes[0].getrawchangeaddress()
+ nodes[0].getrawchangeaddress()
+ nodes[0].getrawchangeaddress()
+ nodes[0].getrawchangeaddress()
+ nodes[0].getrawchangeaddress()
+ nodes[0].getrawchangeaddress()
addr = set()
- addr.add(nodes[0].getrawchangeaddress())
- addr.add(nodes[0].getrawchangeaddress())
- addr.add(nodes[0].getrawchangeaddress())
- addr.add(nodes[0].getrawchangeaddress())
- # assert that four unique addresses were returned
- assert(len(addr) == 4)
# the next one should fail
assert_raises_jsonrpc(-12, "Keypool ran out", nodes[0].getrawchangeaddress)
+ # drain the external keys
+ addr.add(nodes[0].getnewaddress())
+ addr.add(nodes[0].getnewaddress())
+ addr.add(nodes[0].getnewaddress())
+ addr.add(nodes[0].getnewaddress())
+ addr.add(nodes[0].getnewaddress())
+ addr.add(nodes[0].getnewaddress())
+ assert(len(addr) == 6)
+ # the next one should fail
+ assert_raises_jsonrpc(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress)
+
# refill keypool with three new addresses
nodes[0].walletpassphrase('test', 1)
nodes[0].keypoolrefill(3)
+
# test walletpassphrase timeout
time.sleep(1.1)
assert_equal(nodes[0].getwalletinfo()["unlocked_until"], 0)
@@ -57,9 +71,14 @@ class KeyPoolTest(BitcoinTestFramework):
nodes[0].generate(1)
nodes[0].generate(1)
nodes[0].generate(1)
- nodes[0].generate(1)
assert_raises_jsonrpc(-12, "Keypool ran out", nodes[0].generate, 1)
+ nodes[0].walletpassphrase('test', 100)
+ nodes[0].keypoolrefill(100)
+ wi = nodes[0].getwalletinfo()
+ assert_equal(wi['keypoolsize_hd_internal'], 100)
+ assert_equal(wi['keypoolsize'], 100)
+
def __init__(self):
super().__init__()
self.setup_clean_chain = False
diff --git a/test/functional/maxblocksinflight.py b/test/functional/maxblocksinflight.py
index 2c3766125a..4ef2a35a44 100755
--- a/test/functional/maxblocksinflight.py
+++ b/test/functional/maxblocksinflight.py
@@ -17,7 +17,6 @@ from test_framework.util import *
MAX_REQUESTS = 128
class TestManager(NodeConnCB):
- # set up NodeConnCB callbacks, overriding base class
def on_getdata(self, conn, message):
self.log.debug("got getdata %s" % repr(message))
# Log the requests
@@ -30,11 +29,8 @@ class TestManager(NodeConnCB):
if not self.disconnectOkay:
raise EarlyDisconnectError(0)
- def __init__(self):
- NodeConnCB.__init__(self)
-
def add_new_connection(self, connection):
- self.connection = connection
+ super().add_connection(connection)
self.blockReqCounts = {}
self.disconnectOkay = False
diff --git a/test/functional/maxuploadtarget.py b/test/functional/maxuploadtarget.py
index 40cd85c9ec..9b42bf276c 100755
--- a/test/functional/maxuploadtarget.py
+++ b/test/functional/maxuploadtarget.py
@@ -20,7 +20,7 @@ import time
# p2p messages to a node, generating the messages in the main testing logic.
class TestNode(NodeConnCB):
def __init__(self):
- NodeConnCB.__init__(self)
+ super().__init__()
self.connection = None
self.ping_counter = 1
self.last_pong = msg_pong()
@@ -68,15 +68,6 @@ class TestNode(NodeConnCB):
def on_close(self, conn):
self.peer_disconnected = True
- # Sync up with the node after delivery of a block
- def sync_with_ping(self, timeout=30):
- def received_pong():
- return (self.last_pong.nonce == self.ping_counter)
- self.connection.send_message(msg_ping(nonce=self.ping_counter))
- success = wait_until(received_pong, timeout=timeout)
- self.ping_counter += 1
- return success
-
class MaxUploadTest(BitcoinTestFramework):
def __init__(self):
diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py
index 17e3a9a967..feec8a7fd9 100755
--- a/test/functional/mempool_packages.py
+++ b/test/functional/mempool_packages.py
@@ -101,6 +101,18 @@ class MempoolPackagesTest(BitcoinTestFramework):
assert_equal(mempool[x], v_descendants[x])
assert(chain[0] not in v_descendants.keys())
+ # Check that ancestor modified fees includes fee deltas from
+ # prioritisetransaction
+ self.nodes[0].prioritisetransaction(chain[0], 1000)
+ mempool = self.nodes[0].getrawmempool(True)
+ ancestor_fees = 0
+ for x in chain:
+ ancestor_fees += mempool[x]['fee']
+ assert_equal(mempool[x]['ancestorfees'], ancestor_fees * COIN + 1000)
+
+ # Undo the prioritisetransaction for later tests
+ self.nodes[0].prioritisetransaction(chain[0], -1000)
+
# Check that descendant modified fees includes fee deltas from
# prioritisetransaction
self.nodes[0].prioritisetransaction(chain[-1], 1000)
diff --git a/test/functional/net.py b/test/functional/net.py
new file mode 100755
index 0000000000..e9463c7dc7
--- /dev/null
+++ b/test/functional/net.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python3
+# Copyright (c) 2017 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test RPC calls related to net.
+
+Tests correspond to code in rpc/net.cpp.
+"""
+
+from decimal import Decimal
+import time
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.authproxy import JSONRPCException
+from test_framework.util import (
+ assert_equal,
+ start_nodes,
+ connect_nodes_bi,
+)
+
+
+class NetTest(BitcoinTestFramework):
+ def __init__(self):
+ super().__init__()
+ self.setup_clean_chain = True
+ self.num_nodes = 2
+
+ def setup_network(self):
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
+ connect_nodes_bi(self.nodes, 0, 1)
+ self.is_network_split = False
+ self.sync_all()
+
+ def run_test(self):
+ assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
+ assert_equal(self.nodes[0].getnetworkinfo()['connections'], 2) # bilateral connection
+
+ 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)
+
+ self.nodes[0].setnetworkactive(True)
+ connect_nodes_bi(self.nodes, 0, 1)
+ assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
+ assert_equal(self.nodes[0].getnetworkinfo()['connections'], 2)
+
+
+if __name__ == '__main__':
+ NetTest().main()
diff --git a/test/functional/p2p-acceptblock.py b/test/functional/p2p-acceptblock.py
index e1111da4ae..c09945baa6 100755
--- a/test/functional/p2p-acceptblock.py
+++ b/test/functional/p2p-acceptblock.py
@@ -58,7 +58,7 @@ from test_framework.blocktools import create_block, create_coinbase
# p2p messages to a node, generating the messages in the main testing logic.
class TestNode(NodeConnCB):
def __init__(self):
- NodeConnCB.__init__(self)
+ super().__init__()
self.connection = None
self.ping_counter = 1
self.last_pong = msg_pong()
@@ -88,21 +88,6 @@ class TestNode(NodeConnCB):
def on_pong(self, conn, message):
self.last_pong = message
- # Sync up with the node after delivery of a block
- def sync_with_ping(self, timeout=30):
- self.connection.send_message(msg_ping(nonce=self.ping_counter))
- received_pong = False
- sleep_time = 0.05
- while not received_pong and timeout > 0:
- time.sleep(sleep_time)
- timeout -= sleep_time
- with mininode_lock:
- if self.last_pong.nonce == self.ping_counter:
- received_pong = True
- self.ping_counter += 1
- return received_pong
-
-
class AcceptBlockTest(BitcoinTestFramework):
def add_options(self, parser):
parser.add_option("--testbinary", dest="testbinary",
diff --git a/test/functional/p2p-compactblocks.py b/test/functional/p2p-compactblocks.py
index 1fc0312c34..bf8d113767 100755
--- a/test/functional/p2p-compactblocks.py
+++ b/test/functional/p2p-compactblocks.py
@@ -15,9 +15,9 @@ from test_framework.blocktools import create_block, create_coinbase, add_witness
from test_framework.script import CScript, OP_TRUE
# TestNode: A peer we use to send messages to bitcoind, and store responses.
-class TestNode(SingleNodeConnCB):
+class TestNode(NodeConnCB):
def __init__(self):
- SingleNodeConnCB.__init__(self)
+ super().__init__()
self.last_sendcmpct = []
self.last_headers = None
self.last_inv = None
@@ -32,6 +32,13 @@ class TestNode(SingleNodeConnCB):
# This is for synchronizing the p2p message traffic,
# so we can eg wait until a particular block is announced.
self.set_announced_blockhashes = set()
+ self.connected = False
+
+ def on_open(self, conn):
+ self.connected = True
+
+ def on_close(self, conn):
+ self.connected = False
def on_sendcmpct(self, conn, message):
self.last_sendcmpct.append(message)
@@ -107,6 +114,18 @@ class TestNode(SingleNodeConnCB):
return (block_hash in self.set_announced_blockhashes)
return wait_until(received_hash, timeout=timeout)
+ def send_await_disconnect(self, message, timeout=30):
+ """Sends a message to the node and wait for disconnect.
+
+ This is used when we want to send a message into the node that we expect
+ will get us disconnected, eg an invalid block."""
+ self.send_message(message)
+ success = wait_until(lambda: not self.connected, timeout=timeout)
+ if not success:
+ logger.error("send_await_disconnect failed!")
+ raise AssertionError("send_await_disconnect failed!")
+ return success
+
class CompactBlocksTest(BitcoinTestFramework):
def __init__(self):
super().__init__()
@@ -274,8 +293,8 @@ class CompactBlocksTest(BitcoinTestFramework):
# This index will be too high
prefilled_txn = PrefilledTransaction(1, block.vtx[0])
cmpct_block.prefilled_txn = [prefilled_txn]
- self.test_node.send_and_ping(msg_cmpctblock(cmpct_block))
- assert(int(self.nodes[0].getbestblockhash(), 16) == block.hashPrevBlock)
+ self.test_node.send_await_disconnect(msg_cmpctblock(cmpct_block))
+ assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.hashPrevBlock)
# Compare the generated shortids to what we expect based on BIP 152, given
# bitcoind's choice of nonce.
diff --git a/test/functional/p2p-feefilter.py b/test/functional/p2p-feefilter.py
index d8f07700d0..12539be950 100755
--- a/test/functional/p2p-feefilter.py
+++ b/test/functional/p2p-feefilter.py
@@ -24,9 +24,9 @@ def allInvsMatch(invsExpected, testnode):
# TestNode: bare-bones "peer". Used to track which invs are received from a node
# and to send the node feefilter messages.
-class TestNode(SingleNodeConnCB):
+class TestNode(NodeConnCB):
def __init__(self):
- SingleNodeConnCB.__init__(self)
+ super().__init__()
self.txinvs = []
def on_inv(self, conn, message):
diff --git a/test/functional/p2p-leaktests.py b/test/functional/p2p-leaktests.py
index 3a843197fb..5853ec86f0 100755
--- a/test/functional/p2p-leaktests.py
+++ b/test/functional/p2p-leaktests.py
@@ -19,10 +19,10 @@ banscore = 10
class CLazyNode(NodeConnCB):
def __init__(self):
+ super().__init__()
self.connection = None
self.unexpected_msg = False
self.connected = False
- super().__init__()
def add_connection(self, conn):
self.connection = conn
diff --git a/test/functional/p2p-mempool.py b/test/functional/p2p-mempool.py
index 0aa9c90e8f..5064ce74aa 100755
--- a/test/functional/p2p-mempool.py
+++ b/test/functional/p2p-mempool.py
@@ -14,7 +14,7 @@ from test_framework.util import *
class TestNode(NodeConnCB):
def __init__(self):
- NodeConnCB.__init__(self)
+ super().__init__()
self.connection = None
self.ping_counter = 1
self.last_pong = msg_pong()
@@ -62,15 +62,6 @@ class TestNode(NodeConnCB):
def on_close(self, conn):
self.peer_disconnected = True
- # Sync up with the node after delivery of a block
- def sync_with_ping(self, timeout=30):
- def received_pong():
- return (self.last_pong.nonce == self.ping_counter)
- self.connection.send_message(msg_ping(nonce=self.ping_counter))
- success = wait_until(received_pong, timeout=timeout)
- self.ping_counter += 1
- return success
-
def send_mempool(self):
self.lastInv = []
self.send_message(msg_mempool())
diff --git a/test/functional/p2p-segwit.py b/test/functional/p2p-segwit.py
index dcf2b9a7de..cd7b788eb4 100755
--- a/test/functional/p2p-segwit.py
+++ b/test/functional/p2p-segwit.py
@@ -32,10 +32,9 @@ def get_virtual_size(witness_block):
vsize = int((3*base_size + total_size + 3)/4)
return vsize
-# Note: we can reduce code by using SingleNodeConnCB (in master, not 0.12)
class TestNode(NodeConnCB):
def __init__(self):
- NodeConnCB.__init__(self)
+ super().__init__()
self.connection = None
self.ping_counter = 1
self.last_pong = msg_pong(0)
@@ -81,13 +80,6 @@ class TestNode(NodeConnCB):
timeout -= self.sleep_time
raise AssertionError("Sync failed to complete")
- def sync_with_ping(self, timeout=60):
- self.send_message(msg_ping(nonce=self.ping_counter))
- test_function = lambda: self.last_pong.nonce == self.ping_counter
- self.sync(test_function, timeout)
- self.ping_counter += 1
- return
-
def wait_for_block(self, blockhash, timeout=60):
test_function = lambda: self.last_block != None and self.last_block.sha256 == blockhash
self.sync(test_function, timeout)
@@ -149,7 +141,7 @@ class TestNode(NodeConnCB):
if with_witness:
tx_message = msg_witness_tx(tx)
self.send_message(tx_message)
- self.sync_with_ping()
+ self.sync_with_ping(60)
assert_equal(tx.hash in self.connection.rpc.getrawmempool(), accepted)
if (reason != None and not accepted):
# Check the rejection reason as well.
@@ -162,7 +154,7 @@ class TestNode(NodeConnCB):
self.send_message(msg_witness_block(block))
else:
self.send_message(msg_block(block))
- self.sync_with_ping()
+ self.sync_with_ping(60)
assert_equal(self.connection.rpc.getbestblockhash() == block.hash, accepted)
@@ -236,7 +228,7 @@ class SegWitTest(BitcoinTestFramework):
block = self.build_next_block(nVersion=1)
block.solve()
self.test_node.send_message(msg_block(block))
- self.test_node.sync_with_ping() # make sure the block was processed
+ self.test_node.sync_with_ping(60) # make sure the block was processed
txid = block.vtx[0].sha256
self.nodes[0].generate(99) # let the block mature
@@ -252,7 +244,7 @@ class SegWitTest(BitcoinTestFramework):
assert_equal(msg_tx(tx).serialize(), msg_witness_tx(tx).serialize())
self.test_node.send_message(msg_witness_tx(tx))
- self.test_node.sync_with_ping() # make sure the tx was processed
+ self.test_node.sync_with_ping(60) # make sure the tx was processed
assert(tx.hash in self.nodes[0].getrawmempool())
# Save this transaction for later
self.utxo.append(UTXO(tx.sha256, 0, 49*100000000))
@@ -292,7 +284,7 @@ class SegWitTest(BitcoinTestFramework):
# But it should not be permanently marked bad...
# Resend without witness information.
self.test_node.send_message(msg_block(block))
- self.test_node.sync_with_ping()
+ self.test_node.sync_with_ping(60)
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
sync_blocks(self.nodes)
@@ -1258,7 +1250,7 @@ class SegWitTest(BitcoinTestFramework):
# Spending a higher version witness output is not allowed by policy,
# even with fRequireStandard=false.
self.test_node.test_transaction_acceptance(tx3, with_witness=True, accepted=False)
- self.test_node.sync_with_ping()
+ self.test_node.sync_with_ping(60)
with mininode_lock:
assert(b"reserved for soft-fork upgrades" in self.test_node.last_reject.reason)
@@ -1388,7 +1380,7 @@ class SegWitTest(BitcoinTestFramework):
for i in range(NUM_TESTS):
# Ping regularly to keep the connection alive
if (not i % 100):
- self.test_node.sync_with_ping()
+ self.test_node.sync_with_ping(60)
# Choose random number of inputs to use.
num_inputs = random.randint(1, 10)
# Create a slight bias for producing more utxos
diff --git a/test/functional/p2p-timeouts.py b/test/functional/p2p-timeouts.py
index 498acb23fe..de4edd6800 100755
--- a/test/functional/p2p-timeouts.py
+++ b/test/functional/p2p-timeouts.py
@@ -27,9 +27,9 @@ from test_framework.mininode import *
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
-class TestNode(SingleNodeConnCB):
+class TestNode(NodeConnCB):
def __init__(self):
- SingleNodeConnCB.__init__(self)
+ super().__init__()
self.connected = False
self.received_version = False
diff --git a/test/functional/p2p-versionbits-warning.py b/test/functional/p2p-versionbits-warning.py
index dc714e9a4a..da960e2d80 100755
--- a/test/functional/p2p-versionbits-warning.py
+++ b/test/functional/p2p-versionbits-warning.py
@@ -28,7 +28,7 @@ VB_PATTERN = re.compile("^Warning.*versionbit")
# p2p messages to a node, generating the messages in the main testing logic.
class TestNode(NodeConnCB):
def __init__(self):
- NodeConnCB.__init__(self)
+ super().__init__()
self.connection = None
self.ping_counter = 1
self.last_pong = msg_pong()
@@ -46,21 +46,6 @@ class TestNode(NodeConnCB):
def on_pong(self, conn, message):
self.last_pong = message
- # Sync up with the node after delivery of a block
- def sync_with_ping(self, timeout=30):
- self.connection.send_message(msg_ping(nonce=self.ping_counter))
- received_pong = False
- sleep_time = 0.05
- while not received_pong and timeout > 0:
- time.sleep(sleep_time)
- timeout -= sleep_time
- with mininode_lock:
- if self.last_pong.nonce == self.ping_counter:
- received_pong = True
- self.ping_counter += 1
- return received_pong
-
-
class VersionBitsWarningTest(BitcoinTestFramework):
def __init__(self):
super().__init__()
diff --git a/test/functional/rpcbind_test.py b/test/functional/rpcbind_test.py
index 8720a345ce..efc36481d1 100755
--- a/test/functional/rpcbind_test.py
+++ b/test/functional/rpcbind_test.py
@@ -4,6 +4,9 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test running bitcoind with the -rpcbind and -rpcallowip options."""
+import socket
+import sys
+
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
from test_framework.netutil import *
@@ -52,7 +55,9 @@ class RPCBindTest(BitcoinTestFramework):
def run_test(self):
# due to OS-specific network stats queries, this test works only on Linux
- assert(sys.platform.startswith('linux'))
+ if not sys.platform.startswith('linux'):
+ self.log.warning("This test can only be run on linux. Skipping test.")
+ sys.exit(self.TEST_EXIT_SKIPPED)
# find the first non-loopback interface for testing
non_loopback_ip = None
for name,ip in all_interfaces():
@@ -60,7 +65,16 @@ class RPCBindTest(BitcoinTestFramework):
non_loopback_ip = ip
break
if non_loopback_ip is None:
- assert(not 'This test requires at least one non-loopback IPv4 interface')
+ self.log.warning("This test requires at least one non-loopback IPv4 interface. Skipping test.")
+ sys.exit(self.TEST_EXIT_SKIPPED)
+ try:
+ s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
+ s.connect(("::1",1))
+ s.close
+ except OSError:
+ self.log.warning("This test requires IPv6 support. Skipping test.")
+ sys.exit(self.TEST_EXIT_SKIPPED)
+
self.log.info("Using interface %s for testing" % non_loopback_ip)
defaultport = rpc_port(0)
diff --git a/test/functional/sendheaders.py b/test/functional/sendheaders.py
index de7f5e0849..1a7475ae84 100755
--- a/test/functional/sendheaders.py
+++ b/test/functional/sendheaders.py
@@ -81,9 +81,9 @@ from test_framework.blocktools import create_block, create_coinbase
direct_fetch_response_time = 0.05
-class BaseNode(SingleNodeConnCB):
+class BaseNode(NodeConnCB):
def __init__(self):
- SingleNodeConnCB.__init__(self)
+ super().__init__()
self.last_inv = None
self.last_headers = None
self.last_block = None
diff --git a/test/functional/test_framework/comptool.py b/test/functional/test_framework/comptool.py
index 70d1d700ef..25c18bda82 100755
--- a/test/functional/test_framework/comptool.py
+++ b/test/functional/test_framework/comptool.py
@@ -42,7 +42,7 @@ class RejectResult(object):
class TestNode(NodeConnCB):
def __init__(self, block_store, tx_store):
- NodeConnCB.__init__(self)
+ super().__init__()
self.conn = None
self.bestblockhash = None
self.block_store = block_store
diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py
index aace17a043..d57d46f2ff 100755
--- a/test/functional/test_framework/mininode.py
+++ b/test/functional/test_framework/mininode.py
@@ -1476,25 +1476,9 @@ class NodeConnCB(object):
self.deliver_sleep_time = None
# Remember the services our peer has advertised
self.peer_services = None
-
- def set_deliver_sleep_time(self, value):
- with mininode_lock:
- self.deliver_sleep_time = value
-
- def get_deliver_sleep_time(self):
- with mininode_lock:
- return self.deliver_sleep_time
-
- # Spin until verack message is received from the node.
- # Tests may want to use this as a signal that the test can begin.
- # This can be called from the testing thread, so it needs to acquire the
- # global lock.
- def wait_for_verack(self):
- while True:
- with mininode_lock:
- if self.verack_received:
- return
- time.sleep(0.05)
+ self.connection = None
+ self.ping_counter = 1
+ self.last_pong = msg_pong()
def deliver(self, conn, message):
deliver_sleep = self.get_deliver_sleep_time()
@@ -1506,17 +1490,36 @@ class NodeConnCB(object):
except:
logger.exception("ERROR delivering %s" % repr(message))
- def on_version(self, conn, message):
- if message.nVersion >= 209:
- conn.send_message(msg_verack())
- conn.ver_send = min(MY_VERSION, message.nVersion)
- if message.nVersion < 209:
- conn.ver_recv = conn.ver_send
- conn.nServices = message.nServices
+ def set_deliver_sleep_time(self, value):
+ with mininode_lock:
+ self.deliver_sleep_time = value
- def on_verack(self, conn, message):
- conn.ver_recv = conn.ver_send
- self.verack_received = True
+ def get_deliver_sleep_time(self):
+ with mininode_lock:
+ return self.deliver_sleep_time
+
+ # Callbacks which can be overridden by subclasses
+ #################################################
+
+ def on_addr(self, conn, message): pass
+ def on_alert(self, conn, message): pass
+ def on_block(self, conn, message): pass
+ def on_blocktxn(self, conn, message): pass
+ def on_close(self, conn): pass
+ def on_cmpctblock(self, conn, message): pass
+ def on_feefilter(self, conn, message): pass
+ def on_getaddr(self, conn, message): pass
+ def on_getblocks(self, conn, message): pass
+ def on_getblocktxn(self, conn, message): pass
+ def on_getdata(self, conn, message): pass
+ def on_getheaders(self, conn, message): pass
+ def on_headers(self, conn, message): pass
+ def on_mempool(self, conn): pass
+ def on_open(self, conn): pass
+ def on_reject(self, conn, message): pass
+ def on_sendcmpct(self, conn, message): pass
+ def on_sendheaders(self, conn, message): pass
+ def on_tx(self, conn, message): pass
def on_inv(self, conn, message):
want = msg_getdata()
@@ -1526,37 +1529,27 @@ class NodeConnCB(object):
if len(want.inv):
conn.send_message(want)
- def on_addr(self, conn, message): pass
- def on_alert(self, conn, message): pass
- def on_getdata(self, conn, message): pass
- def on_getblocks(self, conn, message): pass
- def on_tx(self, conn, message): pass
- def on_block(self, conn, message): pass
- def on_getaddr(self, conn, message): pass
- def on_headers(self, conn, message): pass
- def on_getheaders(self, conn, message): pass
def on_ping(self, conn, message):
if conn.ver_send > BIP0031_VERSION:
conn.send_message(msg_pong(message.nonce))
- def on_reject(self, conn, message): pass
- def on_open(self, conn): pass
- def on_close(self, conn): pass
- def on_mempool(self, conn): pass
- def on_pong(self, conn, message): pass
- def on_feefilter(self, conn, message): pass
- def on_sendheaders(self, conn, message): pass
- def on_sendcmpct(self, conn, message): pass
- def on_cmpctblock(self, conn, message): pass
- def on_getblocktxn(self, conn, message): pass
- def on_blocktxn(self, conn, message): pass
-# More useful callbacks and functions for NodeConnCB's which have a single NodeConn
-class SingleNodeConnCB(NodeConnCB):
- def __init__(self):
- NodeConnCB.__init__(self)
- self.connection = None
- self.ping_counter = 1
- self.last_pong = msg_pong()
+ def on_pong(self, conn, message):
+ self.last_pong = message
+
+ def on_verack(self, conn, message):
+ conn.ver_recv = conn.ver_send
+ self.verack_received = True
+
+ def on_version(self, conn, message):
+ if message.nVersion >= 209:
+ conn.send_message(msg_verack())
+ conn.ver_send = min(MY_VERSION, message.nVersion)
+ if message.nVersion < 209:
+ conn.ver_recv = conn.ver_send
+ conn.nServices = message.nServices
+
+ # Helper functions
+ ##################
def add_connection(self, conn):
self.connection = conn
@@ -1569,18 +1562,30 @@ class SingleNodeConnCB(NodeConnCB):
self.send_message(message)
self.sync_with_ping()
- def on_pong(self, conn, message):
- self.last_pong = message
-
# Sync up with the node
- def sync_with_ping(self, timeout=30):
+ def sync_with_ping(self, timeout=60):
def received_pong():
return (self.last_pong.nonce == self.ping_counter)
self.send_message(msg_ping(nonce=self.ping_counter))
success = wait_until(received_pong, timeout=timeout)
+ if not success:
+ logger.error("sync_with_ping failed!")
+ raise AssertionError("sync_with_ping failed!")
self.ping_counter += 1
+
return success
+ # Spin until verack message is received from the node.
+ # Tests may want to use this as a signal that the test can begin.
+ # This can be called from the testing thread, so it needs to acquire the
+ # global lock.
+ def wait_for_verack(self):
+ while True:
+ with mininode_lock:
+ if self.verack_received:
+ return
+ time.sleep(0.05)
+
# The actual NodeConn class
# This class provides an interface for a p2p connection to a specified node
class NodeConn(asyncore.dispatcher):
@@ -1744,7 +1749,7 @@ class NodeConn(asyncore.dispatcher):
def send_message(self, message, pushbuf=False):
if self.state != "connected" and not pushbuf:
raise IOError('Not connected, no pushbuf')
- logger.debug("Send message to %s:%d: %s" % (self.dstaddr, self.dstport, repr(message)))
+ self._log_message("send", message)
command = message.command
data = message.serialize()
tmsg = self.MAGIC_BYTES[self.network]
@@ -1766,9 +1771,19 @@ class NodeConn(asyncore.dispatcher):
self.messagemap[b'ping'] = msg_ping_prebip31
if self.last_sent + 30 * 60 < time.time():
self.send_message(self.messagemap[b'ping']())
- logger.debug("Received message from %s:%d: %s" % (self.dstaddr, self.dstport, repr(message)))
+ self._log_message("receive", message)
self.cb.deliver(self, message)
+ def _log_message(self, direction, msg):
+ if direction == "send":
+ log_message = "Send message to "
+ elif direction == "receive":
+ log_message = "Received message from "
+ log_message += "%s:%d: %s" % (self.dstaddr, self.dstport, repr(msg)[:500])
+ if len(log_message) > 500:
+ log_message += "... (msg truncated)"
+ logger.debug(log_message)
+
def disconnect_node(self):
self.disconnect = True
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 8656a1ca6a..473b7c14a9 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -4,6 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Base class for RPC testing."""
+from collections import deque
import logging
import optparse
import os
@@ -27,9 +28,12 @@ from .util import (
)
from .authproxy import JSONRPCException
-
class BitcoinTestFramework(object):
+ TEST_EXIT_PASSED = 0
+ TEST_EXIT_FAILED = 1
+ TEST_EXIT_SKIPPED = 77
+
def __init__(self):
self.num_nodes = 4
self.setup_clean_chain = False
@@ -174,19 +178,24 @@ class BitcoinTestFramework(object):
# Dump the end of the debug logs, to aid in debugging rare
# travis failures.
import glob
- filenames = glob.glob(self.options.tmpdir + "/node*/regtest/debug.log")
+ filenames = [self.options.tmpdir + "/test_framework.log"]
+ filenames += glob.glob(self.options.tmpdir + "/node*/regtest/debug.log")
MAX_LINES_TO_PRINT = 1000
- for f in filenames:
- print("From" , f, ":")
- from collections import deque
- print("".join(deque(open(f), MAX_LINES_TO_PRINT)))
+ for fn in filenames:
+ try:
+ with open(fn, 'r') as f:
+ print("From" , fn, ":")
+ print("".join(deque(f, MAX_LINES_TO_PRINT)))
+ except OSError:
+ print("Opening file %s failed." % fn)
+ traceback.print_exc()
if success:
self.log.info("Tests successful")
- sys.exit(0)
+ sys.exit(self.TEST_EXIT_PASSED)
else:
self.log.error("Test failed. Test logging available at %s/test_framework.log", self.options.tmpdir)
logging.shutdown()
- sys.exit(1)
+ sys.exit(self.TEST_EXIT_FAILED)
def _start_logging(self):
# Add logger and logging handlers
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 23ac324510..899b0b5a1b 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -315,7 +315,7 @@ def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=
datadir = os.path.join(dirname, "node"+str(i))
if binary is None:
binary = os.getenv("BITCOIND", "bitcoind")
- args = [ binary, "-datadir="+datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-logtimemicros", "-debug", "-mocktime="+str(get_mocktime()) ]
+ args = [binary, "-datadir=" + datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-logtimemicros", "-debug", "-debugexclude=libevent", "-debugexclude=leveldb", "-mocktime=" + str(get_mocktime())]
if extra_args is not None: args.extend(extra_args)
bitcoind_processes[i] = subprocess.Popen(args, stderr=stderr)
logger.debug("initialize_chain: bitcoind started, waiting for RPC to come up")
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 12eb92028f..c0bbc623a8 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -23,6 +23,23 @@ import sys
import subprocess
import tempfile
import re
+import logging
+
+# Formatting. Default colors to empty strings.
+BOLD, BLUE, RED, GREY = ("", ""), ("", ""), ("", ""), ("", "")
+TICK = "✓ "
+CROSS = "✖ "
+CIRCLE = "○ "
+if os.name == 'posix':
+ # primitive formatting on supported
+ # terminal via ANSI escape sequences:
+ BOLD = ('\033[0m', '\033[1m')
+ BLUE = ('\033[0m', '\033[0;34m')
+ RED = ('\033[0m', '\033[0;31m')
+ GREY = ('\033[0m', '\033[1;30m')
+
+TEST_EXIT_PASSED = 0
+TEST_EXIT_SKIPPED = 77
BASE_SCRIPTS= [
# Scripts that are run by the travis build process.
@@ -67,6 +84,7 @@ BASE_SCRIPTS= [
'decodescript.py',
'blockchain.py',
'disablewallet.py',
+ 'net.py',
'keypool.py',
'p2p-mempool.py',
'prioritise_transaction.py',
@@ -87,7 +105,7 @@ BASE_SCRIPTS= [
ZMQ_SCRIPTS = [
# ZMQ test can only be run if bitcoin was built with zmq-enabled.
# call test_runner.py with -nozmq to explicitly exclude these tests.
- "zmq_test.py"]
+ 'zmq_test.py']
EXTENDED_SCRIPTS = [
# These tests are not run by the travis build process.
@@ -107,6 +125,7 @@ EXTENDED_SCRIPTS = [
'p2p-feefilter.py',
'rpcbind_test.py',
# vv Tests less than 30s vv
+ 'assumevalid.py',
'bip65-cltv.py',
'bip65-cltv-p2p.py',
'bipdersig-p2p.py',
@@ -123,6 +142,13 @@ EXTENDED_SCRIPTS = [
ALL_SCRIPTS = BASE_SCRIPTS + ZMQ_SCRIPTS + EXTENDED_SCRIPTS
+NON_SCRIPTS = [
+ # These are python files that live in the functional tests directory, but are not test scripts.
+ "combine_logs.py",
+ "create_cache.py",
+ "test_runner.py",
+]
+
def main():
# Parse arguments and pass through unrecognised args
parser = argparse.ArgumentParser(add_help=False,
@@ -137,6 +163,7 @@ def main():
parser.add_argument('--force', '-f', action='store_true', help='run tests even on platforms where they are disabled by default (e.g. windows).')
parser.add_argument('--help', '-h', '-?', action='store_true', help='print help text and exit')
parser.add_argument('--jobs', '-j', type=int, default=4, help='how many test scripts to run in parallel. Default=4.')
+ parser.add_argument('--quiet', '-q', action='store_true', help='only print results summary and failure logs')
parser.add_argument('--nozmq', action='store_true', help='do not run the zmq tests')
args, unknown_args = parser.parse_known_args()
@@ -148,6 +175,10 @@ def main():
config = configparser.ConfigParser()
config.read_file(open(os.path.dirname(__file__) + "/config.ini"))
+ # Set up logging
+ logging_level = logging.INFO if args.quiet else logging.DEBUG
+ logging.basicConfig(format='%(message)s', level=logging_level)
+
enable_wallet = config["components"].getboolean("ENABLE_WALLET")
enable_utils = config["components"].getboolean("ENABLE_UTILS")
enable_bitcoind = config["components"].getboolean("ENABLE_BITCOIND")
@@ -203,19 +234,16 @@ def main():
sys.exit(0)
if args.help:
- # Print help for test_runner.py, then print help of the first script and exit.
+ # Print help for test_runner.py, then print help of the first script (with args removed) and exit.
parser.print_help()
- subprocess.check_call((config["environment"]["SRCDIR"] + '/test/functional/' + test_list[0]).split() + ['-h'])
+ subprocess.check_call([(config["environment"]["SRCDIR"] + '/test/functional/' + test_list[0].split()[0])] + ['-h'])
sys.exit(0)
+ check_script_list(config["environment"]["SRCDIR"])
+
run_tests(test_list, config["environment"]["SRCDIR"], config["environment"]["BUILDDIR"], config["environment"]["EXEEXT"], args.jobs, args.coverage, passon_args)
def run_tests(test_list, src_dir, build_dir, exeext, jobs=1, enable_coverage=False, args=[]):
- BOLD = ("","")
- if os.name == 'posix':
- # primitive formatting on supported
- # terminal via ANSI escape sequences:
- BOLD = ('\033[0m', '\033[1m')
#Set env vars
if "BITCOIND" not in os.environ:
@@ -229,7 +257,7 @@ def run_tests(test_list, src_dir, build_dir, exeext, jobs=1, enable_coverage=Fal
if enable_coverage:
coverage = RPCCoverage()
flags.append(coverage.flag)
- print("Initializing coverage directory at %s\n" % coverage.dir)
+ logging.debug("Initializing coverage directory at %s" % coverage.dir)
else:
coverage = None
@@ -238,38 +266,55 @@ def run_tests(test_list, src_dir, build_dir, exeext, jobs=1, enable_coverage=Fal
subprocess.check_output([tests_dir + 'create_cache.py'] + flags)
#Run Tests
- all_passed = True
- time_sum = 0
- time0 = time.time()
-
job_queue = TestHandler(jobs, tests_dir, test_list, flags)
+ time0 = time.time()
+ test_results = []
max_len_name = len(max(test_list, key=len))
- results = BOLD[1] + "%s | %s | %s\n\n" % ("TEST".ljust(max_len_name), "PASSED", "DURATION") + BOLD[0]
- for _ in range(len(test_list)):
- (name, stdout, stderr, passed, duration) = job_queue.get_next()
- all_passed = all_passed and passed
- time_sum += duration
- print('\n' + BOLD[1] + name + BOLD[0] + ":")
- print('' if passed else stdout + '\n', end='')
- print('' if stderr == '' else 'stderr:\n' + stderr + '\n', end='')
- print("Pass: %s%s%s, Duration: %s s\n" % (BOLD[1], passed, BOLD[0], duration))
+ for _ in range(len(test_list)):
+ test_result, stdout, stderr = job_queue.get_next()
+ test_results.append(test_result)
- results += "%s | %s | %s s\n" % (name.ljust(max_len_name), str(passed).ljust(6), duration)
+ if test_result.status == "Passed":
+ logging.debug("\n%s%s%s passed, Duration: %s s" % (BOLD[1], test_result.name, BOLD[0], test_result.time))
+ elif test_result.status == "Skipped":
+ logging.debug("\n%s%s%s skipped" % (BOLD[1], test_result.name, BOLD[0]))
+ else:
+ print("\n%s%s%s failed, Duration: %s s\n" % (BOLD[1], test_result.name, BOLD[0], test_result.time))
+ print(BOLD[1] + 'stdout:\n' + BOLD[0] + stdout + '\n')
+ print(BOLD[1] + 'stderr:\n' + BOLD[0] + stderr + '\n')
- results += BOLD[1] + "\n%s | %s | %s s (accumulated)" % ("ALL".ljust(max_len_name), str(all_passed).ljust(6), time_sum) + BOLD[0]
- print(results)
- print("\nRuntime: %s s" % (int(time.time() - time0)))
+ print_results(test_results, max_len_name, (int(time.time() - time0)))
if coverage:
coverage.report_rpc_coverage()
- print("Cleaning up coverage data")
+ logging.debug("Cleaning up coverage data")
coverage.cleanup()
+ all_passed = all(map(lambda test_result: test_result.status == "Passed", test_results))
+
sys.exit(not all_passed)
+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())
+ all_passed = True
+ time_sum = 0
+
+ for test_result in test_results:
+ all_passed = all_passed and test_result.status != "Failed"
+ time_sum += test_result.time
+ test_result.padding = max_len_name
+ results += str(test_result)
+
+ status = TICK + "Passed" if all_passed else CROSS + "Failed"
+ results += BOLD[1] + "\n%s | %s | %s s (accumulated) \n" % ("ALL".ljust(max_len_name), status.ljust(9), time_sum) + BOLD[0]
+ results += "Runtime: %s s\n" % (runtime)
+ print(results)
+
class TestHandler:
"""
Trigger the testscrips passed in via the list.
@@ -296,9 +341,10 @@ class TestHandler:
port_seed = ["--portseed={}".format(len(self.test_list) + self.portseed_offset)]
log_stdout = tempfile.SpooledTemporaryFile(max_size=2**16)
log_stderr = tempfile.SpooledTemporaryFile(max_size=2**16)
+ test_argv = t.split()
self.jobs.append((t,
time.time(),
- subprocess.Popen((self.tests_dir + t).split() + self.flags + port_seed,
+ subprocess.Popen([self.tests_dir + test_argv[0]] + test_argv[1:] + self.flags + port_seed,
universal_newlines=True,
stdout=log_stdout,
stderr=log_stderr),
@@ -315,12 +361,51 @@ class TestHandler:
log_out.seek(0), log_err.seek(0)
[stdout, stderr] = [l.read().decode('utf-8') for l in (log_out, log_err)]
log_out.close(), log_err.close()
- passed = stderr == "" and proc.returncode == 0
+ if proc.returncode == TEST_EXIT_PASSED and stderr == "":
+ status = "Passed"
+ elif proc.returncode == TEST_EXIT_SKIPPED:
+ status = "Skipped"
+ else:
+ status = "Failed"
self.num_running -= 1
self.jobs.remove(j)
- return name, stdout, stderr, passed, int(time.time() - time0)
+
+ return TestResult(name, status, int(time.time() - time0)), stdout, stderr
print('.', end='', flush=True)
+class TestResult():
+ def __init__(self, name, status, time):
+ self.name = name
+ self.status = status
+ self.time = time
+ self.padding = 0
+
+ def __repr__(self):
+ if self.status == "Passed":
+ color = BLUE
+ glyph = TICK
+ elif self.status == "Failed":
+ color = RED
+ glyph = CROSS
+ elif self.status == "Skipped":
+ color = GREY
+ glyph = CIRCLE
+
+ return color[1] + "%s | %s%s | %s s\n" % (self.name.ljust(self.padding), glyph, self.status.ljust(7), self.time) + color[0]
+
+
+def check_script_list(src_dir):
+ """Check scripts directory.
+
+ 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([t for t in os.listdir(script_dir) if t[-3:] == ".py"])
+ missed_tests = list(python_files - set(map(lambda x: x.split()[0], ALL_SCRIPTS + NON_SCRIPTS)))
+ if len(missed_tests) != 0:
+ print("The following scripts are not being run:" + str(missed_tests))
+ print("Check the test lists in test_runner.py")
+ sys.exit(1)
class RPCCoverage(object):
"""
diff --git a/test/functional/wallet-dump.py b/test/functional/wallet-dump.py
index b819b72b75..8876f935a4 100755
--- a/test/functional/wallet-dump.py
+++ b/test/functional/wallet-dump.py
@@ -88,7 +88,7 @@ class WalletDumpTest(BitcoinTestFramework):
read_dump(tmpdir + "/node0/wallet.unencrypted.dump", addrs, None)
assert_equal(found_addr, test_addr_count) # all keys must be in the dump
assert_equal(found_addr_chg, 50) # 50 blocks where mined
- assert_equal(found_addr_rsv, 90 + 1) # keypool size (TODO: fix off-by-one)
+ assert_equal(found_addr_rsv, 90*2) # 90 keys plus 100% internal keys
#encrypt wallet, restart, unlock and dump
self.nodes[0].encryptwallet('test')
@@ -102,8 +102,8 @@ class WalletDumpTest(BitcoinTestFramework):
found_addr, found_addr_chg, found_addr_rsv, hd_master_addr_enc = \
read_dump(tmpdir + "/node0/wallet.encrypted.dump", addrs, hd_master_addr_unenc)
assert_equal(found_addr, test_addr_count)
- assert_equal(found_addr_chg, 90 + 1 + 50) # old reserve keys are marked as change now
- assert_equal(found_addr_rsv, 90 + 1) # keypool size (TODO: fix off-by-one)
+ assert_equal(found_addr_chg, 90*2 + 50) # old reserve keys are marked as change now
+ assert_equal(found_addr_rsv, 90*2)
if __name__ == '__main__':
WalletDumpTest().main ()
diff --git a/test/functional/wallet-hd.py b/test/functional/wallet-hd.py
index c40662dc3d..64a6c92782 100755
--- a/test/functional/wallet-hd.py
+++ b/test/functional/wallet-hd.py
@@ -42,6 +42,11 @@ class WalletHDTest(BitcoinTestFramework):
masterkeyid = self.nodes[1].getwalletinfo()['hdmasterkeyid']
assert_equal(len(masterkeyid), 40)
+ # create an internal key
+ change_addr = self.nodes[1].getrawchangeaddress()
+ change_addrV= self.nodes[1].validateaddress(change_addr);
+ assert_equal(change_addrV["hdkeypath"], "m/0'/1'/0'") #first internal child key
+
# Import a non-HD private key in the HD wallet
non_hd_add = self.nodes[0].getnewaddress()
self.nodes[1].importprivkey(self.nodes[0].dumpprivkey(non_hd_add))
@@ -65,6 +70,11 @@ class WalletHDTest(BitcoinTestFramework):
self.nodes[0].sendtoaddress(non_hd_add, 1)
self.nodes[0].generate(1)
+ # create an internal key (again)
+ change_addr = self.nodes[1].getrawchangeaddress()
+ change_addrV= self.nodes[1].validateaddress(change_addr);
+ assert_equal(change_addrV["hdkeypath"], "m/0'/1'/1'") #second internal child key
+
self.sync_all()
assert_equal(self.nodes[1].getbalance(), num_hd_adds + 1)
@@ -90,6 +100,15 @@ class WalletHDTest(BitcoinTestFramework):
#connect_nodes_bi(self.nodes, 0, 1)
assert_equal(self.nodes[1].getbalance(), num_hd_adds + 1)
+ # send a tx and make sure its using the internal chain for the changeoutput
+ txid = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1)
+ outs = self.nodes[1].decoderawtransaction(self.nodes[1].gettransaction(txid)['hex'])['vout'];
+ keypath = ""
+ for out in outs:
+ if out['value'] != 1:
+ keypath = self.nodes[1].validateaddress(out['scriptPubKey']['addresses'][0])['hdkeypath']
+
+ assert_equal(keypath[0:7], "m/0'/1'")
if __name__ == '__main__':
WalletHDTest().main ()