aboutsummaryrefslogtreecommitdiff
path: root/test/functional
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional')
-rwxr-xr-xtest/functional/feature_backwards_compatibility.py3
-rwxr-xr-xtest/functional/p2p_addr_relay.py71
-rwxr-xr-xtest/functional/p2p_filter.py5
-rwxr-xr-xtest/functional/p2p_segwit.py6
-rwxr-xr-xtest/functional/p2p_sendheaders.py4
-rwxr-xr-xtest/functional/rpc_signrawtransaction.py43
-rwxr-xr-xtest/functional/test_framework/messages.py19
-rwxr-xr-xtest/functional/test_framework/mininode.py3
-rwxr-xr-xtest/functional/test_framework/test_node.py4
-rwxr-xr-xtest/functional/test_runner.py1
-rwxr-xr-xtest/functional/wallet_avoidreuse.py25
-rwxr-xr-xtest/functional/wallet_bumpfee.py17
12 files changed, 185 insertions, 16 deletions
diff --git a/test/functional/feature_backwards_compatibility.py b/test/functional/feature_backwards_compatibility.py
index 0db74432e2..adf66243b7 100755
--- a/test/functional/feature_backwards_compatibility.py
+++ b/test/functional/feature_backwards_compatibility.py
@@ -40,6 +40,9 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
["-nowallet", "-walletrbf=1", "-addresstype=bech32"] # v0.17.1
]
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
def setup_nodes(self):
if os.getenv("TEST_PREVIOUS_RELEASES") == "false":
raise SkipTest("backwards compatibility tests")
diff --git a/test/functional/p2p_addr_relay.py b/test/functional/p2p_addr_relay.py
new file mode 100755
index 0000000000..6046237101
--- /dev/null
+++ b/test/functional/p2p_addr_relay.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python3
+# Copyright (c) 2020 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""
+Test addr relay
+"""
+
+from test_framework.messages import (
+ CAddress,
+ NODE_NETWORK,
+ NODE_WITNESS,
+ msg_addr,
+)
+from test_framework.mininode import (
+ P2PInterface,
+)
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+)
+import time
+
+ADDRS = []
+for i in range(10):
+ addr = CAddress()
+ addr.time = int(time.time()) + i
+ addr.nServices = NODE_NETWORK | NODE_WITNESS
+ addr.ip = "123.123.123.{}".format(i % 256)
+ addr.port = 8333 + i
+ ADDRS.append(addr)
+
+
+class AddrReceiver(P2PInterface):
+ def on_addr(self, message):
+ for addr in message.addrs:
+ assert_equal(addr.nServices, 9)
+ assert addr.ip.startswith('123.123.123.')
+ assert (8333 <= addr.port < 8343)
+
+
+class AddrTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = False
+ self.num_nodes = 1
+
+ def run_test(self):
+ self.log.info('Create connection that sends addr messages')
+ addr_source = self.nodes[0].add_p2p_connection(P2PInterface())
+ msg = msg_addr()
+
+ self.log.info('Send too large addr message')
+ msg.addrs = ADDRS * 101
+ with self.nodes[0].assert_debug_log(['message addr size() = 1010']):
+ addr_source.send_and_ping(msg)
+
+ self.log.info('Check that addr message content is relayed and added to addrman')
+ addr_receiver = self.nodes[0].add_p2p_connection(AddrReceiver())
+ msg.addrs = ADDRS
+ with self.nodes[0].assert_debug_log([
+ 'Added 10 addresses from 127.0.0.1: 0 tried',
+ 'received: addr (301 bytes) peer=0',
+ 'sending addr (301 bytes) peer=1',
+ ]):
+ addr_source.send_and_ping(msg)
+ self.nodes[0].setmocktime(int(time.time()) + 30 * 60)
+ addr_receiver.sync_with_ping()
+
+
+if __name__ == '__main__':
+ AddrTest().main()
diff --git a/test/functional/p2p_filter.py b/test/functional/p2p_filter.py
index 2940542e5e..188b130a57 100755
--- a/test/functional/p2p_filter.py
+++ b/test/functional/p2p_filter.py
@@ -11,6 +11,7 @@ from test_framework.messages import (
MSG_FILTERED_BLOCK,
msg_getdata,
msg_filterload,
+ msg_filteradd,
msg_filterclear,
)
from test_framework.mininode import (
@@ -103,6 +104,10 @@ class FilterTest(BitcoinTestFramework):
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 7)
filter_node.wait_for_tx(txid)
+ self.log.info("Check that division-by-zero remote crash bug [CVE-2013-5700] is fixed")
+ filter_node.send_and_ping(msg_filterload(data=b'', nHashFuncs=1))
+ filter_node.send_and_ping(msg_filteradd(data=b'letstrytocrashthisnode'))
+
if __name__ == '__main__':
FilterTest().main()
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index 118f31382a..d8dce7fe56 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -125,8 +125,7 @@ def test_transaction_acceptance(node, p2p, tx, with_witness, accepted, reason=No
- use the getrawmempool rpc to check for acceptance."""
reason = [reason] if reason else []
with node.assert_debug_log(expected_msgs=reason):
- p2p.send_message(msg_tx(tx) if with_witness else msg_no_witness_tx(tx))
- p2p.sync_with_ping()
+ p2p.send_and_ping(msg_tx(tx) if with_witness else msg_no_witness_tx(tx))
assert_equal(tx.hash in node.getrawmempool(), accepted)
@@ -137,8 +136,7 @@ def test_witness_block(node, p2p, block, accepted, with_witness=True, reason=Non
- use the getbestblockhash rpc to check for acceptance."""
reason = [reason] if reason else []
with node.assert_debug_log(expected_msgs=reason):
- p2p.send_message(msg_block(block) if with_witness else msg_no_witness_block(block))
- p2p.sync_with_ping()
+ p2p.send_and_ping(msg_block(block) if with_witness else msg_no_witness_block(block))
assert_equal(node.getbestblockhash() == block.hash, accepted)
diff --git a/test/functional/p2p_sendheaders.py b/test/functional/p2p_sendheaders.py
index e543e647f2..84d818400a 100755
--- a/test/functional/p2p_sendheaders.py
+++ b/test/functional/p2p_sendheaders.py
@@ -243,10 +243,6 @@ class SendHeadersTest(BitcoinTestFramework):
# will occur outside of direct fetching
test_node = self.nodes[0].add_p2p_connection(BaseNode(), services=NODE_WITNESS)
- # Ensure verack's have been processed by our peer
- inv_node.sync_with_ping()
- test_node.sync_with_ping()
-
self.test_null_locators(test_node, inv_node)
self.test_nonnull_locators(test_node, inv_node)
diff --git a/test/functional/rpc_signrawtransaction.py b/test/functional/rpc_signrawtransaction.py
index 780758e219..17686f3a78 100755
--- a/test/functional/rpc_signrawtransaction.py
+++ b/test/functional/rpc_signrawtransaction.py
@@ -4,10 +4,11 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test transaction signing using the signrawtransaction* RPCs."""
+from test_framework.address import check_script, script_to_p2sh
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_rpc_error, hex_str_to_bytes
+from test_framework.util import assert_equal, assert_raises_rpc_error, find_vout_for_address, hex_str_to_bytes
from test_framework.messages import sha256
-from test_framework.script import CScript, OP_0
+from test_framework.script import CScript, OP_0, OP_CHECKSIG
from decimal import Decimal
@@ -168,6 +169,44 @@ class SignRawTransactionsTest(BitcoinTestFramework):
assert 'complete' in spending_tx_signed
assert_equal(spending_tx_signed['complete'], True)
+ self.log.info('Try with a P2PKH script as the witnessScript')
+ embedded_addr_info = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress('', 'legacy'))
+ embedded_privkey = self.nodes[1].dumpprivkey(embedded_addr_info['address'])
+ witness_script = embedded_addr_info['scriptPubKey']
+ redeem_script = CScript([OP_0, sha256(check_script(witness_script))]).hex()
+ addr = script_to_p2sh(redeem_script)
+ script_pub_key = self.nodes[1].validateaddress(addr)['scriptPubKey']
+ # Fund that address
+ txid = self.nodes[0].sendtoaddress(addr, 10)
+ vout = find_vout_for_address(self.nodes[0], txid, addr)
+ self.nodes[0].generate(1)
+ # Now create and sign a transaction spending that output on node[0], which doesn't know the scripts or keys
+ spending_tx = self.nodes[0].createrawtransaction([{'txid': txid, 'vout': vout}], {self.nodes[1].getnewaddress(): Decimal("9.999")})
+ spending_tx_signed = self.nodes[0].signrawtransactionwithkey(spending_tx, [embedded_privkey], [{'txid': txid, 'vout': vout, 'scriptPubKey': script_pub_key, 'redeemScript': redeem_script, 'witnessScript': witness_script, 'amount': 10}])
+ # Check the signing completed successfully
+ assert 'complete' in spending_tx_signed
+ assert_equal(spending_tx_signed['complete'], True)
+ self.nodes[0].sendrawtransaction(spending_tx_signed['hex'])
+
+ self.log.info('Try with a P2PK script as the witnessScript')
+ embedded_addr_info = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress('', 'legacy'))
+ embedded_privkey = self.nodes[1].dumpprivkey(embedded_addr_info['address'])
+ witness_script = CScript([hex_str_to_bytes(embedded_addr_info['pubkey']), OP_CHECKSIG]).hex()
+ redeem_script = CScript([OP_0, sha256(check_script(witness_script))]).hex()
+ addr = script_to_p2sh(redeem_script)
+ script_pub_key = self.nodes[1].validateaddress(addr)['scriptPubKey']
+ # Fund that address
+ txid = self.nodes[0].sendtoaddress(addr, 10)
+ vout = find_vout_for_address(self.nodes[0], txid, addr)
+ self.nodes[0].generate(1)
+ # Now create and sign a transaction spending that output on node[0], which doesn't know the scripts or keys
+ spending_tx = self.nodes[0].createrawtransaction([{'txid': txid, 'vout': vout}], {self.nodes[1].getnewaddress(): Decimal("9.999")})
+ spending_tx_signed = self.nodes[0].signrawtransactionwithkey(spending_tx, [embedded_privkey], [{'txid': txid, 'vout': vout, 'scriptPubKey': script_pub_key, 'redeemScript': redeem_script, 'witnessScript': witness_script, 'amount': 10}])
+ # Check the signing completed successfully
+ assert 'complete' in spending_tx_signed
+ assert_equal(spending_tx_signed['complete'], True)
+ self.nodes[0].sendrawtransaction(spending_tx_signed['hex'])
+
def run_test(self):
self.successful_signing_test()
self.script_verification_error_test()
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py
index ff0c763b72..5f8fcc6fd8 100755
--- a/test/functional/test_framework/messages.py
+++ b/test/functional/test_framework/messages.py
@@ -1356,6 +1356,25 @@ class msg_filterload:
self.data, self.nHashFuncs, self.nTweak, self.nFlags)
+class msg_filteradd:
+ __slots__ = ("data")
+ command = b"filteradd"
+
+ def __init__(self, data):
+ self.data = data
+
+ def deserialize(self, f):
+ self.data = deser_string(f)
+
+ def serialize(self):
+ r = b""
+ r += ser_string(self.data)
+ return r
+
+ def __repr__(self):
+ return "msg_filteradd(data={})".format(self.data)
+
+
class msg_filterclear:
__slots__ = ()
command = b"filterclear"
diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py
index ce51513ce9..ad330f2a93 100755
--- a/test/functional/test_framework/mininode.py
+++ b/test/functional/test_framework/mininode.py
@@ -30,6 +30,7 @@ from test_framework.messages import (
msg_blocktxn,
msg_cmpctblock,
msg_feefilter,
+ msg_filteradd,
msg_filterclear,
msg_filterload,
msg_getaddr,
@@ -65,6 +66,7 @@ MESSAGEMAP = {
b"blocktxn": msg_blocktxn,
b"cmpctblock": msg_cmpctblock,
b"feefilter": msg_feefilter,
+ b"filteradd": msg_filteradd,
b"filterclear": msg_filterclear,
b"filterload": msg_filterload,
b"getaddr": msg_getaddr,
@@ -324,6 +326,7 @@ class P2PInterface(P2PConnection):
def on_blocktxn(self, message): pass
def on_cmpctblock(self, message): pass
def on_feefilter(self, message): pass
+ def on_filteradd(self, message): pass
def on_filterclear(self, message): pass
def on_filterload(self, message): pass
def on_getaddr(self, message): pass
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 8260c917fe..53bc5ca9e7 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -233,6 +233,10 @@ class TestNode():
# -342 Service unavailable, RPC server started but is shutting down due to error
if e.error['code'] != -28 and e.error['code'] != -342:
raise # unknown JSON RPC exception
+ except ConnectionResetError:
+ # This might happen when the RPC server is in warmup, but shut down before the call to getblockcount
+ # succeeds. Try again to properly raise the FailedToStartError
+ pass
except ValueError as e: # cookie file not found and no rpcuser or rpcassword. bitcoind still starting
if "No RPC credentials" not in str(e):
raise
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 2f307750a9..faa2dee4ed 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -144,6 +144,7 @@ BASE_SCRIPTS = [
'rpc_blockchain.py',
'rpc_deprecated.py',
'wallet_disable.py',
+ 'p2p_addr_relay.py',
'rpc_net.py',
'wallet_keypool.py',
'p2p_mempool.py',
diff --git a/test/functional/wallet_avoidreuse.py b/test/functional/wallet_avoidreuse.py
index 8e2dc03ac2..2ce8d459c6 100755
--- a/test/functional/wallet_avoidreuse.py
+++ b/test/functional/wallet_avoidreuse.py
@@ -83,6 +83,7 @@ class AvoidReuseTest(BitcoinTestFramework):
self.nodes[0].generate(110)
self.sync_all()
+ self.test_change_remains_change(self.nodes[1])
reset_balance(self.nodes[1], self.nodes[0].getnewaddress())
self.test_fund_send_fund_senddirty()
reset_balance(self.nodes[1], self.nodes[0].getnewaddress())
@@ -137,6 +138,30 @@ class AvoidReuseTest(BitcoinTestFramework):
# Unload temp wallet
self.nodes[1].unloadwallet(tempwallet)
+ def test_change_remains_change(self, node):
+ self.log.info("Test that change doesn't turn into non-change when spent")
+
+ reset_balance(node, node.getnewaddress())
+ addr = node.getnewaddress()
+ txid = node.sendtoaddress(addr, 1)
+ out = node.listunspent(minconf=0, query_options={'minimumAmount': 2})
+ assert_equal(len(out), 1)
+ assert_equal(out[0]['txid'], txid)
+ changeaddr = out[0]['address']
+
+ # Make sure it's starting out as change as expected
+ assert node.getaddressinfo(changeaddr)['ischange']
+ for logical_tx in node.listtransactions():
+ assert logical_tx.get('address') != changeaddr
+
+ # Spend it
+ reset_balance(node, node.getnewaddress())
+
+ # It should still be change
+ assert node.getaddressinfo(changeaddr)['ischange']
+ for logical_tx in node.listtransactions():
+ assert logical_tx.get('address') != changeaddr
+
def test_fund_send_fund_senddirty(self):
'''
Test the same as test_fund_send_fund_send, except send the 10 BTC with
diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py
index 38c9807757..0b3dea94d5 100755
--- a/test/functional/wallet_bumpfee.py
+++ b/test/functional/wallet_bumpfee.py
@@ -253,12 +253,17 @@ def test_dust_to_fee(self, rbf_node, dest_address):
self.log.info('Test that bumped output that is dust is dropped to fee')
rbfid = spend_one_input(rbf_node, dest_address)
fulltx = rbf_node.getrawtransaction(rbfid, 1)
- # size of transaction (p2wpkh, 1 input, 2 outputs): 141 vbytes
- assert_equal(fulltx["vsize"], 141)
- # bump with fee_rate of 0.00350000 BTC per 1000 vbytes
- # expected bump fee of 141 vbytes * fee_rate 0.00350000 BTC / 1000 vbytes = 0.00049350 BTC
- # but dust is dropped, so actual bump fee is 0.00050000
- bumped_tx = rbf_node.bumpfee(rbfid, {"fee_rate": 0.0035})
+ # The DER formatting used by Bitcoin to serialize ECDSA signatures means that signatures can have a
+ # variable size of 70-72 bytes (or possibly even less), with most being 71 or 72 bytes. The signature
+ # in the witness is divided by 4 for the vsize, so this variance can take the weight across a 4-byte
+ # boundary. Thus expected transaction size (p2wpkh, 1 input, 2 outputs) is 140-141 vbytes, usually 141.
+ if not 140 <= fulltx["vsize"] <= 141:
+ raise AssertionError("Invalid tx vsize of {} (140-141 expected), full tx: {}".format(fulltx["vsize"], fulltx))
+ # Bump with fee_rate of 0.00350250 BTC per 1000 vbytes to create dust.
+ # Expected fee is 141 vbytes * fee_rate 0.00350250 BTC / 1000 vbytes = 0.00049385 BTC.
+ # or occasionally 140 vbytes * fee_rate 0.00350250 BTC / 1000 vbytes = 0.00049035 BTC.
+ # Dust should be dropped to the fee, so actual bump fee is 0.00050000 BTC.
+ bumped_tx = rbf_node.bumpfee(rbfid, {"fee_rate": 0.00350250})
full_bumped_tx = rbf_node.getrawtransaction(bumped_tx["txid"], 1)
assert_equal(bumped_tx["fee"], Decimal("0.00050000"))
assert_equal(len(fulltx["vout"]), 2)