aboutsummaryrefslogtreecommitdiff
path: root/qa/rpc-tests
diff options
context:
space:
mode:
Diffstat (limited to 'qa/rpc-tests')
-rwxr-xr-xqa/rpc-tests/abandonconflict.py26
-rwxr-xr-xqa/rpc-tests/bip65-cltv-p2p.py40
-rwxr-xr-xqa/rpc-tests/bip65-cltv.py3
-rwxr-xr-xqa/rpc-tests/bip9-softforks.py1
-rwxr-xr-xqa/rpc-tests/bipdersig-p2p.py42
-rwxr-xr-xqa/rpc-tests/create_cache.py10
-rwxr-xr-xqa/rpc-tests/forknotify.py6
-rwxr-xr-xqa/rpc-tests/fundrawtransaction.py1
-rwxr-xr-xqa/rpc-tests/importprunedfunds.py37
-rwxr-xr-xqa/rpc-tests/keypool.py10
-rwxr-xr-xqa/rpc-tests/maxuploadtarget.py2
-rwxr-xr-xqa/rpc-tests/multi_rpc.py2
-rwxr-xr-xqa/rpc-tests/nulldummy.py148
-rwxr-xr-xqa/rpc-tests/p2p-compactblocks.py852
-rwxr-xr-xqa/rpc-tests/p2p-feefilter.py14
-rwxr-xr-xqa/rpc-tests/p2p-fullblocktest.py3
-rwxr-xr-xqa/rpc-tests/p2p-mempool.py9
-rwxr-xr-xqa/rpc-tests/p2p-segwit.py353
-rwxr-xr-xqa/rpc-tests/p2p-versionbits-warning.py50
-rwxr-xr-xqa/rpc-tests/preciousblock.py116
-rwxr-xr-xqa/rpc-tests/pruning.py5
-rwxr-xr-xqa/rpc-tests/reindex.py16
-rwxr-xr-xqa/rpc-tests/rest.py10
-rwxr-xr-xqa/rpc-tests/rpcbind_test.py219
-rwxr-xr-xqa/rpc-tests/segwit.py421
-rwxr-xr-xqa/rpc-tests/sendheaders.py68
-rw-r--r--qa/rpc-tests/test_framework/address.py74
-rw-r--r--qa/rpc-tests/test_framework/authproxy.py11
-rw-r--r--qa/rpc-tests/test_framework/blockstore.py9
-rw-r--r--qa/rpc-tests/test_framework/coverage.py4
-rw-r--r--qa/rpc-tests/test_framework/key.py25
-rwxr-xr-xqa/rpc-tests/test_framework/mininode.py410
-rw-r--r--qa/rpc-tests/test_framework/netutil.py2
-rw-r--r--qa/rpc-tests/test_framework/script.py2
-rw-r--r--qa/rpc-tests/test_framework/siphash.py64
-rwxr-xr-xqa/rpc-tests/test_framework/test_framework.py13
-rw-r--r--qa/rpc-tests/test_framework/util.py52
-rwxr-xr-xqa/rpc-tests/wallet-accounts.py94
-rwxr-xr-xqa/rpc-tests/wallet-dump.py104
-rwxr-xr-xqa/rpc-tests/wallet-hd.py8
-rwxr-xr-xqa/rpc-tests/wallet.py8
-rwxr-xr-xqa/rpc-tests/walletbackup.py7
42 files changed, 2887 insertions, 464 deletions
diff --git a/qa/rpc-tests/abandonconflict.py b/qa/rpc-tests/abandonconflict.py
index c50c3cc562..874df48777 100755
--- a/qa/rpc-tests/abandonconflict.py
+++ b/qa/rpc-tests/abandonconflict.py
@@ -68,7 +68,7 @@ class AbandonConflictTest(BitcoinTestFramework):
# In mempool txs from self should increase balance from change
newbalance = self.nodes[0].getbalance()
- assert(newbalance == balance - Decimal("30") + Decimal("24.9996"))
+ assert_equal(newbalance, balance - Decimal("30") + Decimal("24.9996"))
balance = newbalance
# Restart the node with a higher min relay fee so the parent tx is no longer in mempool
@@ -78,16 +78,16 @@ class AbandonConflictTest(BitcoinTestFramework):
self.nodes[0]=start_node(0, self.options.tmpdir, ["-debug","-logtimemicros","-minrelaytxfee=0.0001"])
# Verify txs no longer in mempool
- assert(len(self.nodes[0].getrawmempool()) == 0)
+ assert_equal(len(self.nodes[0].getrawmempool()), 0)
# Not in mempool txs from self should only reduce balance
# inputs are still spent, but change not received
newbalance = self.nodes[0].getbalance()
- assert(newbalance == balance - Decimal("24.9996"))
+ assert_equal(newbalance, balance - Decimal("24.9996"))
# Unconfirmed received funds that are not in mempool, also shouldn't show
# up in unconfirmed balance
unconfbalance = self.nodes[0].getunconfirmedbalance() + self.nodes[0].getbalance()
- assert(unconfbalance == newbalance)
+ assert_equal(unconfbalance, newbalance)
# Also shouldn't show up in listunspent
assert(not txABC2 in [utxo["txid"] for utxo in self.nodes[0].listunspent(0)])
balance = newbalance
@@ -96,35 +96,35 @@ class AbandonConflictTest(BitcoinTestFramework):
# including that the child tx was also abandoned
self.nodes[0].abandontransaction(txAB1)
newbalance = self.nodes[0].getbalance()
- assert(newbalance == balance + Decimal("30"))
+ assert_equal(newbalance, balance + Decimal("30"))
balance = newbalance
# Verify that even with a low min relay fee, the tx is not reaccepted from wallet on startup once abandoned
stop_node(self.nodes[0],0)
self.nodes[0]=start_node(0, self.options.tmpdir, ["-debug","-logtimemicros","-minrelaytxfee=0.00001"])
- assert(len(self.nodes[0].getrawmempool()) == 0)
- assert(self.nodes[0].getbalance() == balance)
+ assert_equal(len(self.nodes[0].getrawmempool()), 0)
+ assert_equal(self.nodes[0].getbalance(), balance)
# But if its received again then it is unabandoned
# And since now in mempool, the change is available
# But its child tx remains abandoned
self.nodes[0].sendrawtransaction(signed["hex"])
newbalance = self.nodes[0].getbalance()
- assert(newbalance == balance - Decimal("20") + Decimal("14.99998"))
+ assert_equal(newbalance, balance - Decimal("20") + Decimal("14.99998"))
balance = newbalance
# Send child tx again so its unabandoned
self.nodes[0].sendrawtransaction(signed2["hex"])
newbalance = self.nodes[0].getbalance()
- assert(newbalance == balance - Decimal("10") - Decimal("14.99998") + Decimal("24.9996"))
+ assert_equal(newbalance, balance - Decimal("10") - Decimal("14.99998") + Decimal("24.9996"))
balance = newbalance
# Remove using high relay fee again
stop_node(self.nodes[0],0)
self.nodes[0]=start_node(0, self.options.tmpdir, ["-debug","-logtimemicros","-minrelaytxfee=0.0001"])
- assert(len(self.nodes[0].getrawmempool()) == 0)
+ assert_equal(len(self.nodes[0].getrawmempool()), 0)
newbalance = self.nodes[0].getbalance()
- assert(newbalance == balance - Decimal("24.9996"))
+ assert_equal(newbalance, balance - Decimal("24.9996"))
balance = newbalance
# Create a double spend of AB1 by spending again from only A's 10 output
@@ -143,7 +143,7 @@ class AbandonConflictTest(BitcoinTestFramework):
# Verify that B and C's 10 BTC outputs are available for spending again because AB1 is now conflicted
newbalance = self.nodes[0].getbalance()
- assert(newbalance == balance + Decimal("20"))
+ assert_equal(newbalance, balance + Decimal("20"))
balance = newbalance
# There is currently a minor bug around this and so this test doesn't work. See Issue #7315
@@ -151,7 +151,7 @@ class AbandonConflictTest(BitcoinTestFramework):
# Don't think C's should either
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
newbalance = self.nodes[0].getbalance()
- #assert(newbalance == balance - Decimal("10"))
+ #assert_equal(newbalance, balance - Decimal("10"))
print("If balance has not declined after invalidateblock then out of mempool wallet tx which is no longer")
print("conflicted has not resumed causing its inputs to be seen as spent. See Issue #7315")
print(str(balance) + " -> " + str(newbalance) + " ?")
diff --git a/qa/rpc-tests/bip65-cltv-p2p.py b/qa/rpc-tests/bip65-cltv-p2p.py
index 754b6873b7..e903b2fbf0 100755
--- a/qa/rpc-tests/bip65-cltv-p2p.py
+++ b/qa/rpc-tests/bip65-cltv-p2p.py
@@ -71,9 +71,9 @@ class BIP65Test(ComparisonTestFramework):
self.nodeaddress = self.nodes[0].getnewaddress()
self.last_block_time = int(time.time())
- ''' 98 more version 3 blocks '''
+ ''' 398 more version 3 blocks '''
test_blocks = []
- for i in range(98):
+ for i in range(398):
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
block.nVersion = 3
block.rehash()
@@ -118,24 +118,6 @@ class BIP65Test(ComparisonTestFramework):
height += 1
yield TestInstance([[block, True]])
- '''
- Check that the new CLTV rules are enforced in the 751st version 4
- block.
- '''
- spendtx = self.create_transaction(self.nodes[0],
- self.coinbase_blocks[1], self.nodeaddress, 1.0)
- cltv_invalidate(spendtx)
- spendtx.rehash()
-
- block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
- block.nVersion = 4
- block.vtx.append(spendtx)
- block.hashMerkleRoot = block.calc_merkle_root()
- block.rehash()
- block.solve()
- self.last_block_time += 1
- yield TestInstance([[block, False]])
-
''' Mine 199 new version blocks on last valid tip '''
test_blocks = []
for i in range(199):
@@ -169,6 +151,24 @@ class BIP65Test(ComparisonTestFramework):
height += 1
yield TestInstance([[block, True]])
+ '''
+ Check that the new CLTV rules are enforced in the 951st version 4
+ block.
+ '''
+ spendtx = self.create_transaction(self.nodes[0],
+ self.coinbase_blocks[1], self.nodeaddress, 1.0)
+ cltv_invalidate(spendtx)
+ spendtx.rehash()
+
+ block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
+ block.nVersion = 4
+ block.vtx.append(spendtx)
+ block.hashMerkleRoot = block.calc_merkle_root()
+ block.rehash()
+ block.solve()
+ self.last_block_time += 1
+ yield TestInstance([[block, False]])
+
''' Mine 1 old version block, should be invalid '''
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
block.nVersion = 3
diff --git a/qa/rpc-tests/bip65-cltv.py b/qa/rpc-tests/bip65-cltv.py
index abba7fc20e..baa77b92a0 100755
--- a/qa/rpc-tests/bip65-cltv.py
+++ b/qa/rpc-tests/bip65-cltv.py
@@ -30,7 +30,8 @@ class BIP65Test(BitcoinTestFramework):
cnt = self.nodes[0].getblockcount()
# Mine some old-version blocks
- self.nodes[1].generate(100)
+ self.nodes[1].generate(200)
+ cnt += 100
self.sync_all()
if (self.nodes[0].getblockcount() != cnt + 100):
raise AssertionError("Failed to mine 100 version=3 blocks")
diff --git a/qa/rpc-tests/bip9-softforks.py b/qa/rpc-tests/bip9-softforks.py
index 979d1410c2..be6ddde112 100755
--- a/qa/rpc-tests/bip9-softforks.py
+++ b/qa/rpc-tests/bip9-softforks.py
@@ -195,7 +195,6 @@ class BIP9SoftForksTest(ComparisonTestFramework):
# Restart all
self.test.block_store.close()
stop_nodes(self.nodes)
- wait_bitcoinds()
shutil.rmtree(self.options.tmpdir)
self.setup_chain()
self.setup_network()
diff --git a/qa/rpc-tests/bipdersig-p2p.py b/qa/rpc-tests/bipdersig-p2p.py
index 4e4936a4ae..3bad5af5e6 100755
--- a/qa/rpc-tests/bipdersig-p2p.py
+++ b/qa/rpc-tests/bipdersig-p2p.py
@@ -79,9 +79,9 @@ class BIP66Test(ComparisonTestFramework):
self.nodeaddress = self.nodes[0].getnewaddress()
self.last_block_time = int(time.time())
- ''' 98 more version 2 blocks '''
+ ''' 298 more version 2 blocks '''
test_blocks = []
- for i in range(98):
+ for i in range(298):
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
block.nVersion = 2
block.rehash()
@@ -124,25 +124,7 @@ class BIP66Test(ComparisonTestFramework):
self.last_block_time += 1
self.tip = block.sha256
height += 1
- yield TestInstance([[block, True]])
-
- '''
- Check that the new DERSIG rules are enforced in the 751st version 3
- block.
- '''
- spendtx = self.create_transaction(self.nodes[0],
- self.coinbase_blocks[1], self.nodeaddress, 1.0)
- unDERify(spendtx)
- spendtx.rehash()
-
- block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
- block.nVersion = 3
- block.vtx.append(spendtx)
- block.hashMerkleRoot = block.calc_merkle_root()
- block.rehash()
- block.solve()
- self.last_block_time += 1
- yield TestInstance([[block, False]])
+ yield TestInstance([[block, True]])
''' Mine 199 new version blocks on last valid tip '''
test_blocks = []
@@ -177,6 +159,24 @@ class BIP66Test(ComparisonTestFramework):
height += 1
yield TestInstance([[block, True]])
+ '''
+ Check that the new DERSIG rules are enforced in the 951st version 3
+ block.
+ '''
+ spendtx = self.create_transaction(self.nodes[0],
+ self.coinbase_blocks[1], self.nodeaddress, 1.0)
+ unDERify(spendtx)
+ spendtx.rehash()
+
+ block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
+ block.nVersion = 3
+ block.vtx.append(spendtx)
+ block.hashMerkleRoot = block.calc_merkle_root()
+ block.rehash()
+ block.solve()
+ self.last_block_time += 1
+ yield TestInstance([[block, False]])
+
''' Mine 1 old version block, should be invalid '''
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
block.nVersion = 2
diff --git a/qa/rpc-tests/create_cache.py b/qa/rpc-tests/create_cache.py
index b6161e0917..1ace6310d0 100755
--- a/qa/rpc-tests/create_cache.py
+++ b/qa/rpc-tests/create_cache.py
@@ -12,9 +12,15 @@ from test_framework.test_framework import BitcoinTestFramework
class CreateCache(BitcoinTestFramework):
+ def __init__(self):
+ super().__init__()
+
+ # Test network and test nodes are not required:
+ self.num_nodes = 0
+ self.nodes = []
+
def setup_network(self):
- # Don't setup any test nodes
- self.options.noshutdown = True
+ pass
def run_test(self):
pass
diff --git a/qa/rpc-tests/forknotify.py b/qa/rpc-tests/forknotify.py
index 5a3f75c808..a1901aedab 100755
--- a/qa/rpc-tests/forknotify.py
+++ b/qa/rpc-tests/forknotify.py
@@ -22,7 +22,7 @@ class ForkNotifyTest(BitcoinTestFramework):
def setup_network(self):
self.nodes = []
self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt")
- with open(self.alert_filename, 'w') as f:
+ with open(self.alert_filename, 'w', encoding='utf8') as f:
pass # Just open then close to create zero-length file
self.nodes.append(start_node(0, self.options.tmpdir,
["-blockversion=2", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""]))
@@ -44,7 +44,7 @@ class ForkNotifyTest(BitcoinTestFramework):
self.nodes[1].generate(1)
self.sync_all()
- with open(self.alert_filename, 'r') as f:
+ with open(self.alert_filename, 'r', encoding='utf8') as f:
alert_text = f.read()
if len(alert_text) == 0:
@@ -56,7 +56,7 @@ class ForkNotifyTest(BitcoinTestFramework):
self.nodes[1].generate(1)
self.sync_all()
- with open(self.alert_filename, 'r') as f:
+ with open(self.alert_filename, 'r', encoding='utf8') as f:
alert_text2 = f.read()
if alert_text != alert_text2:
diff --git a/qa/rpc-tests/fundrawtransaction.py b/qa/rpc-tests/fundrawtransaction.py
index eeb8476634..8c45578fcf 100755
--- a/qa/rpc-tests/fundrawtransaction.py
+++ b/qa/rpc-tests/fundrawtransaction.py
@@ -470,7 +470,6 @@ class RawTransactionsTest(BitcoinTestFramework):
self.nodes[1].encryptwallet("test")
self.nodes.pop(1)
stop_nodes(self.nodes)
- wait_bitcoinds()
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
# This test is not meant to test fee estimation and we'd like
diff --git a/qa/rpc-tests/importprunedfunds.py b/qa/rpc-tests/importprunedfunds.py
index d86f51b7f3..0dee8ad4ec 100755
--- a/qa/rpc-tests/importprunedfunds.py
+++ b/qa/rpc-tests/importprunedfunds.py
@@ -5,7 +5,7 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
-import decimal
+
class ImportPrunedFundsTest(BitcoinTestFramework):
@@ -20,14 +20,10 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
self.is_network_split=False
self.sync_all()
- def run_test (self):
- import time
- begintime = int(time.time())
-
+ def run_test(self):
print("Mining blocks...")
self.nodes[0].generate(101)
- # sync
self.sync_all()
# address
@@ -72,7 +68,6 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
rawtxn2 = self.nodes[0].gettransaction(txnid2)['hex']
proof2 = self.nodes[0].gettxoutproof([txnid2])
-
txnid3 = self.nodes[0].sendtoaddress(address3, 0.025)
self.nodes[0].generate(1)
rawtxn3 = self.nodes[0].gettransaction(txnid3)['hex']
@@ -82,28 +77,27 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
#Import with no affiliated address
try:
- result1 = self.nodes[1].importprunedfunds(rawtxn1, proof1, "")
+ self.nodes[1].importprunedfunds(rawtxn1, proof1)
except JSONRPCException as e:
assert('No addresses' in e.error['message'])
else:
assert(False)
-
balance1 = self.nodes[1].getbalance("", 0, True)
assert_equal(balance1, Decimal(0))
#Import with affiliated address with no rescan
- self.nodes[1].importaddress(address2, "", False)
- result2 = self.nodes[1].importprunedfunds(rawtxn2, proof2, "")
- balance2 = Decimal(self.nodes[1].getbalance("", 0, True))
+ self.nodes[1].importaddress(address2, "add2", False)
+ result2 = self.nodes[1].importprunedfunds(rawtxn2, proof2)
+ balance2 = self.nodes[1].getbalance("add2", 0, True)
assert_equal(balance2, Decimal('0.05'))
#Import with private key with no rescan
- self.nodes[1].importprivkey(address3_privkey, "", False)
- result3 = self.nodes[1].importprunedfunds(rawtxn3, proof3, "")
- balance3 = Decimal(self.nodes[1].getbalance("", 0, False))
+ self.nodes[1].importprivkey(address3_privkey, "add3", False)
+ result3 = self.nodes[1].importprunedfunds(rawtxn3, proof3)
+ balance3 = self.nodes[1].getbalance("add3", 0, False)
assert_equal(balance3, Decimal('0.025'))
- balance3 = Decimal(self.nodes[1].getbalance("", 0, True))
+ balance3 = self.nodes[1].getbalance("*", 0, True)
assert_equal(balance3, Decimal('0.075'))
#Addresses Test - after import
@@ -118,7 +112,6 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
assert_equal(address_info['ismine'], True)
#Remove transactions
-
try:
self.nodes[1].removeprunedfunds(txnid1)
except JSONRPCException as e:
@@ -126,18 +119,16 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
else:
assert(False)
-
- balance1 = Decimal(self.nodes[1].getbalance("", 0, True))
+ balance1 = self.nodes[1].getbalance("*", 0, True)
assert_equal(balance1, Decimal('0.075'))
-
self.nodes[1].removeprunedfunds(txnid2)
- balance2 = Decimal(self.nodes[1].getbalance("", 0, True))
+ balance2 = self.nodes[1].getbalance("*", 0, True)
assert_equal(balance2, Decimal('0.025'))
self.nodes[1].removeprunedfunds(txnid3)
- balance3 = Decimal(self.nodes[1].getbalance("", 0, True))
+ balance3 = self.nodes[1].getbalance("*", 0, True)
assert_equal(balance3, Decimal('0.0'))
if __name__ == '__main__':
- ImportPrunedFundsTest ().main ()
+ ImportPrunedFundsTest().main()
diff --git a/qa/rpc-tests/keypool.py b/qa/rpc-tests/keypool.py
index c75303ecbf..fa39476568 100755
--- a/qa/rpc-tests/keypool.py
+++ b/qa/rpc-tests/keypool.py
@@ -12,6 +12,11 @@ class KeyPoolTest(BitcoinTestFramework):
def run_test(self):
nodes = self.nodes
+ addr_before_encrypting = nodes[0].getnewaddress()
+ addr_before_encrypting_data = nodes[0].validateaddress(addr_before_encrypting)
+ wallet_info_old = nodes[0].getwalletinfo()
+ assert(addr_before_encrypting_data['hdmasterkeyid'] == wallet_info_old['hdmasterkeyid'])
+
# Encrypt wallet and wait to terminate
nodes[0].encryptwallet('test')
bitcoind_processes[0].wait()
@@ -19,6 +24,11 @@ class KeyPoolTest(BitcoinTestFramework):
nodes[0] = start_node(0, self.options.tmpdir)
# Keep creating keys
addr = nodes[0].getnewaddress()
+ addr_data = nodes[0].validateaddress(addr)
+ wallet_info = nodes[0].getwalletinfo()
+ assert(addr_before_encrypting_data['hdmasterkeyid'] != wallet_info['hdmasterkeyid'])
+ assert(addr_data['hdmasterkeyid'] == wallet_info['hdmasterkeyid'])
+
try:
addr = nodes[0].getnewaddress()
raise AssertionError('Keypool should be exhausted after one address')
diff --git a/qa/rpc-tests/maxuploadtarget.py b/qa/rpc-tests/maxuploadtarget.py
index 125d4eb275..d0e9fe9a3f 100755
--- a/qa/rpc-tests/maxuploadtarget.py
+++ b/qa/rpc-tests/maxuploadtarget.py
@@ -75,7 +75,7 @@ class TestNode(NodeConnCB):
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)
+ success = wait_until(received_pong, timeout=timeout)
self.ping_counter += 1
return success
diff --git a/qa/rpc-tests/multi_rpc.py b/qa/rpc-tests/multi_rpc.py
index 24373b257d..cdeb94caa0 100755
--- a/qa/rpc-tests/multi_rpc.py
+++ b/qa/rpc-tests/multi_rpc.py
@@ -26,7 +26,7 @@ class HTTPBasicsTest (BitcoinTestFramework):
#Append rpcauth to bitcoin.conf before initialization
rpcauth = "rpcauth=rt:93648e835a54c573682c2eb19f882535$7681e9c5b74bdd85e78166031d2058e1069b3ed7ed967c93fc63abba06f31144"
rpcauth2 = "rpcauth=rt2:f8607b1a88861fac29dfccf9b52ff9f$ff36a0c23c8c62b4846112e50fa888416e94c17bfd4c42f88fd8f55ec6a3137e"
- with open(os.path.join(self.options.tmpdir+"/node0", "bitcoin.conf"), 'a') as f:
+ with open(os.path.join(self.options.tmpdir+"/node0", "bitcoin.conf"), 'a', encoding='utf8') as f:
f.write(rpcauth+"\n")
f.write(rpcauth2+"\n")
diff --git a/qa/rpc-tests/nulldummy.py b/qa/rpc-tests/nulldummy.py
new file mode 100755
index 0000000000..54b7eac376
--- /dev/null
+++ b/qa/rpc-tests/nulldummy.py
@@ -0,0 +1,148 @@
+#!/usr/bin/env python3
+# Copyright (c) 2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import *
+from test_framework.mininode import CTransaction, NetworkThread
+from test_framework.blocktools import create_coinbase, create_block, add_witness_commitment
+from test_framework.script import CScript
+from io import BytesIO
+import time
+
+NULLDUMMY_ERROR = "64: non-mandatory-script-verify-flag (Dummy CHECKMULTISIG argument must be zero)"
+
+def trueDummy(tx):
+ scriptSig = CScript(tx.vin[0].scriptSig)
+ newscript = []
+ for i in scriptSig:
+ if (len(newscript) == 0):
+ assert(len(i) == 0)
+ newscript.append(b'\x51')
+ else:
+ newscript.append(i)
+ tx.vin[0].scriptSig = CScript(newscript)
+ tx.rehash()
+
+'''
+This test is meant to exercise NULLDUMMY softfork.
+Connect to a single node.
+Generate 2 blocks (save the coinbases for later).
+Generate 427 more blocks.
+[Policy/Consensus] Check that NULLDUMMY compliant transactions are accepted in the 430th block.
+[Policy] Check that non-NULLDUMMY transactions are rejected before activation.
+[Consensus] Check that the new NULLDUMMY rules are not enforced on the 431st block.
+[Policy/Consensus] Check that the new NULLDUMMY rules are enforced on the 432nd block.
+'''
+
+class NULLDUMMYTest(BitcoinTestFramework):
+
+ def __init__(self):
+ super().__init__()
+ self.num_nodes = 1
+ self.setup_clean_chain = True
+
+ def setup_network(self):
+ # Must set the blockversion for this test
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir,
+ extra_args=[['-debug', '-whitelist=127.0.0.1', '-walletprematurewitness']])
+
+ def run_test(self):
+ self.address = self.nodes[0].getnewaddress()
+ self.ms_address = self.nodes[0].addmultisigaddress(1,[self.address])
+ self.wit_address = self.nodes[0].addwitnessaddress(self.address)
+ self.wit_ms_address = self.nodes[0].addwitnessaddress(self.ms_address)
+
+ NetworkThread().start() # Start up network handling in another thread
+ self.coinbase_blocks = self.nodes[0].generate(2) # Block 2
+ coinbase_txid = []
+ for i in self.coinbase_blocks:
+ coinbase_txid.append(self.nodes[0].getblock(i)['tx'][0])
+ self.nodes[0].generate(427) # Block 429
+ self.lastblockhash = self.nodes[0].getbestblockhash()
+ self.tip = int("0x" + self.lastblockhash, 0)
+ self.lastblockheight = 429
+ self.lastblocktime = int(time.time()) + 429
+
+ print ("Test 1: NULLDUMMY compliant base transactions should be accepted to mempool and mined before activation [430]")
+ test1txs = [self.create_transaction(self.nodes[0], coinbase_txid[0], self.ms_address, 49)]
+ txid1 = self.tx_submit(self.nodes[0], test1txs[0])
+ test1txs.append(self.create_transaction(self.nodes[0], txid1, self.ms_address, 48))
+ txid2 = self.tx_submit(self.nodes[0], test1txs[1])
+ test1txs.append(self.create_transaction(self.nodes[0], coinbase_txid[1], self.wit_ms_address, 49))
+ txid3 = self.tx_submit(self.nodes[0], test1txs[2])
+ self.block_submit(self.nodes[0], test1txs, False, True)
+
+ print ("Test 2: Non-NULLDUMMY base multisig transaction should not be accepted to mempool before activation")
+ test2tx = self.create_transaction(self.nodes[0], txid2, self.ms_address, 48)
+ trueDummy(test2tx)
+ txid4 = self.tx_submit(self.nodes[0], test2tx, NULLDUMMY_ERROR)
+
+ print ("Test 3: Non-NULLDUMMY base transactions should be accepted in a block before activation [431]")
+ self.block_submit(self.nodes[0], [test2tx], False, True)
+
+ print ("Test 4: Non-NULLDUMMY base multisig transaction is invalid after activation")
+ test4tx = self.create_transaction(self.nodes[0], txid4, self.address, 47)
+ test6txs=[CTransaction(test4tx)]
+ trueDummy(test4tx)
+ self.tx_submit(self.nodes[0], test4tx, NULLDUMMY_ERROR)
+ self.block_submit(self.nodes[0], [test4tx])
+
+ print ("Test 5: Non-NULLDUMMY P2WSH multisig transaction invalid after activation")
+ test5tx = self.create_transaction(self.nodes[0], txid3, self.wit_address, 48)
+ test6txs.append(CTransaction(test5tx))
+ test5tx.wit.vtxinwit[0].scriptWitness.stack[0] = b'\x01'
+ self.tx_submit(self.nodes[0], test5tx, NULLDUMMY_ERROR)
+ self.block_submit(self.nodes[0], [test5tx], True)
+
+ print ("Test 6: NULLDUMMY compliant base/witness transactions should be accepted to mempool and in block after activation [432]")
+ for i in test6txs:
+ self.tx_submit(self.nodes[0], i)
+ self.block_submit(self.nodes[0], test6txs, True, True)
+
+
+ def create_transaction(self, node, txid, to_address, amount):
+ inputs = [{ "txid" : txid, "vout" : 0}]
+ outputs = { to_address : amount }
+ rawtx = node.createrawtransaction(inputs, outputs)
+ signresult = node.signrawtransaction(rawtx)
+ tx = CTransaction()
+ f = BytesIO(hex_str_to_bytes(signresult['hex']))
+ tx.deserialize(f)
+ return tx
+
+
+ def tx_submit(self, node, tx, msg = ""):
+ tx.rehash()
+ try:
+ node.sendrawtransaction(bytes_to_hex_str(tx.serialize_with_witness()), True)
+ except JSONRPCException as exp:
+ assert_equal(exp.error["message"], msg)
+ else:
+ assert_equal('', msg)
+ return tx.hash
+
+
+ def block_submit(self, node, txs, witness = False, accept = False):
+ block = create_block(self.tip, create_coinbase(self.lastblockheight + 1), self.lastblocktime + 1)
+ block.nVersion = 4
+ for tx in txs:
+ tx.rehash()
+ block.vtx.append(tx)
+ block.hashMerkleRoot = block.calc_merkle_root()
+ witness and add_witness_commitment(block)
+ block.rehash()
+ block.solve()
+ node.submitblock(bytes_to_hex_str(block.serialize(True)))
+ if (accept):
+ assert_equal(node.getbestblockhash(), block.hash)
+ self.tip = block.sha256
+ self.lastblockhash = block.hash
+ self.lastblocktime += 1
+ self.lastblockheight += 1
+ else:
+ assert_equal(node.getbestblockhash(), self.lastblockhash)
+
+if __name__ == '__main__':
+ NULLDUMMYTest().main()
diff --git a/qa/rpc-tests/p2p-compactblocks.py b/qa/rpc-tests/p2p-compactblocks.py
new file mode 100755
index 0000000000..6b5d477131
--- /dev/null
+++ b/qa/rpc-tests/p2p-compactblocks.py
@@ -0,0 +1,852 @@
+#!/usr/bin/env python3
+# Copyright (c) 2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+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, add_witness_commitment
+from test_framework.siphash import siphash256
+from test_framework.script import CScript, OP_TRUE
+
+'''
+CompactBlocksTest -- test compact blocks (BIP 152)
+
+Version 1 compact blocks are pre-segwit (txids)
+Version 2 compact blocks are post-segwit (wtxids)
+'''
+
+# TestNode: A peer we use to send messages to bitcoind, and store responses.
+class TestNode(SingleNodeConnCB):
+ def __init__(self):
+ SingleNodeConnCB.__init__(self)
+ self.last_sendcmpct = []
+ self.last_headers = None
+ self.last_inv = None
+ self.last_cmpctblock = None
+ self.block_announced = False
+ self.last_getdata = None
+ self.last_getblocktxn = None
+ self.last_block = None
+ self.last_blocktxn = None
+ # Store the hashes of blocks we've seen announced.
+ # This is for synchronizing the p2p message traffic,
+ # so we can eg wait until a particular block is announced.
+ self.set_announced_blockhashes = set()
+
+ def on_sendcmpct(self, conn, message):
+ self.last_sendcmpct.append(message)
+
+ def on_block(self, conn, message):
+ self.last_block = message
+
+ def on_cmpctblock(self, conn, message):
+ self.last_cmpctblock = message
+ self.block_announced = True
+ self.last_cmpctblock.header_and_shortids.header.calc_sha256()
+ self.set_announced_blockhashes.add(self.last_cmpctblock.header_and_shortids.header.sha256)
+
+ def on_headers(self, conn, message):
+ self.last_headers = message
+ self.block_announced = True
+ for x in self.last_headers.headers:
+ x.calc_sha256()
+ self.set_announced_blockhashes.add(x.sha256)
+
+ def on_inv(self, conn, message):
+ self.last_inv = message
+ for x in self.last_inv.inv:
+ if x.type == 2:
+ self.block_announced = True
+ self.set_announced_blockhashes.add(x.hash)
+
+ def on_getdata(self, conn, message):
+ self.last_getdata = message
+
+ def on_getblocktxn(self, conn, message):
+ self.last_getblocktxn = message
+
+ def on_blocktxn(self, conn, message):
+ self.last_blocktxn = message
+
+ # Requires caller to hold mininode_lock
+ def received_block_announcement(self):
+ return self.block_announced
+
+ def clear_block_announcement(self):
+ with mininode_lock:
+ self.block_announced = False
+ self.last_inv = None
+ self.last_headers = None
+ self.last_cmpctblock = None
+
+ def get_headers(self, locator, hashstop):
+ msg = msg_getheaders()
+ msg.locator.vHave = locator
+ msg.hashstop = hashstop
+ self.connection.send_message(msg)
+
+ def send_header_for_blocks(self, new_blocks):
+ headers_message = msg_headers()
+ headers_message.headers = [CBlockHeader(b) for b in new_blocks]
+ self.send_message(headers_message)
+
+ def request_headers_and_sync(self, locator, hashstop=0):
+ self.clear_block_announcement()
+ self.get_headers(locator, hashstop)
+ assert(wait_until(self.received_block_announcement, timeout=30))
+ assert(self.received_block_announcement())
+ self.clear_block_announcement()
+
+ # Block until a block announcement for a particular block hash is
+ # received.
+ def wait_for_block_announcement(self, block_hash, timeout=30):
+ def received_hash():
+ return (block_hash in self.set_announced_blockhashes)
+ return wait_until(received_hash, timeout=timeout)
+
+class CompactBlocksTest(BitcoinTestFramework):
+ def __init__(self):
+ super().__init__()
+ self.setup_clean_chain = True
+ # Node0 = pre-segwit, node1 = segwit-aware
+ self.num_nodes = 2
+ self.utxos = []
+
+ def setup_network(self):
+ self.nodes = []
+
+ # Start up node0 to be a version 1, pre-segwit node.
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir,
+ [["-debug", "-logtimemicros=1", "-bip9params=segwit:0:0"],
+ ["-debug", "-logtimemicros", "-txindex"]])
+ connect_nodes(self.nodes[0], 1)
+
+ def build_block_on_tip(self, node, segwit=False):
+ height = node.getblockcount()
+ tip = node.getbestblockhash()
+ mtp = node.getblockheader(tip)['mediantime']
+ block = create_block(int(tip, 16), create_coinbase(height + 1), mtp + 1)
+ block.nVersion = 4
+ if segwit:
+ add_witness_commitment(block)
+ block.solve()
+ return block
+
+ # Create 10 more anyone-can-spend utxo's for testing.
+ def make_utxos(self):
+ # Doesn't matter which node we use, just use node0.
+ block = self.build_block_on_tip(self.nodes[0])
+ self.test_node.send_and_ping(msg_block(block))
+ assert(int(self.nodes[0].getbestblockhash(), 16) == block.sha256)
+ self.nodes[0].generate(100)
+
+ total_value = block.vtx[0].vout[0].nValue
+ out_value = total_value // 10
+ tx = CTransaction()
+ tx.vin.append(CTxIn(COutPoint(block.vtx[0].sha256, 0), b''))
+ for i in range(10):
+ tx.vout.append(CTxOut(out_value, CScript([OP_TRUE])))
+ tx.rehash()
+
+ block2 = self.build_block_on_tip(self.nodes[0])
+ block2.vtx.append(tx)
+ block2.hashMerkleRoot = block2.calc_merkle_root()
+ block2.solve()
+ self.test_node.send_and_ping(msg_block(block2))
+ assert_equal(int(self.nodes[0].getbestblockhash(), 16), block2.sha256)
+ self.utxos.extend([[tx.sha256, i, out_value] for i in range(10)])
+ return
+
+ # Test "sendcmpct" (between peers preferring the same version):
+ # - No compact block announcements unless sendcmpct is sent.
+ # - If sendcmpct is sent with version > preferred_version, the message is ignored.
+ # - If sendcmpct is sent with boolean 0, then block announcements are not
+ # made with compact blocks.
+ # - If sendcmpct is then sent with boolean 1, then new block announcements
+ # are made with compact blocks.
+ # If old_node is passed in, request compact blocks with version=preferred-1
+ # and verify that it receives block announcements via compact block.
+ def test_sendcmpct(self, node, test_node, preferred_version, old_node=None):
+ # Make sure we get a SENDCMPCT message from our peer
+ def received_sendcmpct():
+ return (len(test_node.last_sendcmpct) > 0)
+ got_message = wait_until(received_sendcmpct, timeout=30)
+ assert(received_sendcmpct())
+ assert(got_message)
+ with mininode_lock:
+ # Check that the first version received is the preferred one
+ assert_equal(test_node.last_sendcmpct[0].version, preferred_version)
+ # And that we receive versions down to 1.
+ assert_equal(test_node.last_sendcmpct[-1].version, 1)
+ test_node.last_sendcmpct = []
+
+ tip = int(node.getbestblockhash(), 16)
+
+ def check_announcement_of_new_block(node, peer, predicate):
+ peer.clear_block_announcement()
+ node.generate(1)
+ got_message = wait_until(lambda: peer.block_announced, timeout=30)
+ assert(peer.block_announced)
+ assert(got_message)
+ with mininode_lock:
+ assert(predicate(peer))
+
+ # We shouldn't get any block announcements via cmpctblock yet.
+ check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is None)
+
+ # Try one more time, this time after requesting headers.
+ test_node.request_headers_and_sync(locator=[tip])
+ check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is None and p.last_inv is not None)
+
+ # Test a few ways of using sendcmpct that should NOT
+ # result in compact block announcements.
+ # Before each test, sync the headers chain.
+ test_node.request_headers_and_sync(locator=[tip])
+
+ # Now try a SENDCMPCT message with too-high version
+ sendcmpct = msg_sendcmpct()
+ sendcmpct.version = preferred_version+1
+ sendcmpct.announce = True
+ test_node.send_and_ping(sendcmpct)
+ check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is None)
+
+ # Headers sync before next test.
+ test_node.request_headers_and_sync(locator=[tip])
+
+ # Now try a SENDCMPCT message with valid version, but announce=False
+ sendcmpct.version = preferred_version
+ sendcmpct.announce = False
+ test_node.send_and_ping(sendcmpct)
+ check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is None)
+
+ # Headers sync before next test.
+ test_node.request_headers_and_sync(locator=[tip])
+
+ # Finally, try a SENDCMPCT message with announce=True
+ sendcmpct.version = preferred_version
+ sendcmpct.announce = True
+ test_node.send_and_ping(sendcmpct)
+ check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is not None)
+
+ # Try one more time (no headers sync should be needed!)
+ check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is not None)
+
+ # Try one more time, after turning on sendheaders
+ test_node.send_and_ping(msg_sendheaders())
+ check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is not None)
+
+ # Try one more time, after sending a version-1, announce=false message.
+ sendcmpct.version = preferred_version-1
+ sendcmpct.announce = False
+ test_node.send_and_ping(sendcmpct)
+ check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is not None)
+
+ # Now turn off announcements
+ sendcmpct.version = preferred_version
+ sendcmpct.announce = False
+ test_node.send_and_ping(sendcmpct)
+ check_announcement_of_new_block(node, test_node, lambda p: p.last_cmpctblock is None and p.last_headers is not None)
+
+ if old_node is not None:
+ # Verify that a peer using an older protocol version can receive
+ # announcements from this node.
+ sendcmpct.version = preferred_version-1
+ sendcmpct.announce = True
+ old_node.send_and_ping(sendcmpct)
+ # Header sync
+ old_node.request_headers_and_sync(locator=[tip])
+ check_announcement_of_new_block(node, old_node, lambda p: p.last_cmpctblock is not None)
+
+ # This test actually causes bitcoind to (reasonably!) disconnect us, so do this last.
+ def test_invalid_cmpctblock_message(self):
+ self.nodes[0].generate(101)
+ block = self.build_block_on_tip(self.nodes[0])
+
+ cmpct_block = P2PHeaderAndShortIDs()
+ cmpct_block.header = CBlockHeader(block)
+ cmpct_block.prefilled_txn_length = 1
+ # 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)
+
+ # Compare the generated shortids to what we expect based on BIP 152, given
+ # bitcoind's choice of nonce.
+ def test_compactblock_construction(self, node, test_node, version, use_witness_address):
+ # Generate a bunch of transactions.
+ node.generate(101)
+ num_transactions = 25
+ address = node.getnewaddress()
+ if use_witness_address:
+ # Want at least one segwit spend, so move all funds to
+ # a witness address.
+ address = node.addwitnessaddress(address)
+ value_to_send = node.getbalance()
+ node.sendtoaddress(address, satoshi_round(value_to_send-Decimal(0.1)))
+ node.generate(1)
+
+ segwit_tx_generated = False
+ for i in range(num_transactions):
+ txid = node.sendtoaddress(address, 0.1)
+ hex_tx = node.gettransaction(txid)["hex"]
+ tx = FromHex(CTransaction(), hex_tx)
+ if not tx.wit.is_null():
+ segwit_tx_generated = True
+
+ if use_witness_address:
+ assert(segwit_tx_generated) # check that our test is not broken
+
+ # Wait until we've seen the block announcement for the resulting tip
+ tip = int(self.nodes[0].getbestblockhash(), 16)
+ assert(self.test_node.wait_for_block_announcement(tip))
+
+ # Now mine a block, and look at the resulting compact block.
+ test_node.clear_block_announcement()
+ block_hash = int(node.generate(1)[0], 16)
+
+ # Store the raw block in our internal format.
+ block = FromHex(CBlock(), node.getblock("%02x" % block_hash, False))
+ [tx.calc_sha256() for tx in block.vtx]
+ block.rehash()
+
+ # Don't care which type of announcement came back for this test; just
+ # request the compact block if we didn't get one yet.
+ wait_until(test_node.received_block_announcement, timeout=30)
+ assert(test_node.received_block_announcement())
+
+ with mininode_lock:
+ if test_node.last_cmpctblock is None:
+ test_node.clear_block_announcement()
+ inv = CInv(4, block_hash) # 4 == "CompactBlock"
+ test_node.send_message(msg_getdata([inv]))
+
+ wait_until(test_node.received_block_announcement, timeout=30)
+ assert(test_node.received_block_announcement())
+
+ # Now we should have the compactblock
+ header_and_shortids = None
+ with mininode_lock:
+ assert(test_node.last_cmpctblock is not None)
+ # Convert the on-the-wire representation to absolute indexes
+ header_and_shortids = HeaderAndShortIDs(test_node.last_cmpctblock.header_and_shortids)
+
+ # Check that we got the right block!
+ header_and_shortids.header.calc_sha256()
+ assert_equal(header_and_shortids.header.sha256, block_hash)
+
+ # Make sure the prefilled_txn appears to have included the coinbase
+ assert(len(header_and_shortids.prefilled_txn) >= 1)
+ assert_equal(header_and_shortids.prefilled_txn[0].index, 0)
+
+ # Check that all prefilled_txn entries match what's in the block.
+ for entry in header_and_shortids.prefilled_txn:
+ entry.tx.calc_sha256()
+ # This checks the non-witness parts of the tx agree
+ assert_equal(entry.tx.sha256, block.vtx[entry.index].sha256)
+
+ # And this checks the witness
+ wtxid = entry.tx.calc_sha256(True)
+ if version == 2:
+ assert_equal(wtxid, block.vtx[entry.index].calc_sha256(True))
+ else:
+ # Shouldn't have received a witness
+ assert(entry.tx.wit.is_null())
+
+ # Check that the cmpctblock message announced all the transactions.
+ assert_equal(len(header_and_shortids.prefilled_txn) + len(header_and_shortids.shortids), len(block.vtx))
+
+ # And now check that all the shortids are as expected as well.
+ # Determine the siphash keys to use.
+ [k0, k1] = header_and_shortids.get_siphash_keys()
+
+ index = 0
+ while index < len(block.vtx):
+ if (len(header_and_shortids.prefilled_txn) > 0 and
+ header_and_shortids.prefilled_txn[0].index == index):
+ # Already checked prefilled transactions above
+ header_and_shortids.prefilled_txn.pop(0)
+ else:
+ tx_hash = block.vtx[index].sha256
+ if version == 2:
+ tx_hash = block.vtx[index].calc_sha256(True)
+ shortid = calculate_shortid(k0, k1, tx_hash)
+ assert_equal(shortid, header_and_shortids.shortids[0])
+ header_and_shortids.shortids.pop(0)
+ index += 1
+
+ # Test that bitcoind requests compact blocks when we announce new blocks
+ # via header or inv, and that responding to getblocktxn causes the block
+ # to be successfully reconstructed.
+ # Post-segwit: upgraded nodes would only make this request of cb-version-2,
+ # NODE_WITNESS peers. Unupgraded nodes would still make this request of
+ # any cb-version-1-supporting peer.
+ def test_compactblock_requests(self, node, test_node, version, segwit):
+ # Try announcing a block with an inv or header, expect a compactblock
+ # request
+ for announce in ["inv", "header"]:
+ block = self.build_block_on_tip(node, segwit=segwit)
+ with mininode_lock:
+ test_node.last_getdata = None
+
+ if announce == "inv":
+ test_node.send_message(msg_inv([CInv(2, block.sha256)]))
+ else:
+ test_node.send_header_for_blocks([block])
+ success = wait_until(lambda: test_node.last_getdata is not None, timeout=30)
+ assert(success)
+ assert_equal(len(test_node.last_getdata.inv), 1)
+ assert_equal(test_node.last_getdata.inv[0].type, 4)
+ assert_equal(test_node.last_getdata.inv[0].hash, block.sha256)
+
+ # Send back a compactblock message that omits the coinbase
+ comp_block = HeaderAndShortIDs()
+ comp_block.header = CBlockHeader(block)
+ comp_block.nonce = 0
+ [k0, k1] = comp_block.get_siphash_keys()
+ coinbase_hash = block.vtx[0].sha256
+ if version == 2:
+ coinbase_hash = block.vtx[0].calc_sha256(True)
+ comp_block.shortids = [
+ calculate_shortid(k0, k1, coinbase_hash) ]
+ test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
+ assert_equal(int(node.getbestblockhash(), 16), block.hashPrevBlock)
+ # Expect a getblocktxn message.
+ with mininode_lock:
+ assert(test_node.last_getblocktxn is not None)
+ absolute_indexes = test_node.last_getblocktxn.block_txn_request.to_absolute()
+ assert_equal(absolute_indexes, [0]) # should be a coinbase request
+
+ # Send the coinbase, and verify that the tip advances.
+ if version == 2:
+ msg = msg_witness_blocktxn()
+ else:
+ msg = msg_blocktxn()
+ msg.block_transactions.blockhash = block.sha256
+ msg.block_transactions.transactions = [block.vtx[0]]
+ test_node.send_and_ping(msg)
+ assert_equal(int(node.getbestblockhash(), 16), block.sha256)
+
+ # Create a chain of transactions from given utxo, and add to a new block.
+ def build_block_with_transactions(self, node, utxo, num_transactions):
+ block = self.build_block_on_tip(node)
+
+ for i in range(num_transactions):
+ tx = CTransaction()
+ tx.vin.append(CTxIn(COutPoint(utxo[0], utxo[1]), b''))
+ tx.vout.append(CTxOut(utxo[2] - 1000, CScript([OP_TRUE])))
+ tx.rehash()
+ utxo = [tx.sha256, 0, tx.vout[0].nValue]
+ block.vtx.append(tx)
+
+ block.hashMerkleRoot = block.calc_merkle_root()
+ block.solve()
+ return block
+
+ # Test that we only receive getblocktxn requests for transactions that the
+ # node needs, and that responding to them causes the block to be
+ # reconstructed.
+ def test_getblocktxn_requests(self, node, test_node, version):
+ with_witness = (version==2)
+
+ def test_getblocktxn_response(compact_block, peer, expected_result):
+ msg = msg_cmpctblock(compact_block.to_p2p())
+ peer.send_and_ping(msg)
+ with mininode_lock:
+ assert(peer.last_getblocktxn is not None)
+ absolute_indexes = peer.last_getblocktxn.block_txn_request.to_absolute()
+ assert_equal(absolute_indexes, expected_result)
+
+ def test_tip_after_message(node, peer, msg, tip):
+ peer.send_and_ping(msg)
+ assert_equal(int(node.getbestblockhash(), 16), tip)
+
+ # First try announcing compactblocks that won't reconstruct, and verify
+ # that we receive getblocktxn messages back.
+ utxo = self.utxos.pop(0)
+
+ block = self.build_block_with_transactions(node, utxo, 5)
+ self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue])
+ comp_block = HeaderAndShortIDs()
+ comp_block.initialize_from_block(block, use_witness=with_witness)
+
+ test_getblocktxn_response(comp_block, test_node, [1, 2, 3, 4, 5])
+
+ msg_bt = msg_blocktxn()
+ if with_witness:
+ msg_bt = msg_witness_blocktxn() # serialize with witnesses
+ msg_bt.block_transactions = BlockTransactions(block.sha256, block.vtx[1:])
+ test_tip_after_message(node, test_node, msg_bt, block.sha256)
+
+ utxo = self.utxos.pop(0)
+ block = self.build_block_with_transactions(node, utxo, 5)
+ self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue])
+
+ # Now try interspersing the prefilled transactions
+ comp_block.initialize_from_block(block, prefill_list=[0, 1, 5], use_witness=with_witness)
+ test_getblocktxn_response(comp_block, test_node, [2, 3, 4])
+ msg_bt.block_transactions = BlockTransactions(block.sha256, block.vtx[2:5])
+ test_tip_after_message(node, test_node, msg_bt, block.sha256)
+
+ # Now try giving one transaction ahead of time.
+ utxo = self.utxos.pop(0)
+ block = self.build_block_with_transactions(node, utxo, 5)
+ self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue])
+ test_node.send_and_ping(msg_tx(block.vtx[1]))
+ assert(block.vtx[1].hash in node.getrawmempool())
+
+ # Prefill 4 out of the 6 transactions, and verify that only the one
+ # that was not in the mempool is requested.
+ comp_block.initialize_from_block(block, prefill_list=[0, 2, 3, 4], use_witness=with_witness)
+ test_getblocktxn_response(comp_block, test_node, [5])
+
+ msg_bt.block_transactions = BlockTransactions(block.sha256, [block.vtx[5]])
+ test_tip_after_message(node, test_node, msg_bt, block.sha256)
+
+ # Now provide all transactions to the node before the block is
+ # announced and verify reconstruction happens immediately.
+ utxo = self.utxos.pop(0)
+ block = self.build_block_with_transactions(node, utxo, 10)
+ self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue])
+ for tx in block.vtx[1:]:
+ test_node.send_message(msg_tx(tx))
+ test_node.sync_with_ping()
+ # Make sure all transactions were accepted.
+ mempool = node.getrawmempool()
+ for tx in block.vtx[1:]:
+ assert(tx.hash in mempool)
+
+ # Clear out last request.
+ with mininode_lock:
+ test_node.last_getblocktxn = None
+
+ # Send compact block
+ comp_block.initialize_from_block(block, prefill_list=[0], use_witness=with_witness)
+ test_tip_after_message(node, test_node, msg_cmpctblock(comp_block.to_p2p()), block.sha256)
+ with mininode_lock:
+ # Shouldn't have gotten a request for any transaction
+ assert(test_node.last_getblocktxn is None)
+
+ # Incorrectly responding to a getblocktxn shouldn't cause the block to be
+ # permanently failed.
+ def test_incorrect_blocktxn_response(self, node, test_node, version):
+ if (len(self.utxos) == 0):
+ self.make_utxos()
+ utxo = self.utxos.pop(0)
+
+ block = self.build_block_with_transactions(node, utxo, 10)
+ self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue])
+ # Relay the first 5 transactions from the block in advance
+ for tx in block.vtx[1:6]:
+ test_node.send_message(msg_tx(tx))
+ test_node.sync_with_ping()
+ # Make sure all transactions were accepted.
+ mempool = node.getrawmempool()
+ for tx in block.vtx[1:6]:
+ assert(tx.hash in mempool)
+
+ # Send compact block
+ comp_block = HeaderAndShortIDs()
+ comp_block.initialize_from_block(block, prefill_list=[0], use_witness=(version == 2))
+ test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
+ absolute_indexes = []
+ with mininode_lock:
+ assert(test_node.last_getblocktxn is not None)
+ absolute_indexes = test_node.last_getblocktxn.block_txn_request.to_absolute()
+ assert_equal(absolute_indexes, [6, 7, 8, 9, 10])
+
+ # Now give an incorrect response.
+ # Note that it's possible for bitcoind to be smart enough to know we're
+ # lying, since it could check to see if the shortid matches what we're
+ # sending, and eg disconnect us for misbehavior. If that behavior
+ # change were made, we could just modify this test by having a
+ # different peer provide the block further down, so that we're still
+ # verifying that the block isn't marked bad permanently. This is good
+ # enough for now.
+ msg = msg_blocktxn()
+ if version==2:
+ msg = msg_witness_blocktxn()
+ msg.block_transactions = BlockTransactions(block.sha256, [block.vtx[5]] + block.vtx[7:])
+ test_node.send_and_ping(msg)
+
+ # Tip should not have updated
+ assert_equal(int(node.getbestblockhash(), 16), block.hashPrevBlock)
+
+ # We should receive a getdata request
+ success = wait_until(lambda: test_node.last_getdata is not None, timeout=10)
+ assert(success)
+ assert_equal(len(test_node.last_getdata.inv), 1)
+ assert(test_node.last_getdata.inv[0].type == 2 or test_node.last_getdata.inv[0].type == 2|MSG_WITNESS_FLAG)
+ assert_equal(test_node.last_getdata.inv[0].hash, block.sha256)
+
+ # Deliver the block
+ if version==2:
+ test_node.send_and_ping(msg_witness_block(block))
+ else:
+ test_node.send_and_ping(msg_block(block))
+ assert_equal(int(node.getbestblockhash(), 16), block.sha256)
+
+ def test_getblocktxn_handler(self, node, test_node, version):
+ # bitcoind won't respond for blocks whose height is more than 15 blocks
+ # deep.
+ MAX_GETBLOCKTXN_DEPTH = 10
+ chain_height = node.getblockcount()
+ current_height = chain_height
+ while (current_height >= chain_height - MAX_GETBLOCKTXN_DEPTH):
+ block_hash = node.getblockhash(current_height)
+ block = FromHex(CBlock(), node.getblock(block_hash, False))
+
+ msg = msg_getblocktxn()
+ msg.block_txn_request = BlockTransactionsRequest(int(block_hash, 16), [])
+ num_to_request = random.randint(1, len(block.vtx))
+ msg.block_txn_request.from_absolute(sorted(random.sample(range(len(block.vtx)), num_to_request)))
+ test_node.send_message(msg)
+ success = wait_until(lambda: test_node.last_blocktxn is not None, timeout=10)
+ assert(success)
+
+ [tx.calc_sha256() for tx in block.vtx]
+ with mininode_lock:
+ assert_equal(test_node.last_blocktxn.block_transactions.blockhash, int(block_hash, 16))
+ all_indices = msg.block_txn_request.to_absolute()
+ for index in all_indices:
+ tx = test_node.last_blocktxn.block_transactions.transactions.pop(0)
+ tx.calc_sha256()
+ assert_equal(tx.sha256, block.vtx[index].sha256)
+ if version == 1:
+ # Witnesses should have been stripped
+ assert(tx.wit.is_null())
+ else:
+ # Check that the witness matches
+ assert_equal(tx.calc_sha256(True), block.vtx[index].calc_sha256(True))
+ test_node.last_blocktxn = None
+ current_height -= 1
+
+ # Next request should be ignored, as we're past the allowed depth.
+ block_hash = node.getblockhash(current_height)
+ msg.block_txn_request = BlockTransactionsRequest(int(block_hash, 16), [0])
+ test_node.send_and_ping(msg)
+ with mininode_lock:
+ assert_equal(test_node.last_blocktxn, None)
+
+ def test_compactblocks_not_at_tip(self, node, test_node):
+ # Test that requesting old compactblocks doesn't work.
+ MAX_CMPCTBLOCK_DEPTH = 5
+ new_blocks = []
+ for i in range(MAX_CMPCTBLOCK_DEPTH + 1):
+ test_node.clear_block_announcement()
+ new_blocks.append(node.generate(1)[0])
+ wait_until(test_node.received_block_announcement, timeout=30)
+
+ test_node.clear_block_announcement()
+ test_node.send_message(msg_getdata([CInv(4, int(new_blocks[0], 16))]))
+ success = wait_until(lambda: test_node.last_cmpctblock is not None, timeout=30)
+ assert(success)
+
+ test_node.clear_block_announcement()
+ node.generate(1)
+ wait_until(test_node.received_block_announcement, timeout=30)
+ test_node.clear_block_announcement()
+ test_node.send_message(msg_getdata([CInv(4, int(new_blocks[0], 16))]))
+ success = wait_until(lambda: test_node.last_block is not None, timeout=30)
+ assert(success)
+ with mininode_lock:
+ test_node.last_block.block.calc_sha256()
+ assert_equal(test_node.last_block.block.sha256, int(new_blocks[0], 16))
+
+ # Generate an old compactblock, and verify that it's not accepted.
+ cur_height = node.getblockcount()
+ hashPrevBlock = int(node.getblockhash(cur_height-5), 16)
+ block = self.build_block_on_tip(node)
+ block.hashPrevBlock = hashPrevBlock
+ block.solve()
+
+ comp_block = HeaderAndShortIDs()
+ comp_block.initialize_from_block(block)
+ test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
+
+ tips = node.getchaintips()
+ found = False
+ for x in tips:
+ if x["hash"] == block.hash:
+ assert_equal(x["status"], "headers-only")
+ found = True
+ break
+ assert(found)
+
+ # Requesting this block via getblocktxn should silently fail
+ # (to avoid fingerprinting attacks).
+ msg = msg_getblocktxn()
+ msg.block_txn_request = BlockTransactionsRequest(block.sha256, [0])
+ with mininode_lock:
+ test_node.last_blocktxn = None
+ test_node.send_and_ping(msg)
+ with mininode_lock:
+ assert(test_node.last_blocktxn is None)
+
+ def activate_segwit(self, node):
+ node.generate(144*3)
+ assert_equal(get_bip9_status(node, "segwit")["status"], 'active')
+
+ def test_end_to_end_block_relay(self, node, listeners):
+ utxo = self.utxos.pop(0)
+
+ block = self.build_block_with_transactions(node, utxo, 10)
+
+ [l.clear_block_announcement() for l in listeners]
+
+ # ToHex() won't serialize with witness, but this block has no witnesses
+ # anyway. TODO: repeat this test with witness tx's to a segwit node.
+ node.submitblock(ToHex(block))
+
+ for l in listeners:
+ wait_until(lambda: l.received_block_announcement(), timeout=30)
+ with mininode_lock:
+ for l in listeners:
+ assert(l.last_cmpctblock is not None)
+ l.last_cmpctblock.header_and_shortids.header.calc_sha256()
+ assert_equal(l.last_cmpctblock.header_and_shortids.header.sha256, block.sha256)
+
+ # Helper for enabling cb announcements
+ # Send the sendcmpct request and sync headers
+ def request_cb_announcements(self, peer, node, version):
+ tip = node.getbestblockhash()
+ peer.get_headers(locator=[int(tip, 16)], hashstop=0)
+
+ msg = msg_sendcmpct()
+ msg.version = version
+ msg.announce = True
+ peer.send_and_ping(msg)
+
+
+ def run_test(self):
+ # Setup the p2p connections and start up the network thread.
+ self.test_node = TestNode()
+ self.segwit_node = TestNode()
+ self.old_node = TestNode() # version 1 peer <--> segwit node
+
+ connections = []
+ connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.test_node))
+ connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1],
+ self.segwit_node, services=NODE_NETWORK|NODE_WITNESS))
+ connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1],
+ self.old_node, services=NODE_NETWORK))
+ self.test_node.add_connection(connections[0])
+ self.segwit_node.add_connection(connections[1])
+ self.old_node.add_connection(connections[2])
+
+ NetworkThread().start() # Start up network handling in another thread
+
+ # Test logic begins here
+ self.test_node.wait_for_verack()
+
+ # We will need UTXOs to construct transactions in later tests.
+ self.make_utxos()
+
+ print("Running tests, pre-segwit activation:")
+
+ print("\tTesting SENDCMPCT p2p message... ")
+ self.test_sendcmpct(self.nodes[0], self.test_node, 1)
+ sync_blocks(self.nodes)
+ self.test_sendcmpct(self.nodes[1], self.segwit_node, 2, old_node=self.old_node)
+ sync_blocks(self.nodes)
+
+ print("\tTesting compactblock construction...")
+ self.test_compactblock_construction(self.nodes[0], self.test_node, 1, False)
+ sync_blocks(self.nodes)
+ self.test_compactblock_construction(self.nodes[1], self.segwit_node, 2, False)
+ sync_blocks(self.nodes)
+
+ print("\tTesting compactblock requests... ")
+ self.test_compactblock_requests(self.nodes[0], self.test_node, 1, False)
+ sync_blocks(self.nodes)
+ self.test_compactblock_requests(self.nodes[1], self.segwit_node, 2, False)
+ sync_blocks(self.nodes)
+
+ print("\tTesting getblocktxn requests...")
+ self.test_getblocktxn_requests(self.nodes[0], self.test_node, 1)
+ sync_blocks(self.nodes)
+ self.test_getblocktxn_requests(self.nodes[1], self.segwit_node, 2)
+ sync_blocks(self.nodes)
+
+ print("\tTesting getblocktxn handler...")
+ self.test_getblocktxn_handler(self.nodes[0], self.test_node, 1)
+ sync_blocks(self.nodes)
+ self.test_getblocktxn_handler(self.nodes[1], self.segwit_node, 2)
+ self.test_getblocktxn_handler(self.nodes[1], self.old_node, 1)
+ sync_blocks(self.nodes)
+
+ print("\tTesting compactblock requests/announcements not at chain tip...")
+ self.test_compactblocks_not_at_tip(self.nodes[0], self.test_node)
+ sync_blocks(self.nodes)
+ self.test_compactblocks_not_at_tip(self.nodes[1], self.segwit_node)
+ self.test_compactblocks_not_at_tip(self.nodes[1], self.old_node)
+ sync_blocks(self.nodes)
+
+ print("\tTesting handling of incorrect blocktxn responses...")
+ self.test_incorrect_blocktxn_response(self.nodes[0], self.test_node, 1)
+ sync_blocks(self.nodes)
+ self.test_incorrect_blocktxn_response(self.nodes[1], self.segwit_node, 2)
+ sync_blocks(self.nodes)
+
+ # End-to-end block relay tests
+ print("\tTesting end-to-end block relay...")
+ self.request_cb_announcements(self.test_node, self.nodes[0], 1)
+ self.request_cb_announcements(self.old_node, self.nodes[1], 1)
+ self.request_cb_announcements(self.segwit_node, self.nodes[1], 2)
+ self.test_end_to_end_block_relay(self.nodes[0], [self.segwit_node, self.test_node, self.old_node])
+ self.test_end_to_end_block_relay(self.nodes[1], [self.segwit_node, self.test_node, self.old_node])
+
+ # Advance to segwit activation
+ print ("\nAdvancing to segwit activation\n")
+ self.activate_segwit(self.nodes[1])
+ print ("Running tests, post-segwit activation...")
+
+ print("\tTesting compactblock construction...")
+ self.test_compactblock_construction(self.nodes[1], self.old_node, 1, True)
+ self.test_compactblock_construction(self.nodes[1], self.segwit_node, 2, True)
+ sync_blocks(self.nodes)
+
+ print("\tTesting compactblock requests (unupgraded node)... ")
+ self.test_compactblock_requests(self.nodes[0], self.test_node, 1, True)
+
+ print("\tTesting getblocktxn requests (unupgraded node)...")
+ self.test_getblocktxn_requests(self.nodes[0], self.test_node, 1)
+
+ # Need to manually sync node0 and node1, because post-segwit activation,
+ # node1 will not download blocks from node0.
+ print("\tSyncing nodes...")
+ assert(self.nodes[0].getbestblockhash() != self.nodes[1].getbestblockhash())
+ while (self.nodes[0].getblockcount() > self.nodes[1].getblockcount()):
+ block_hash = self.nodes[0].getblockhash(self.nodes[1].getblockcount()+1)
+ self.nodes[1].submitblock(self.nodes[0].getblock(block_hash, False))
+ assert_equal(self.nodes[0].getbestblockhash(), self.nodes[1].getbestblockhash())
+
+ print("\tTesting compactblock requests (segwit node)... ")
+ self.test_compactblock_requests(self.nodes[1], self.segwit_node, 2, True)
+
+ print("\tTesting getblocktxn requests (segwit node)...")
+ self.test_getblocktxn_requests(self.nodes[1], self.segwit_node, 2)
+ sync_blocks(self.nodes)
+
+ print("\tTesting getblocktxn handler (segwit node should return witnesses)...")
+ self.test_getblocktxn_handler(self.nodes[1], self.segwit_node, 2)
+ self.test_getblocktxn_handler(self.nodes[1], self.old_node, 1)
+
+ # Test that if we submitblock to node1, we'll get a compact block
+ # announcement to all peers.
+ # (Post-segwit activation, blocks won't propagate from node0 to node1
+ # automatically, so don't bother testing a block announced to node0.)
+ print("\tTesting end-to-end block relay...")
+ self.request_cb_announcements(self.test_node, self.nodes[0], 1)
+ self.request_cb_announcements(self.old_node, self.nodes[1], 1)
+ self.request_cb_announcements(self.segwit_node, self.nodes[1], 2)
+ self.test_end_to_end_block_relay(self.nodes[1], [self.segwit_node, self.test_node, self.old_node])
+
+ print("\tTesting invalid index in cmpctblock message...")
+ self.test_invalid_cmpctblock_message()
+
+
+if __name__ == '__main__':
+ CompactBlocksTest().main()
diff --git a/qa/rpc-tests/p2p-feefilter.py b/qa/rpc-tests/p2p-feefilter.py
index cd0501a314..96d99d38a7 100755
--- a/qa/rpc-tests/p2p-feefilter.py
+++ b/qa/rpc-tests/p2p-feefilter.py
@@ -62,6 +62,7 @@ class FeeFilterTest(BitcoinTestFramework):
def run_test(self):
node1 = self.nodes[1]
+ node0 = self.nodes[0]
# Get out of IBD
node1.generate(1)
sync_blocks(self.nodes)
@@ -91,8 +92,17 @@ class FeeFilterTest(BitcoinTestFramework):
node1.settxfee(Decimal("0.00010000"))
[node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3)]
sync_mempools(self.nodes) # must be sure node 0 has received all txs
- time.sleep(10) # wait 10 secs to be sure its doesn't relay any
- assert(allInvsMatch([], test_node))
+
+ # Send one transaction from node0 that should be received, so that we
+ # we can sync the test on receipt (if node1's txs were relayed, they'd
+ # be received by the time this node0 tx is received). This is
+ # unfortunately reliant on the current relay behavior where we batch up
+ # to 35 entries in an inv, which means that when this next transaction
+ # is eligible for relay, the prior transactions from node1 are eligible
+ # as well.
+ node0.settxfee(Decimal("0.00020000"))
+ txids = [node0.sendtoaddress(node0.getnewaddress(), 1)]
+ assert(allInvsMatch(txids, test_node))
test_node.clear_invs()
# Remove fee filter and check that txs are received again
diff --git a/qa/rpc-tests/p2p-fullblocktest.py b/qa/rpc-tests/p2p-fullblocktest.py
index 17fd40ef1d..9aee81164f 100755
--- a/qa/rpc-tests/p2p-fullblocktest.py
+++ b/qa/rpc-tests/p2p-fullblocktest.py
@@ -25,9 +25,6 @@ We use the testing framework in which we expect a particular answer from
each test.
'''
-def hash160(s):
- return hashlib.new('ripemd160', sha256(s)).digest()
-
# Use this class for tests that require behavior other than normal "mininode" behavior.
# For now, it is used to serialize a bloated varint (b64).
class CBrokenBlock(CBlock):
diff --git a/qa/rpc-tests/p2p-mempool.py b/qa/rpc-tests/p2p-mempool.py
index 5d2daf39f8..382d7f1e82 100755
--- a/qa/rpc-tests/p2p-mempool.py
+++ b/qa/rpc-tests/p2p-mempool.py
@@ -63,7 +63,7 @@ class TestNode(NodeConnCB):
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)
+ success = wait_until(received_pong, timeout=timeout)
self.ping_counter += 1
return success
@@ -72,8 +72,11 @@ class TestNode(NodeConnCB):
self.send_message(msg_mempool())
class P2PMempoolTests(BitcoinTestFramework):
- def setup_chain(self):
- initialize_chain_clean(self.options.tmpdir, 2)
+
+ def __init__(self):
+ super().__init__()
+ self.setup_clean_chain = True
+ self.num_nodes = 2
def setup_network(self):
# Start a node with maxuploadtarget of 200 MB (/24h)
diff --git a/qa/rpc-tests/p2p-segwit.py b/qa/rpc-tests/p2p-segwit.py
index fa2c5d1f05..09ab1b80fc 100755
--- a/qa/rpc-tests/p2p-segwit.py
+++ b/qa/rpc-tests/p2p-segwit.py
@@ -166,10 +166,24 @@ class UTXO(object):
self.n = n
self.nValue = nValue
+# Helper for getting the script associated with a P2PKH
+def GetP2PKHScript(pubkeyhash):
+ return CScript([CScriptOp(OP_DUP), CScriptOp(OP_HASH160), pubkeyhash, CScriptOp(OP_EQUALVERIFY), CScriptOp(OP_CHECKSIG)])
+
+# Add signature for a P2PK witness program.
+def sign_P2PK_witness_input(script, txTo, inIdx, hashtype, value, key):
+ tx_hash = SegwitVersion1SignatureHash(script, txTo, inIdx, hashtype, value)
+ signature = key.sign(tx_hash) + chr(hashtype).encode('latin-1')
+ txTo.wit.vtxinwit[inIdx].scriptWitness.stack = [signature, script]
+ txTo.rehash()
+
class SegWitTest(BitcoinTestFramework):
- def setup_chain(self):
- initialize_chain_clean(self.options.tmpdir, 3)
+
+ def __init__(self):
+ super().__init__()
+ self.setup_clean_chain = True
+ self.num_nodes = 3
def add_options(self, parser):
parser.add_option("--oldbinary", dest="oldbinary",
@@ -302,13 +316,18 @@ class SegWitTest(BitcoinTestFramework):
sync_blocks(self.nodes)
# We'll add an unnecessary witness to this transaction that would cause
- # it to be too large according to IsStandard.
+ # it to be non-standard, to test that violating policy with a witness before
+ # segwit activation doesn't blind a node to a transaction. Transactions
+ # rejected for having a witness before segwit activation shouldn't be added
+ # to the rejection cache.
tx3 = CTransaction()
tx3.vin.append(CTxIn(COutPoint(tx2.sha256, 0), CScript([p2sh_program])))
tx3.vout.append(CTxOut(tx2.vout[0].nValue-1000, scriptPubKey))
tx3.wit.vtxinwit.append(CTxInWitness())
tx3.wit.vtxinwit[0].scriptWitness.stack = [b'a'*400000]
tx3.rehash()
+ # Note that this should be rejected for the premature witness reason,
+ # rather than a policy check, since segwit hasn't activated yet.
self.std_node.test_transaction_acceptance(tx3, True, False, b'no-witness-yet')
# If we send without witness, it should be accepted.
@@ -904,14 +923,6 @@ class SegWitTest(BitcoinTestFramework):
# But eliminating the witness should fix it
self.test_node.test_transaction_acceptance(tx, with_witness=False, accepted=True)
- # Verify that inv's to test_node come with getdata's for non-witness tx's
- # Just tweak the transaction, announce it, and verify we get a getdata
- # for a normal tx
- tx.vout[0].scriptPubKey = CScript([OP_TRUE, OP_TRUE])
- tx.rehash()
- self.test_node.announce_tx_and_wait_for_getdata(tx)
- assert(self.test_node.last_getdata.inv[0].type == 1)
-
# Cleanup: mine the first transaction and update utxo
self.nodes[0].generate(1)
assert_equal(len(self.nodes[0].getrawmempool()), 0)
@@ -946,8 +957,7 @@ class SegWitTest(BitcoinTestFramework):
self.test_node.test_transaction_acceptance(tx, with_witness=True, accepted=False)
# Verify that removing the witness succeeds.
- # Re-announcing won't result in a getdata for ~2.5 minutes, so just
- # deliver the modified transaction.
+ self.test_node.announce_tx_and_wait_for_getdata(tx)
self.test_node.test_transaction_acceptance(tx, with_witness=False, accepted=True)
# Now try to add extra witness data to a valid witness tx.
@@ -961,8 +971,24 @@ class SegWitTest(BitcoinTestFramework):
tx3 = CTransaction()
tx3.vin.append(CTxIn(COutPoint(tx2.sha256, 0), b""))
- tx3.vout.append(CTxOut(tx2.vout[0].nValue-1000, CScript([OP_TRUE])))
tx3.wit.vtxinwit.append(CTxInWitness())
+
+ # Add too-large for IsStandard witness and check that it does not enter reject filter
+ p2sh_program = CScript([OP_TRUE])
+ p2sh_pubkey = hash160(p2sh_program)
+ witness_program2 = CScript([b'a'*400000])
+ tx3.vout.append(CTxOut(tx2.vout[0].nValue-1000, CScript([OP_HASH160, p2sh_pubkey, OP_EQUAL])))
+ tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_program2]
+ tx3.rehash()
+
+ # Node will not be blinded to the transaction
+ self.std_node.announce_tx_and_wait_for_getdata(tx3)
+ self.std_node.test_transaction_acceptance(tx3, True, False, b'tx-size')
+ self.std_node.announce_tx_and_wait_for_getdata(tx3)
+ self.std_node.test_transaction_acceptance(tx3, True, False, b'tx-size')
+
+ # Remove witness stuffing, instead add extra witness push on stack
+ tx3.vout[0] = CTxOut(tx2.vout[0].nValue-1000, CScript([OP_TRUE]))
tx3.wit.vtxinwit[0].scriptWitness.stack = [CScript([CScriptNum(1)]), witness_program ]
tx3.rehash()
@@ -1002,7 +1028,7 @@ class SegWitTest(BitcoinTestFramework):
def test_block_relay(self, segwit_activated):
print("\tTesting block relay")
- blocktype = 2|MSG_WITNESS_FLAG if segwit_activated else 2
+ blocktype = 2|MSG_WITNESS_FLAG
# test_node has set NODE_WITNESS, so all getdata requests should be for
# witness blocks.
@@ -1086,6 +1112,82 @@ class SegWitTest(BitcoinTestFramework):
self.old_node.announce_tx_and_wait_for_getdata(block4.vtx[0])
assert(block4.sha256 not in self.old_node.getdataset)
+ # V0 segwit outputs should be standard after activation, but not before.
+ def test_standardness_v0(self, segwit_activated):
+ print("\tTesting standardness of v0 outputs (%s activation)" % ("after" if segwit_activated else "before"))
+ assert(len(self.utxo))
+
+ witness_program = CScript([OP_TRUE])
+ witness_hash = sha256(witness_program)
+ scriptPubKey = CScript([OP_0, witness_hash])
+
+ p2sh_pubkey = hash160(witness_program)
+ p2sh_scriptPubKey = CScript([OP_HASH160, p2sh_pubkey, OP_EQUAL])
+
+ # First prepare a p2sh output (so that spending it will pass standardness)
+ p2sh_tx = CTransaction()
+ p2sh_tx.vin = [CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")]
+ p2sh_tx.vout = [CTxOut(self.utxo[0].nValue-1000, p2sh_scriptPubKey)]
+ p2sh_tx.rehash()
+
+ # Mine it on test_node to create the confirmed output.
+ self.test_node.test_transaction_acceptance(p2sh_tx, with_witness=True, accepted=True)
+ self.nodes[0].generate(1)
+ sync_blocks(self.nodes)
+
+ # Now test standardness of v0 P2WSH outputs.
+ # Start by creating a transaction with two outputs.
+ tx = CTransaction()
+ tx.vin = [CTxIn(COutPoint(p2sh_tx.sha256, 0), CScript([witness_program]))]
+ tx.vout = [CTxOut(p2sh_tx.vout[0].nValue-10000, scriptPubKey)]
+ tx.vout.append(CTxOut(8000, scriptPubKey)) # Might burn this later
+ tx.rehash()
+
+ self.std_node.test_transaction_acceptance(tx, with_witness=True, accepted=segwit_activated)
+
+ # Now create something that looks like a P2PKH output. This won't be spendable.
+ scriptPubKey = CScript([OP_0, hash160(witness_hash)])
+ tx2 = CTransaction()
+ if segwit_activated:
+ # if tx was accepted, then we spend the second output.
+ tx2.vin = [CTxIn(COutPoint(tx.sha256, 1), b"")]
+ tx2.vout = [CTxOut(7000, scriptPubKey)]
+ tx2.wit.vtxinwit.append(CTxInWitness())
+ tx2.wit.vtxinwit[0].scriptWitness.stack = [witness_program]
+ else:
+ # if tx wasn't accepted, we just re-spend the p2sh output we started with.
+ tx2.vin = [CTxIn(COutPoint(p2sh_tx.sha256, 0), CScript([witness_program]))]
+ tx2.vout = [CTxOut(p2sh_tx.vout[0].nValue-1000, scriptPubKey)]
+ tx2.rehash()
+
+ self.std_node.test_transaction_acceptance(tx2, with_witness=True, accepted=segwit_activated)
+
+ # Now update self.utxo for later tests.
+ tx3 = CTransaction()
+ if segwit_activated:
+ # tx and tx2 were both accepted. Don't bother trying to reclaim the
+ # P2PKH output; just send tx's first output back to an anyone-can-spend.
+ sync_mempools(self.nodes)
+ tx3.vin = [CTxIn(COutPoint(tx.sha256, 0), b"")]
+ tx3.vout = [CTxOut(tx.vout[0].nValue-1000, CScript([OP_TRUE]))]
+ tx3.wit.vtxinwit.append(CTxInWitness())
+ tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_program]
+ tx3.rehash()
+ self.test_node.test_transaction_acceptance(tx3, with_witness=True, accepted=True)
+ else:
+ # tx and tx2 didn't go anywhere; just clean up the p2sh_tx output.
+ tx3.vin = [CTxIn(COutPoint(p2sh_tx.sha256, 0), CScript([witness_program]))]
+ tx3.vout = [CTxOut(p2sh_tx.vout[0].nValue-1000, witness_program)]
+ tx3.rehash()
+ self.test_node.test_transaction_acceptance(tx3, with_witness=True, accepted=True)
+
+ self.nodes[0].generate(1)
+ sync_blocks(self.nodes)
+ self.utxo.pop(0)
+ self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue))
+ assert_equal(len(self.nodes[1].getrawmempool()), 0)
+
+
# Verify that future segwit upgraded transactions are non-standard,
# but valid in blocks. Can run this before and after segwit activation.
def test_segwit_versions(self):
@@ -1232,13 +1334,6 @@ class SegWitTest(BitcoinTestFramework):
sync_blocks(self.nodes)
self.utxo.pop(0)
- # Add signature for a P2PK witness program.
- def sign_P2PK_witness_input(script, txTo, inIdx, hashtype, value, key):
- tx_hash = SegwitVersion1SignatureHash(script, txTo, inIdx, hashtype, value)
- signature = key.sign(tx_hash) + chr(hashtype).encode('latin-1')
- txTo.wit.vtxinwit[inIdx].scriptWitness.stack = [signature, script]
- txTo.rehash()
-
# Test each hashtype
prev_utxo = UTXO(tx.sha256, 0, tx.vout[0].nValue)
for sigflag in [ 0, SIGHASH_ANYONECANPAY ]:
@@ -1293,6 +1388,9 @@ class SegWitTest(BitcoinTestFramework):
block = self.build_next_block()
used_sighash_single_out_of_bounds = False
for i in range(NUM_TESTS):
+ # Ping regularly to keep the connection alive
+ if (not i % 100):
+ self.test_node.sync_with_ping()
# Choose random number of inputs to use.
num_inputs = random.randint(1, 10)
# Create a slight bias for producing more utxos
@@ -1349,7 +1447,7 @@ class SegWitTest(BitcoinTestFramework):
tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b""))
tx2.vout.append(CTxOut(tx.vout[0].nValue, CScript([OP_TRUE])))
- script = CScript([CScriptOp(OP_DUP), CScriptOp(OP_HASH160), pubkeyhash, CScriptOp(OP_EQUALVERIFY), CScriptOp(OP_CHECKSIG)])
+ script = GetP2PKHScript(pubkeyhash)
sig_hash = SegwitVersion1SignatureHash(script, tx2, 0, SIGHASH_ALL, tx.vout[0].nValue)
signature = key.sign(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
@@ -1612,6 +1710,211 @@ class SegWitTest(BitcoinTestFramework):
assert(block_version & (1 << VB_WITNESS_BIT) != 0)
self.nodes[0].setmocktime(0) # undo mocktime
+ # Uncompressed pubkeys are no longer supported in default relay policy,
+ # but (for now) are still valid in blocks.
+ def test_uncompressed_pubkey(self):
+ print("\tTesting uncompressed pubkeys")
+ # Segwit transactions using uncompressed pubkeys are not accepted
+ # under default policy, but should still pass consensus.
+ key = CECKey()
+ key.set_secretbytes(b"9")
+ key.set_compressed(False)
+ pubkey = CPubKey(key.get_pubkey())
+ assert_equal(len(pubkey), 65) # This should be an uncompressed pubkey
+
+ assert(len(self.utxo) > 0)
+ utxo = self.utxo.pop(0)
+
+ # Test 1: P2WPKH
+ # First create a P2WPKH output that uses an uncompressed pubkey
+ pubkeyhash = hash160(pubkey)
+ scriptPKH = CScript([OP_0, pubkeyhash])
+ tx = CTransaction()
+ tx.vin.append(CTxIn(COutPoint(utxo.sha256, utxo.n), b""))
+ tx.vout.append(CTxOut(utxo.nValue-1000, scriptPKH))
+ tx.rehash()
+
+ # Confirm it in a block.
+ block = self.build_next_block()
+ self.update_witness_block_with_transactions(block, [tx])
+ self.test_node.test_witness_block(block, accepted=True)
+
+ # Now try to spend it. Send it to a P2WSH output, which we'll
+ # use in the next test.
+ witness_program = CScript([pubkey, CScriptOp(OP_CHECKSIG)])
+ witness_hash = sha256(witness_program)
+ scriptWSH = CScript([OP_0, witness_hash])
+
+ tx2 = CTransaction()
+ tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b""))
+ tx2.vout.append(CTxOut(tx.vout[0].nValue-1000, scriptWSH))
+ script = GetP2PKHScript(pubkeyhash)
+ sig_hash = SegwitVersion1SignatureHash(script, tx2, 0, SIGHASH_ALL, tx.vout[0].nValue)
+ signature = key.sign(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
+ tx2.wit.vtxinwit.append(CTxInWitness())
+ tx2.wit.vtxinwit[0].scriptWitness.stack = [ signature, pubkey ]
+ tx2.rehash()
+
+ # Should fail policy test.
+ self.test_node.test_transaction_acceptance(tx2, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)')
+ # But passes consensus.
+ block = self.build_next_block()
+ self.update_witness_block_with_transactions(block, [tx2])
+ self.test_node.test_witness_block(block, accepted=True)
+
+ # Test 2: P2WSH
+ # Try to spend the P2WSH output created in last test.
+ # Send it to a P2SH(P2WSH) output, which we'll use in the next test.
+ p2sh_witness_hash = hash160(scriptWSH)
+ scriptP2SH = CScript([OP_HASH160, p2sh_witness_hash, OP_EQUAL])
+ scriptSig = CScript([scriptWSH])
+
+ tx3 = CTransaction()
+ tx3.vin.append(CTxIn(COutPoint(tx2.sha256, 0), b""))
+ tx3.vout.append(CTxOut(tx2.vout[0].nValue-1000, scriptP2SH))
+ tx3.wit.vtxinwit.append(CTxInWitness())
+ sign_P2PK_witness_input(witness_program, tx3, 0, SIGHASH_ALL, tx2.vout[0].nValue, key)
+
+ # Should fail policy test.
+ self.test_node.test_transaction_acceptance(tx3, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)')
+ # But passes consensus.
+ block = self.build_next_block()
+ self.update_witness_block_with_transactions(block, [tx3])
+ self.test_node.test_witness_block(block, accepted=True)
+
+ # Test 3: P2SH(P2WSH)
+ # Try to spend the P2SH output created in the last test.
+ # Send it to a P2PKH output, which we'll use in the next test.
+ scriptPubKey = GetP2PKHScript(pubkeyhash)
+ tx4 = CTransaction()
+ tx4.vin.append(CTxIn(COutPoint(tx3.sha256, 0), scriptSig))
+ tx4.vout.append(CTxOut(tx3.vout[0].nValue-1000, scriptPubKey))
+ tx4.wit.vtxinwit.append(CTxInWitness())
+ sign_P2PK_witness_input(witness_program, tx4, 0, SIGHASH_ALL, tx3.vout[0].nValue, key)
+
+ # Should fail policy test.
+ self.test_node.test_transaction_acceptance(tx4, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)')
+ block = self.build_next_block()
+ self.update_witness_block_with_transactions(block, [tx4])
+ self.test_node.test_witness_block(block, accepted=True)
+
+ # Test 4: Uncompressed pubkeys should still be valid in non-segwit
+ # transactions.
+ tx5 = CTransaction()
+ tx5.vin.append(CTxIn(COutPoint(tx4.sha256, 0), b""))
+ tx5.vout.append(CTxOut(tx4.vout[0].nValue-1000, CScript([OP_TRUE])))
+ (sig_hash, err) = SignatureHash(scriptPubKey, tx5, 0, SIGHASH_ALL)
+ signature = key.sign(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
+ tx5.vin[0].scriptSig = CScript([signature, pubkey])
+ tx5.rehash()
+ # Should pass policy and consensus.
+ self.test_node.test_transaction_acceptance(tx5, True, True)
+ block = self.build_next_block()
+ self.update_witness_block_with_transactions(block, [tx5])
+ self.test_node.test_witness_block(block, accepted=True)
+ self.utxo.append(UTXO(tx5.sha256, 0, tx5.vout[0].nValue))
+
+ def test_non_standard_witness(self):
+ print("\tTesting detection of non-standard P2WSH witness")
+ pad = chr(1).encode('latin-1')
+
+ # Create scripts for tests
+ scripts = []
+ scripts.append(CScript([OP_DROP] * 100))
+ scripts.append(CScript([OP_DROP] * 99))
+ scripts.append(CScript([pad * 59] * 59 + [OP_DROP] * 60))
+ scripts.append(CScript([pad * 59] * 59 + [OP_DROP] * 61))
+
+ p2wsh_scripts = []
+
+ assert(len(self.utxo))
+ tx = CTransaction()
+ tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b""))
+
+ # For each script, generate a pair of P2WSH and P2SH-P2WSH output.
+ outputvalue = (self.utxo[0].nValue - 1000) // (len(scripts) * 2)
+ for i in scripts:
+ p2wsh = CScript([OP_0, sha256(i)])
+ p2sh = hash160(p2wsh)
+ p2wsh_scripts.append(p2wsh)
+ tx.vout.append(CTxOut(outputvalue, p2wsh))
+ tx.vout.append(CTxOut(outputvalue, CScript([OP_HASH160, p2sh, OP_EQUAL])))
+ tx.rehash()
+ txid = tx.sha256
+ self.test_node.test_transaction_acceptance(tx, with_witness=False, accepted=True)
+
+ self.nodes[0].generate(1)
+ sync_blocks(self.nodes)
+
+ # Creating transactions for tests
+ p2wsh_txs = []
+ p2sh_txs = []
+ for i in range(len(scripts)):
+ p2wsh_tx = CTransaction()
+ p2wsh_tx.vin.append(CTxIn(COutPoint(txid,i*2)))
+ p2wsh_tx.vout.append(CTxOut(outputvalue - 5000, CScript([OP_0, hash160(hex_str_to_bytes(""))])))
+ p2wsh_tx.wit.vtxinwit.append(CTxInWitness())
+ p2wsh_tx.rehash()
+ p2wsh_txs.append(p2wsh_tx)
+ p2sh_tx = CTransaction()
+ p2sh_tx.vin.append(CTxIn(COutPoint(txid,i*2+1), CScript([p2wsh_scripts[i]])))
+ p2sh_tx.vout.append(CTxOut(outputvalue - 5000, CScript([OP_0, hash160(hex_str_to_bytes(""))])))
+ p2sh_tx.wit.vtxinwit.append(CTxInWitness())
+ p2sh_tx.rehash()
+ p2sh_txs.append(p2sh_tx)
+
+ # Testing native P2WSH
+ # Witness stack size, excluding witnessScript, over 100 is non-standard
+ p2wsh_txs[0].wit.vtxinwit[0].scriptWitness.stack = [pad] * 101 + [scripts[0]]
+ self.std_node.test_transaction_acceptance(p2wsh_txs[0], True, False, b'bad-witness-nonstandard')
+ # Non-standard nodes should accept
+ self.test_node.test_transaction_acceptance(p2wsh_txs[0], True, True)
+
+ # Stack element size over 80 bytes is non-standard
+ p2wsh_txs[1].wit.vtxinwit[0].scriptWitness.stack = [pad * 81] * 100 + [scripts[1]]
+ self.std_node.test_transaction_acceptance(p2wsh_txs[1], True, False, b'bad-witness-nonstandard')
+ # Non-standard nodes should accept
+ self.test_node.test_transaction_acceptance(p2wsh_txs[1], True, True)
+ # Standard nodes should accept if element size is not over 80 bytes
+ p2wsh_txs[1].wit.vtxinwit[0].scriptWitness.stack = [pad * 80] * 100 + [scripts[1]]
+ self.std_node.test_transaction_acceptance(p2wsh_txs[1], True, True)
+
+ # witnessScript size at 3600 bytes is standard
+ p2wsh_txs[2].wit.vtxinwit[0].scriptWitness.stack = [pad, pad, scripts[2]]
+ self.test_node.test_transaction_acceptance(p2wsh_txs[2], True, True)
+ self.std_node.test_transaction_acceptance(p2wsh_txs[2], True, True)
+
+ # witnessScript size at 3601 bytes is non-standard
+ p2wsh_txs[3].wit.vtxinwit[0].scriptWitness.stack = [pad, pad, pad, scripts[3]]
+ self.std_node.test_transaction_acceptance(p2wsh_txs[3], True, False, b'bad-witness-nonstandard')
+ # Non-standard nodes should accept
+ self.test_node.test_transaction_acceptance(p2wsh_txs[3], True, True)
+
+ # Repeating the same tests with P2SH-P2WSH
+ p2sh_txs[0].wit.vtxinwit[0].scriptWitness.stack = [pad] * 101 + [scripts[0]]
+ self.std_node.test_transaction_acceptance(p2sh_txs[0], True, False, b'bad-witness-nonstandard')
+ self.test_node.test_transaction_acceptance(p2sh_txs[0], True, True)
+ p2sh_txs[1].wit.vtxinwit[0].scriptWitness.stack = [pad * 81] * 100 + [scripts[1]]
+ self.std_node.test_transaction_acceptance(p2sh_txs[1], True, False, b'bad-witness-nonstandard')
+ self.test_node.test_transaction_acceptance(p2sh_txs[1], True, True)
+ p2sh_txs[1].wit.vtxinwit[0].scriptWitness.stack = [pad * 80] * 100 + [scripts[1]]
+ self.std_node.test_transaction_acceptance(p2sh_txs[1], True, True)
+ p2sh_txs[2].wit.vtxinwit[0].scriptWitness.stack = [pad, pad, scripts[2]]
+ self.test_node.test_transaction_acceptance(p2sh_txs[2], True, True)
+ self.std_node.test_transaction_acceptance(p2sh_txs[2], True, True)
+ p2sh_txs[3].wit.vtxinwit[0].scriptWitness.stack = [pad, pad, pad, scripts[3]]
+ self.std_node.test_transaction_acceptance(p2sh_txs[3], True, False, b'bad-witness-nonstandard')
+ self.test_node.test_transaction_acceptance(p2sh_txs[3], True, True)
+
+ self.nodes[0].generate(1) # Mine and clean up the mempool of non-standard node
+ # Valid but non-standard transactions in a block should be accepted by standard node
+ sync_blocks(self.nodes)
+ assert_equal(len(self.nodes[0].getrawmempool()), 0)
+ assert_equal(len(self.nodes[1].getrawmempool()), 0)
+
+ self.utxo.pop(0)
+
+
def run_test(self):
# Setup the p2p connections and start up the network thread.
self.test_node = TestNode() # sets NODE_WITNESS|NODE_NETWORK
@@ -1658,6 +1961,7 @@ class SegWitTest(BitcoinTestFramework):
self.test_witness_tx_relay_before_segwit_activation()
self.test_block_relay(segwit_activated=False)
self.test_p2sh_witness(segwit_activated=False)
+ self.test_standardness_v0(segwit_activated=False)
sync_blocks(self.nodes)
@@ -1679,9 +1983,12 @@ class SegWitTest(BitcoinTestFramework):
self.test_witness_input_length()
self.test_block_relay(segwit_activated=True)
self.test_tx_relay_after_segwit_activation()
+ self.test_standardness_v0(segwit_activated=True)
self.test_segwit_versions()
self.test_premature_coinbase_witness_spend()
+ self.test_uncompressed_pubkey()
self.test_signature_version_1()
+ self.test_non_standard_witness()
sync_blocks(self.nodes)
if self.test_upgrade:
self.test_upgrade_after_activation(self.nodes[2], 2)
diff --git a/qa/rpc-tests/p2p-versionbits-warning.py b/qa/rpc-tests/p2p-versionbits-warning.py
index 962cafef0b..fc3eddddee 100755
--- a/qa/rpc-tests/p2p-versionbits-warning.py
+++ b/qa/rpc-tests/p2p-versionbits-warning.py
@@ -6,6 +6,7 @@
from test_framework.mininode import *
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
+import re
import time
from test_framework.blocktools import create_block, create_coinbase
@@ -21,6 +22,10 @@ VB_THRESHOLD = 108 # versionbits activation threshold for regtest
VB_TOP_BITS = 0x20000000
VB_UNKNOWN_BIT = 27 # Choose a bit unassigned to any deployment
+WARN_UNKNOWN_RULES_MINED = "Unknown block versions being mined! It's possible unknown rules are in effect"
+WARN_UNKNOWN_RULES_ACTIVE = "unknown new rules activated (versionbit {})".format(VB_UNKNOWN_BIT)
+VB_PATTERN = re.compile("^Warning.*versionbit")
+
# TestNode: bare-bones "peer". Used mostly as a conduit for a test to sending
# p2p messages to a node, generating the messages in the main testing logic.
class TestNode(NodeConnCB):
@@ -65,16 +70,12 @@ class VersionBitsWarningTest(BitcoinTestFramework):
self.num_nodes = 1
def setup_network(self):
- self.nodes = []
self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt")
# Open and close to create zero-length file
- with open(self.alert_filename, 'w') as f:
+ with open(self.alert_filename, 'w', encoding='utf8') as _:
pass
- self.node_options = ["-debug", "-logtimemicros=1", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""]
- self.nodes.append(start_node(0, self.options.tmpdir, self.node_options))
-
- import re
- self.vb_pattern = re.compile("^Warning.*versionbit")
+ self.extra_args = [["-debug", "-logtimemicros=1", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""]]
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args)
# Send numblocks blocks via peer with nVersionToUse set.
def send_blocks_with_version(self, peer, numblocks, nVersionToUse):
@@ -83,7 +84,7 @@ class VersionBitsWarningTest(BitcoinTestFramework):
block_time = self.nodes[0].getblockheader(tip)["time"]+1
tip = int(tip, 16)
- for i in range(numblocks):
+ for _ in range(numblocks):
block = create_block(tip, create_coinbase(height+1), block_time)
block.nVersion = nVersionToUse
block.solve()
@@ -94,9 +95,9 @@ class VersionBitsWarningTest(BitcoinTestFramework):
peer.sync_with_ping()
def test_versionbits_in_alert_file(self):
- with open(self.alert_filename, 'r') as f:
+ with open(self.alert_filename, 'r', encoding='utf8') as f:
alert_text = f.read()
- assert(self.vb_pattern.match(alert_text))
+ assert(VB_PATTERN.match(alert_text))
def run_test(self):
# Setup the p2p connection and start up the network thread.
@@ -122,8 +123,10 @@ class VersionBitsWarningTest(BitcoinTestFramework):
# Fill rest of period with regular version blocks
self.nodes[0].generate(VB_PERIOD - VB_THRESHOLD + 1)
# Check that we're not getting any versionbit-related errors in
- # getinfo()
- assert(not self.vb_pattern.match(self.nodes[0].getinfo()["errors"]))
+ # get*info()
+ assert(not VB_PATTERN.match(self.nodes[0].getinfo()["errors"]))
+ assert(not VB_PATTERN.match(self.nodes[0].getmininginfo()["errors"]))
+ assert(not VB_PATTERN.match(self.nodes[0].getnetworkinfo()["warnings"]))
# 3. Now build one period of blocks with >= VB_THRESHOLD blocks signaling
# some unknown bit
@@ -132,30 +135,31 @@ class VersionBitsWarningTest(BitcoinTestFramework):
# Might not get a versionbits-related alert yet, as we should
# have gotten a different alert due to more than 51/100 blocks
# being of unexpected version.
- # Check that getinfo() shows some kind of error.
- assert(len(self.nodes[0].getinfo()["errors"]) != 0)
+ # Check that get*info() shows some kind of error.
+ assert(WARN_UNKNOWN_RULES_MINED in self.nodes[0].getinfo()["errors"])
+ assert(WARN_UNKNOWN_RULES_MINED in self.nodes[0].getmininginfo()["errors"])
+ assert(WARN_UNKNOWN_RULES_MINED in self.nodes[0].getnetworkinfo()["warnings"])
# Mine a period worth of expected blocks so the generic block-version warning
# is cleared, and restart the node. This should move the versionbit state
# to ACTIVE.
self.nodes[0].generate(VB_PERIOD)
- stop_node(self.nodes[0], 0)
- wait_bitcoinds()
+ stop_nodes(self.nodes)
# Empty out the alert file
- with open(self.alert_filename, 'w') as f:
+ with open(self.alert_filename, 'w', encoding='utf8') as _:
pass
- self.nodes[0] = start_node(0, self.options.tmpdir, ["-debug", "-logtimemicros=1", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""])
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args)
# Connecting one block should be enough to generate an error.
self.nodes[0].generate(1)
- assert(len(self.nodes[0].getinfo()["errors"]) != 0)
- stop_node(self.nodes[0], 0)
- wait_bitcoinds()
+ assert(WARN_UNKNOWN_RULES_ACTIVE in self.nodes[0].getinfo()["errors"])
+ assert(WARN_UNKNOWN_RULES_ACTIVE in self.nodes[0].getmininginfo()["errors"])
+ assert(WARN_UNKNOWN_RULES_ACTIVE in self.nodes[0].getnetworkinfo()["warnings"])
+ stop_nodes(self.nodes)
self.test_versionbits_in_alert_file()
# Test framework expects the node to still be running...
- self.nodes[0] = start_node(0, self.options.tmpdir, ["-debug", "-logtimemicros=1", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""])
-
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args)
if __name__ == '__main__':
VersionBitsWarningTest().main()
diff --git a/qa/rpc-tests/preciousblock.py b/qa/rpc-tests/preciousblock.py
new file mode 100755
index 0000000000..854dcc7251
--- /dev/null
+++ b/qa/rpc-tests/preciousblock.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python3
+# Copyright (c) 2015 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 PreciousBlock code
+#
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import *
+
+def unidirectional_node_sync_via_rpc(node_src, node_dest):
+ blocks_to_copy = []
+ blockhash = node_src.getbestblockhash()
+ while True:
+ try:
+ assert(len(node_dest.getblock(blockhash, False)) > 0)
+ break
+ except:
+ blocks_to_copy.append(blockhash)
+ blockhash = node_src.getblockheader(blockhash, True)['previousblockhash']
+ blocks_to_copy.reverse()
+ for blockhash in blocks_to_copy:
+ blockdata = node_src.getblock(blockhash, False)
+ assert(node_dest.submitblock(blockdata) in (None, 'inconclusive'))
+
+def node_sync_via_rpc(nodes):
+ for node_src in nodes:
+ for node_dest in nodes:
+ if node_src is node_dest:
+ continue
+ unidirectional_node_sync_via_rpc(node_src, node_dest)
+
+class PreciousTest(BitcoinTestFramework):
+ def setup_chain(self):
+ print("Initializing test directory "+self.options.tmpdir)
+ initialize_chain_clean(self.options.tmpdir, 3)
+
+ def setup_network(self):
+ self.nodes = []
+ self.is_network_split = False
+ self.nodes.append(start_node(0, self.options.tmpdir, ["-debug"]))
+ self.nodes.append(start_node(1, self.options.tmpdir, ["-debug"]))
+ self.nodes.append(start_node(2, self.options.tmpdir, ["-debug"]))
+
+ def run_test(self):
+ print("Ensure submitblock can in principle reorg to a competing chain")
+ self.nodes[0].generate(1)
+ assert(self.nodes[0].getblockcount() == 1)
+ (hashY, hashZ) = self.nodes[1].generate(2)
+ assert(self.nodes[1].getblockcount() == 2)
+ node_sync_via_rpc(self.nodes[0:3])
+ assert(self.nodes[0].getbestblockhash() == hashZ)
+
+ print("Mine blocks A-B-C on Node 0")
+ (hashA, hashB, hashC) = self.nodes[0].generate(3)
+ assert(self.nodes[0].getblockcount() == 5)
+ print("Mine competing blocks E-F-G on Node 1")
+ (hashE, hashF, hashG) = self.nodes[1].generate(3)
+ assert(self.nodes[1].getblockcount() == 5)
+ assert(hashC != hashG)
+ print("Connect nodes and check no reorg occurs")
+ # Submit competing blocks via RPC so any reorg should occur before we proceed (no way to wait on inaction for p2p sync)
+ node_sync_via_rpc(self.nodes[0:2])
+ connect_nodes_bi(self.nodes,0,1)
+ assert(self.nodes[0].getbestblockhash() == hashC)
+ assert(self.nodes[1].getbestblockhash() == hashG)
+ print("Make Node0 prefer block G")
+ self.nodes[0].preciousblock(hashG)
+ assert(self.nodes[0].getbestblockhash() == hashG)
+ print("Make Node0 prefer block C again")
+ self.nodes[0].preciousblock(hashC)
+ assert(self.nodes[0].getbestblockhash() == hashC)
+ print("Make Node1 prefer block C")
+ self.nodes[1].preciousblock(hashC)
+ sync_chain(self.nodes[0:2]) # wait because node 1 may not have downloaded hashC
+ assert(self.nodes[1].getbestblockhash() == hashC)
+ print("Make Node1 prefer block G again")
+ self.nodes[1].preciousblock(hashG)
+ assert(self.nodes[1].getbestblockhash() == hashG)
+ print("Make Node0 prefer block G again")
+ self.nodes[0].preciousblock(hashG)
+ assert(self.nodes[0].getbestblockhash() == hashG)
+ print("Make Node1 prefer block C again")
+ self.nodes[1].preciousblock(hashC)
+ assert(self.nodes[1].getbestblockhash() == hashC)
+ print("Mine another block (E-F-G-)H on Node 0 and reorg Node 1")
+ self.nodes[0].generate(1)
+ assert(self.nodes[0].getblockcount() == 6)
+ sync_blocks(self.nodes[0:2])
+ hashH = self.nodes[0].getbestblockhash()
+ assert(self.nodes[1].getbestblockhash() == hashH)
+ print("Node1 should not be able to prefer block C anymore")
+ self.nodes[1].preciousblock(hashC)
+ assert(self.nodes[1].getbestblockhash() == hashH)
+ print("Mine competing blocks I-J-K-L on Node 2")
+ self.nodes[2].generate(4)
+ assert(self.nodes[2].getblockcount() == 6)
+ hashL = self.nodes[2].getbestblockhash()
+ print("Connect nodes and check no reorg occurs")
+ node_sync_via_rpc(self.nodes[0:3])
+ connect_nodes_bi(self.nodes,1,2)
+ connect_nodes_bi(self.nodes,0,2)
+ assert(self.nodes[0].getbestblockhash() == hashH)
+ assert(self.nodes[1].getbestblockhash() == hashH)
+ assert(self.nodes[2].getbestblockhash() == hashL)
+ print("Make Node1 prefer block L")
+ self.nodes[1].preciousblock(hashL)
+ assert(self.nodes[1].getbestblockhash() == hashL)
+ print("Make Node2 prefer block H")
+ self.nodes[2].preciousblock(hashH)
+ assert(self.nodes[2].getbestblockhash() == hashH)
+
+if __name__ == '__main__':
+ PreciousTest().main()
diff --git a/qa/rpc-tests/pruning.py b/qa/rpc-tests/pruning.py
index 7cbe69c29b..287dbc776e 100755
--- a/qa/rpc-tests/pruning.py
+++ b/qa/rpc-tests/pruning.py
@@ -157,7 +157,10 @@ class PruneTest(BitcoinTestFramework):
print("Usage possibly still high bc of stale blocks in block files:", calc_usage(self.prunedir))
print("Mine 220 more blocks so we have requisite history (some blocks will be big and cause pruning of previous chain)")
- self.nodes[0].generate(220) #node 0 has many large tx's in its mempool from the disconnects
+ for i in range(22):
+ # This can be slow, so do this in multiple RPC calls to avoid
+ # RPC timeouts.
+ self.nodes[0].generate(10) #node 0 has many large tx's in its mempool from the disconnects
sync_blocks(self.nodes[0:3], timeout=300)
usage = calc_usage(self.prunedir)
diff --git a/qa/rpc-tests/reindex.py b/qa/rpc-tests/reindex.py
index abbbb10336..25cf4c1679 100755
--- a/qa/rpc-tests/reindex.py
+++ b/qa/rpc-tests/reindex.py
@@ -7,7 +7,11 @@
# Test -reindex and -reindex-chainstate with CheckBlockIndex
#
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import *
+from test_framework.util import (
+ start_nodes,
+ stop_nodes,
+ assert_equal,
+)
import time
class ReindexTest(BitcoinTestFramework):
@@ -18,16 +22,14 @@ class ReindexTest(BitcoinTestFramework):
self.num_nodes = 1
def setup_network(self):
- self.nodes = []
- self.is_network_split = False
- self.nodes.append(start_node(0, self.options.tmpdir))
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
def reindex(self, justchainstate=False):
self.nodes[0].generate(3)
blockcount = self.nodes[0].getblockcount()
- stop_node(self.nodes[0], 0)
- wait_bitcoinds()
- self.nodes[0]=start_node(0, self.options.tmpdir, ["-debug", "-reindex-chainstate" if justchainstate else "-reindex", "-checkblockindex=1"])
+ stop_nodes(self.nodes)
+ extra_args = [["-debug", "-reindex-chainstate" if justchainstate else "-reindex", "-checkblockindex=1"]]
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args)
while self.nodes[0].getblockcount() < blockcount:
time.sleep(0.1)
assert_equal(self.nodes[0].getblockcount(), blockcount)
diff --git a/qa/rpc-tests/rest.py b/qa/rpc-tests/rest.py
index c9c2eaf7f3..b769cd71f2 100755
--- a/qa/rpc-tests/rest.py
+++ b/qa/rpc-tests/rest.py
@@ -179,14 +179,14 @@ class RESTTest (BitcoinTestFramework):
#do some invalid requests
json_request = '{"checkmempool'
response = http_post_call(url.hostname, url.port, '/rest/getutxos'+self.FORMAT_SEPARATOR+'json', json_request, True)
- assert_equal(response.status, 500) #must be a 500 because we send a invalid json request
+ assert_equal(response.status, 400) #must be a 400 because we send a invalid json request
json_request = '{"checkmempool'
response = http_post_call(url.hostname, url.port, '/rest/getutxos'+self.FORMAT_SEPARATOR+'bin', json_request, True)
- assert_equal(response.status, 500) #must be a 500 because we send a invalid bin request
+ assert_equal(response.status, 400) #must be a 400 because we send a invalid bin request
response = http_post_call(url.hostname, url.port, '/rest/getutxos/checkmempool'+self.FORMAT_SEPARATOR+'bin', '', True)
- assert_equal(response.status, 500) #must be a 500 because we send a invalid bin request
+ assert_equal(response.status, 400) #must be a 400 because we send a invalid bin request
#test limits
json_request = '/checkmempool/'
@@ -194,14 +194,14 @@ class RESTTest (BitcoinTestFramework):
json_request += txid+'-'+str(n)+'/'
json_request = json_request.rstrip("/")
response = http_post_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json', '', True)
- assert_equal(response.status, 500) #must be a 500 because we exceeding the limits
+ assert_equal(response.status, 400) #must be a 400 because we exceeding the limits
json_request = '/checkmempool/'
for x in range(0, 15):
json_request += txid+'-'+str(n)+'/'
json_request = json_request.rstrip("/")
response = http_post_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json', '', True)
- assert_equal(response.status, 200) #must be a 500 because we exceeding the limits
+ assert_equal(response.status, 200) #must be a 200 because we are within the limits
self.nodes[0].generate(1) #generate block to not affect upcoming tests
self.sync_all()
diff --git a/qa/rpc-tests/rpcbind_test.py b/qa/rpc-tests/rpcbind_test.py
index 572273566b..d78d0b884e 100755
--- a/qa/rpc-tests/rpcbind_test.py
+++ b/qa/rpc-tests/rpcbind_test.py
@@ -5,143 +5,104 @@
# Test for -rpcbind, as well as -rpcallowip and -rpcconnect
-# TODO extend this test from the test framework (like all other tests)
-
-import tempfile
-import traceback
-
+from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
from test_framework.netutil import *
-def run_bind_test(tmpdir, allow_ips, connect_to, addresses, expected):
- '''
- Start a node with requested rpcallowip and rpcbind parameters,
- then try to connect, and check if the set of bound addresses
- matches the expected set.
- '''
- expected = [(addr_to_hex(addr), port) for (addr, port) in expected]
- base_args = ['-disablewallet', '-nolisten']
- if allow_ips:
- base_args += ['-rpcallowip=' + x for x in allow_ips]
- binds = ['-rpcbind='+addr for addr in addresses]
- nodes = start_nodes(self.num_nodes, tmpdir, [base_args + binds], connect_to)
- try:
- pid = bitcoind_processes[0].pid
- assert_equal(set(get_bind_addrs(pid)), set(expected))
- finally:
- stop_nodes(nodes)
- wait_bitcoinds()
-
-def run_allowip_test(tmpdir, allow_ips, rpchost, rpcport):
- '''
- Start a node with rpcwallow IP, and request getinfo
- at a non-localhost IP.
- '''
- base_args = ['-disablewallet', '-nolisten'] + ['-rpcallowip='+x for x in allow_ips]
- nodes = start_nodes(self.num_nodes, tmpdir, [base_args])
- try:
- # connect to node through non-loopback interface
- url = "http://rt:rt@%s:%d" % (rpchost, rpcport,)
- node = get_rpc_proxy(url, 1)
- node.getinfo()
- finally:
- node = None # make sure connection will be garbage collected and closed
- stop_nodes(nodes)
- wait_bitcoinds()
-
-def run_test(tmpdir):
- assert(sys.platform.startswith('linux')) # due to OS-specific network stats queries, this test works only on Linux
- # find the first non-loopback interface for testing
- non_loopback_ip = None
- for name,ip in all_interfaces():
- if ip != '127.0.0.1':
- non_loopback_ip = ip
- break
- if non_loopback_ip is None:
- assert(not 'This test requires at least one non-loopback IPv4 interface')
- print("Using interface %s for testing" % non_loopback_ip)
+class RPCBindTest(BitcoinTestFramework):
- defaultport = rpc_port(0)
+ def __init__(self):
+ super().__init__()
+ self.setup_clean_chain = True
+ self.num_nodes = 1
- # check default without rpcallowip (IPv4 and IPv6 localhost)
- run_bind_test(tmpdir, None, '127.0.0.1', [],
- [('127.0.0.1', defaultport), ('::1', defaultport)])
- # check default with rpcallowip (IPv6 any)
- run_bind_test(tmpdir, ['127.0.0.1'], '127.0.0.1', [],
- [('::0', defaultport)])
- # check only IPv4 localhost (explicit)
- run_bind_test(tmpdir, ['127.0.0.1'], '127.0.0.1', ['127.0.0.1'],
- [('127.0.0.1', defaultport)])
- # check only IPv4 localhost (explicit) with alternative port
- run_bind_test(tmpdir, ['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171'],
- [('127.0.0.1', 32171)])
- # check only IPv4 localhost (explicit) with multiple alternative ports on same host
- run_bind_test(tmpdir, ['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171', '127.0.0.1:32172'],
- [('127.0.0.1', 32171), ('127.0.0.1', 32172)])
- # check only IPv6 localhost (explicit)
- run_bind_test(tmpdir, ['[::1]'], '[::1]', ['[::1]'],
- [('::1', defaultport)])
- # check both IPv4 and IPv6 localhost (explicit)
- run_bind_test(tmpdir, ['127.0.0.1'], '127.0.0.1', ['127.0.0.1', '[::1]'],
- [('127.0.0.1', defaultport), ('::1', defaultport)])
- # check only non-loopback interface
- run_bind_test(tmpdir, [non_loopback_ip], non_loopback_ip, [non_loopback_ip],
- [(non_loopback_ip, defaultport)])
-
- # Check that with invalid rpcallowip, we are denied
- run_allowip_test(tmpdir, [non_loopback_ip], non_loopback_ip, defaultport)
- try:
- run_allowip_test(tmpdir, ['1.1.1.1'], non_loopback_ip, defaultport)
- assert(not 'Connection not denied by rpcallowip as expected')
- except ValueError:
+ def setup_network(self):
pass
-def main():
- import optparse
-
- parser = optparse.OptionParser(usage="%prog [options]")
- parser.add_option("--nocleanup", dest="nocleanup", default=False, action="store_true",
- help="Leave bitcoinds and test.* datadir on exit or error")
- parser.add_option("--srcdir", dest="srcdir", default="../../src",
- help="Source directory containing bitcoind/bitcoin-cli (default: %default%)")
- parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"),
- help="Root directory for datadirs")
- (options, args) = parser.parse_args()
-
- os.environ['PATH'] = options.srcdir+":"+os.environ['PATH']
-
- check_json_precision()
-
- success = False
- nodes = []
- try:
- print("Initializing test directory "+options.tmpdir)
- if not os.path.isdir(options.tmpdir):
- os.makedirs(options.tmpdir)
- initialize_chain(options.tmpdir)
-
- run_test(options.tmpdir)
-
- success = True
-
- except AssertionError as e:
- print("Assertion failed: "+e.message)
- except Exception as e:
- print("Unexpected exception caught during testing: "+str(e))
- traceback.print_tb(sys.exc_info()[2])
-
- if not options.nocleanup:
- print("Cleaning up")
- wait_bitcoinds()
- shutil.rmtree(options.tmpdir)
+ def setup_nodes(self):
+ pass
- if success:
- print("Tests successful")
- sys.exit(0)
- else:
- print("Failed")
- sys.exit(1)
+ def run_bind_test(self, allow_ips, connect_to, addresses, expected):
+ '''
+ Start a node with requested rpcallowip and rpcbind parameters,
+ then try to connect, and check if the set of bound addresses
+ matches the expected set.
+ '''
+ expected = [(addr_to_hex(addr), port) for (addr, port) in expected]
+ base_args = ['-disablewallet', '-nolisten']
+ if allow_ips:
+ base_args += ['-rpcallowip=' + x for x in allow_ips]
+ binds = ['-rpcbind='+addr for addr in addresses]
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, [base_args + binds], connect_to)
+ try:
+ pid = bitcoind_processes[0].pid
+ assert_equal(set(get_bind_addrs(pid)), set(expected))
+ finally:
+ stop_nodes(self.nodes)
+
+ def run_allowip_test(self, allow_ips, rpchost, rpcport):
+ '''
+ Start a node with rpcallow IP, and request getnetworkinfo
+ at a non-localhost IP.
+ '''
+ base_args = ['-disablewallet', '-nolisten'] + ['-rpcallowip='+x for x in allow_ips]
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, [base_args])
+ try:
+ # connect to node through non-loopback interface
+ node = get_rpc_proxy(rpc_url(0, "%s:%d" % (rpchost, rpcport)), 0)
+ node.getnetworkinfo()
+ finally:
+ node = None # make sure connection will be garbage collected and closed
+ stop_nodes(self.nodes)
+
+ def run_test(self):
+ # due to OS-specific network stats queries, this test works only on Linux
+ assert(sys.platform.startswith('linux'))
+ # find the first non-loopback interface for testing
+ non_loopback_ip = None
+ for name,ip in all_interfaces():
+ if ip != '127.0.0.1':
+ non_loopback_ip = ip
+ break
+ if non_loopback_ip is None:
+ assert(not 'This test requires at least one non-loopback IPv4 interface')
+ print("Using interface %s for testing" % non_loopback_ip)
+
+ defaultport = rpc_port(0)
+
+ # check default without rpcallowip (IPv4 and IPv6 localhost)
+ self.run_bind_test(None, '127.0.0.1', [],
+ [('127.0.0.1', defaultport), ('::1', defaultport)])
+ # check default with rpcallowip (IPv6 any)
+ self.run_bind_test(['127.0.0.1'], '127.0.0.1', [],
+ [('::0', defaultport)])
+ # check only IPv4 localhost (explicit)
+ self.run_bind_test(['127.0.0.1'], '127.0.0.1', ['127.0.0.1'],
+ [('127.0.0.1', defaultport)])
+ # check only IPv4 localhost (explicit) with alternative port
+ self.run_bind_test(['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171'],
+ [('127.0.0.1', 32171)])
+ # check only IPv4 localhost (explicit) with multiple alternative ports on same host
+ self.run_bind_test(['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171', '127.0.0.1:32172'],
+ [('127.0.0.1', 32171), ('127.0.0.1', 32172)])
+ # check only IPv6 localhost (explicit)
+ self.run_bind_test(['[::1]'], '[::1]', ['[::1]'],
+ [('::1', defaultport)])
+ # check both IPv4 and IPv6 localhost (explicit)
+ self.run_bind_test(['127.0.0.1'], '127.0.0.1', ['127.0.0.1', '[::1]'],
+ [('127.0.0.1', defaultport), ('::1', defaultport)])
+ # check only non-loopback interface
+ self.run_bind_test([non_loopback_ip], non_loopback_ip, [non_loopback_ip],
+ [(non_loopback_ip, defaultport)])
+
+ # Check that with invalid rpcallowip, we are denied
+ self.run_allowip_test([non_loopback_ip], non_loopback_ip, defaultport)
+ try:
+ self.run_allowip_test(['1.1.1.1'], non_loopback_ip, defaultport)
+ assert(not 'Connection not denied by rpcallowip as expected')
+ except JSONRPCException:
+ pass
if __name__ == '__main__':
- main()
+ RPCBindTest().main()
diff --git a/qa/rpc-tests/segwit.py b/qa/rpc-tests/segwit.py
index d4c9a8afed..41a1b3b20f 100755
--- a/qa/rpc-tests/segwit.py
+++ b/qa/rpc-tests/segwit.py
@@ -9,9 +9,10 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
-from test_framework.mininode import sha256, ripemd160
-import os
-import shutil
+from test_framework.mininode import sha256, ripemd160, CTransaction, CTxIn, COutPoint, CTxOut
+from test_framework.address import script_to_p2sh, key_to_p2pkh
+from test_framework.script import CScript, OP_HASH160, OP_CHECKSIG, OP_0, hash160, OP_EQUAL, OP_DUP, OP_EQUALVERIFY, OP_1, OP_2, OP_CHECKMULTISIG
+from io import BytesIO
NODE_0 = 0
NODE_1 = 1
@@ -69,11 +70,17 @@ def getutxo(txid):
utxo["txid"] = txid
return utxo
+def find_unspent(node, min_value):
+ for utxo in node.listunspent():
+ if utxo['amount'] >= min_value:
+ return utxo
+
class SegWitTest(BitcoinTestFramework):
- def setup_chain(self):
- print("Initializing test directory "+self.options.tmpdir)
- initialize_chain_clean(self.options.tmpdir, 3)
+ def __init__(self):
+ super().__init__()
+ self.setup_clean_chain = True
+ self.num_nodes = 3
def setup_network(self):
self.nodes = []
@@ -117,8 +124,21 @@ class SegWitTest(BitcoinTestFramework):
sync_blocks(self.nodes)
def run_test(self):
- self.nodes[0].generate(160) #block 160
-
+ self.nodes[0].generate(161) #block 161
+
+ print("Verify sigops are counted in GBT with pre-BIP141 rules before the fork")
+ txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
+ tmpl = self.nodes[0].getblocktemplate({})
+ assert(tmpl['sigoplimit'] == 20000)
+ assert(tmpl['transactions'][0]['hash'] == txid)
+ assert(tmpl['transactions'][0]['sigops'] == 2)
+ tmpl = self.nodes[0].getblocktemplate({'rules':['segwit']})
+ assert(tmpl['sigoplimit'] == 20000)
+ assert(tmpl['transactions'][0]['hash'] == txid)
+ assert(tmpl['transactions'][0]['sigops'] == 2)
+ self.nodes[0].generate(1) #block 162
+
+ balance_presetup = self.nodes[0].getbalance()
self.pubkey = []
p2sh_ids = [] # p2sh_ids[NODE][VER] is an array of txids that spend to a witness version VER pkscript to an address for NODE embedded in p2sh
wit_ids = [] # wit_ids[NODE][VER] is an array of txids that spend to a witness version VER pkscript to an address for NODE via bare witness
@@ -137,18 +157,18 @@ class SegWitTest(BitcoinTestFramework):
for i in range(5):
for n in range(3):
for v in range(2):
- wit_ids[n][v].append(send_to_witness(v, self.nodes[0], self.nodes[0].listunspent()[0], self.pubkey[n], False, Decimal("49.999")))
- p2sh_ids[n][v].append(send_to_witness(v, self.nodes[0], self.nodes[0].listunspent()[0], self.pubkey[n], True, Decimal("49.999")))
+ wit_ids[n][v].append(send_to_witness(v, self.nodes[0], find_unspent(self.nodes[0], 50), self.pubkey[n], False, Decimal("49.999")))
+ p2sh_ids[n][v].append(send_to_witness(v, self.nodes[0], find_unspent(self.nodes[0], 50), self.pubkey[n], True, Decimal("49.999")))
- self.nodes[0].generate(1) #block 161
+ self.nodes[0].generate(1) #block 163
sync_blocks(self.nodes)
# Make sure all nodes recognize the transactions as theirs
- assert_equal(self.nodes[0].getbalance(), 60*50 - 60*50 + 20*Decimal("49.999") + 50)
+ assert_equal(self.nodes[0].getbalance(), balance_presetup - 60*50 + 20*Decimal("49.999") + 50)
assert_equal(self.nodes[1].getbalance(), 20*Decimal("49.999"))
assert_equal(self.nodes[2].getbalance(), 20*Decimal("49.999"))
- self.nodes[0].generate(262) #block 423
+ self.nodes[0].generate(260) #block 423
sync_blocks(self.nodes)
print("Verify default node can't accept any witness format txs before fork")
@@ -205,5 +225,380 @@ class SegWitTest(BitcoinTestFramework):
self.success_mine(self.nodes[0], p2sh_ids[NODE_0][WIT_V0][0], True) #block 434
self.success_mine(self.nodes[0], p2sh_ids[NODE_0][WIT_V1][0], True) #block 435
+ print("Verify sigops are counted in GBT with BIP141 rules after the fork")
+ txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
+ tmpl = self.nodes[0].getblocktemplate({'rules':['segwit']})
+ assert(tmpl['sigoplimit'] == 80000)
+ assert(tmpl['transactions'][0]['txid'] == txid)
+ assert(tmpl['transactions'][0]['sigops'] == 8)
+
+ print("Verify non-segwit miners get a valid GBT response after the fork")
+ send_to_witness(1, self.nodes[0], find_unspent(self.nodes[0], 50), self.pubkey[0], False, Decimal("49.998"))
+ try:
+ tmpl = self.nodes[0].getblocktemplate({})
+ assert(len(tmpl['transactions']) == 1) # Doesn't include witness tx
+ assert(tmpl['sigoplimit'] == 20000)
+ assert(tmpl['transactions'][0]['hash'] == txid)
+ assert(tmpl['transactions'][0]['sigops'] == 2)
+ assert(('!segwit' in tmpl['rules']) or ('segwit' not in tmpl['rules']))
+ except JSONRPCException:
+ # This is an acceptable outcome
+ pass
+
+ print("Verify behaviour of importaddress, addwitnessaddress and listunspent")
+
+ # Some public keys to be used later
+ pubkeys = [
+ "0363D44AABD0F1699138239DF2F042C3282C0671CC7A76826A55C8203D90E39242", # cPiM8Ub4heR9NBYmgVzJQiUH1if44GSBGiqaeJySuL2BKxubvgwb
+ "02D3E626B3E616FC8662B489C123349FECBFC611E778E5BE739B257EAE4721E5BF", # cPpAdHaD6VoYbW78kveN2bsvb45Q7G5PhaPApVUGwvF8VQ9brD97
+ "04A47F2CBCEFFA7B9BCDA184E7D5668D3DA6F9079AD41E422FA5FD7B2D458F2538A62F5BD8EC85C2477F39650BD391EA6250207065B2A81DA8B009FC891E898F0E", # 91zqCU5B9sdWxzMt1ca3VzbtVm2YM6Hi5Rxn4UDtxEaN9C9nzXV
+ "02A47F2CBCEFFA7B9BCDA184E7D5668D3DA6F9079AD41E422FA5FD7B2D458F2538", # cPQFjcVRpAUBG8BA9hzr2yEzHwKoMgLkJZBBtK9vJnvGJgMjzTbd
+ "036722F784214129FEB9E8129D626324F3F6716555B603FFE8300BBCB882151228", # cQGtcm34xiLjB1v7bkRa4V3aAc9tS2UTuBZ1UnZGeSeNy627fN66
+ "0266A8396EE936BF6D99D17920DB21C6C7B1AB14C639D5CD72B300297E416FD2EC", # cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K
+ "0450A38BD7F0AC212FEBA77354A9B036A32E0F7C81FC4E0C5ADCA7C549C4505D2522458C2D9AE3CEFD684E039194B72C8A10F9CB9D4764AB26FCC2718D421D3B84", # 92h2XPssjBpsJN5CqSP7v9a7cf2kgDunBC6PDFwJHMACM1rrVBJ
+ ]
+
+ # Import a compressed key and an uncompressed key, generate some multisig addresses
+ self.nodes[0].importprivkey("92e6XLo5jVAVwrQKPNTs93oQco8f8sDNBcpv73Dsrs397fQtFQn")
+ uncompressed_spendable_address = ["mvozP4UwyGD2mGZU4D2eMvMLPB9WkMmMQu"]
+ self.nodes[0].importprivkey("cNC8eQ5dg3mFAVePDX4ddmPYpPbw41r9bm2jd1nLJT77e6RrzTRR")
+ compressed_spendable_address = ["mmWQubrDomqpgSYekvsU7HWEVjLFHAakLe"]
+ assert ((self.nodes[0].validateaddress(uncompressed_spendable_address[0])['iscompressed'] == False))
+ assert ((self.nodes[0].validateaddress(compressed_spendable_address[0])['iscompressed'] == True))
+
+ self.nodes[0].importpubkey(pubkeys[0])
+ compressed_solvable_address = [key_to_p2pkh(pubkeys[0])]
+ self.nodes[0].importpubkey(pubkeys[1])
+ compressed_solvable_address.append(key_to_p2pkh(pubkeys[1]))
+ self.nodes[0].importpubkey(pubkeys[2])
+ uncompressed_solvable_address = [key_to_p2pkh(pubkeys[2])]
+
+ spendable_anytime = [] # These outputs should be seen anytime after importprivkey and addmultisigaddress
+ spendable_after_importaddress = [] # These outputs should be seen after importaddress
+ solvable_after_importaddress = [] # These outputs should be seen after importaddress but not spendable
+ unsolvable_after_importaddress = [] # These outputs should be unsolvable after importaddress
+ solvable_anytime = [] # These outputs should be solvable after importpubkey
+ unseen_anytime = [] # These outputs should never be seen
+
+ uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], compressed_spendable_address[0]]))
+ uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], uncompressed_spendable_address[0]]))
+ compressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_spendable_address[0]]))
+ uncompressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], uncompressed_solvable_address[0]]))
+ compressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_solvable_address[0]]))
+ compressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_solvable_address[0], compressed_solvable_address[1]]))
+ unknown_address = ["mtKKyoHabkk6e4ppT7NaM7THqPUt7AzPrT", "2NDP3jLWAFT8NDAiUa9qiE6oBt2awmMq7Dx"]
+
+ # Test multisig_without_privkey
+ # We have 2 public keys without private keys, use addmultisigaddress to add to wallet.
+ # Money sent to P2SH of multisig of this should only be seen after importaddress with the BASE58 P2SH address.
+
+ multisig_without_privkey_address = self.nodes[0].addmultisigaddress(2, [pubkeys[3], pubkeys[4]])
+ script = CScript([OP_2, hex_str_to_bytes(pubkeys[3]), hex_str_to_bytes(pubkeys[4]), OP_2, OP_CHECKMULTISIG])
+ solvable_after_importaddress.append(CScript([OP_HASH160, hash160(script), OP_EQUAL]))
+
+ for i in compressed_spendable_address:
+ v = self.nodes[0].validateaddress(i)
+ if (v['isscript']):
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ # bare and p2sh multisig with compressed keys should always be spendable
+ spendable_anytime.extend([bare, p2sh])
+ # P2WSH and P2SH(P2WSH) multisig with compressed keys are spendable after direct importaddress
+ spendable_after_importaddress.extend([p2wsh, p2sh_p2wsh])
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # normal P2PKH and P2PK with compressed keys should always be spendable
+ spendable_anytime.extend([p2pkh, p2pk])
+ # P2SH_P2PK, P2SH_P2PKH, and witness with compressed keys are spendable after direct importaddress
+ spendable_after_importaddress.extend([p2wpkh, p2sh_p2wpkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
+
+ for i in uncompressed_spendable_address:
+ v = self.nodes[0].validateaddress(i)
+ if (v['isscript']):
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ # bare and p2sh multisig with uncompressed keys should always be spendable
+ spendable_anytime.extend([bare, p2sh])
+ # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen
+ unseen_anytime.extend([p2wsh, p2sh_p2wsh])
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # normal P2PKH and P2PK with uncompressed keys should always be spendable
+ spendable_anytime.extend([p2pkh, p2pk])
+ # P2SH_P2PK and P2SH_P2PKH are spendable after direct importaddress
+ spendable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh])
+ # witness with uncompressed keys are never seen
+ unseen_anytime.extend([p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
+
+ for i in compressed_solvable_address:
+ v = self.nodes[0].validateaddress(i)
+ if (v['isscript']):
+ # Multisig without private is not seen after addmultisigaddress, but seen after importaddress
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ solvable_after_importaddress.extend([bare, p2sh, p2wsh, p2sh_p2wsh])
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # normal P2PKH and P2PK with compressed keys should always be seen
+ solvable_anytime.extend([p2pkh, p2pk])
+ # P2SH_P2PK, P2SH_P2PKH, and witness with compressed keys are seen after direct importaddress
+ solvable_after_importaddress.extend([p2wpkh, p2sh_p2wpkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
+
+ for i in uncompressed_solvable_address:
+ v = self.nodes[0].validateaddress(i)
+ if (v['isscript']):
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ # Base uncompressed multisig without private is not seen after addmultisigaddress, but seen after importaddress
+ solvable_after_importaddress.extend([bare, p2sh])
+ # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen
+ unseen_anytime.extend([p2wsh, p2sh_p2wsh])
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # normal P2PKH and P2PK with uncompressed keys should always be seen
+ solvable_anytime.extend([p2pkh, p2pk])
+ # P2SH_P2PK, P2SH_P2PKH with uncompressed keys are seen after direct importaddress
+ solvable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh])
+ # witness with uncompressed keys are never seen
+ unseen_anytime.extend([p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
+
+ op1 = CScript([OP_1])
+ op0 = CScript([OP_0])
+ # 2N7MGY19ti4KDMSzRfPAssP6Pxyuxoi6jLe is the P2SH(P2PKH) version of mjoE3sSrb8ByYEvgnC3Aox86u1CHnfJA4V
+ unsolvable_address = ["mjoE3sSrb8ByYEvgnC3Aox86u1CHnfJA4V", "2N7MGY19ti4KDMSzRfPAssP6Pxyuxoi6jLe", script_to_p2sh(op1), script_to_p2sh(op0)]
+ unsolvable_address_key = hex_str_to_bytes("02341AEC7587A51CDE5279E0630A531AEA2615A9F80B17E8D9376327BAEAA59E3D")
+ unsolvablep2pkh = CScript([OP_DUP, OP_HASH160, hash160(unsolvable_address_key), OP_EQUALVERIFY, OP_CHECKSIG])
+ unsolvablep2wshp2pkh = CScript([OP_0, sha256(unsolvablep2pkh)])
+ p2shop0 = CScript([OP_HASH160, hash160(op0), OP_EQUAL])
+ p2wshop1 = CScript([OP_0, sha256(op1)])
+ unsolvable_after_importaddress.append(unsolvablep2pkh)
+ unsolvable_after_importaddress.append(unsolvablep2wshp2pkh)
+ unsolvable_after_importaddress.append(op1) # OP_1 will be imported as script
+ unsolvable_after_importaddress.append(p2wshop1)
+ unseen_anytime.append(op0) # OP_0 will be imported as P2SH address with no script provided
+ unsolvable_after_importaddress.append(p2shop0)
+
+ spendable_txid = []
+ solvable_txid = []
+ spendable_txid.append(self.mine_and_test_listunspent(spendable_anytime, 2))
+ solvable_txid.append(self.mine_and_test_listunspent(solvable_anytime, 1))
+ self.mine_and_test_listunspent(spendable_after_importaddress + solvable_after_importaddress + unseen_anytime + unsolvable_after_importaddress, 0)
+
+ importlist = []
+ for i in compressed_spendable_address + uncompressed_spendable_address + compressed_solvable_address + uncompressed_solvable_address:
+ v = self.nodes[0].validateaddress(i)
+ if (v['isscript']):
+ bare = hex_str_to_bytes(v['hex'])
+ importlist.append(bytes_to_hex_str(bare))
+ importlist.append(bytes_to_hex_str(CScript([OP_0, sha256(bare)])))
+ else:
+ pubkey = hex_str_to_bytes(v['pubkey'])
+ p2pk = CScript([pubkey, OP_CHECKSIG])
+ p2pkh = CScript([OP_DUP, OP_HASH160, hash160(pubkey), OP_EQUALVERIFY, OP_CHECKSIG])
+ importlist.append(bytes_to_hex_str(p2pk))
+ importlist.append(bytes_to_hex_str(p2pkh))
+ importlist.append(bytes_to_hex_str(CScript([OP_0, hash160(pubkey)])))
+ importlist.append(bytes_to_hex_str(CScript([OP_0, sha256(p2pk)])))
+ importlist.append(bytes_to_hex_str(CScript([OP_0, sha256(p2pkh)])))
+
+ importlist.append(bytes_to_hex_str(unsolvablep2pkh))
+ importlist.append(bytes_to_hex_str(unsolvablep2wshp2pkh))
+ importlist.append(bytes_to_hex_str(op1))
+ importlist.append(bytes_to_hex_str(p2wshop1))
+
+ for i in importlist:
+ try:
+ self.nodes[0].importaddress(i,"",False,True)
+ except JSONRPCException as exp:
+ assert_equal(exp.error["message"], "The wallet already contains the private key for this address or script")
+
+ self.nodes[0].importaddress(script_to_p2sh(op0)) # import OP_0 as address only
+ self.nodes[0].importaddress(multisig_without_privkey_address) # Test multisig_without_privkey
+
+ spendable_txid.append(self.mine_and_test_listunspent(spendable_anytime + spendable_after_importaddress, 2))
+ solvable_txid.append(self.mine_and_test_listunspent(solvable_anytime + solvable_after_importaddress, 1))
+ self.mine_and_test_listunspent(unsolvable_after_importaddress, 1)
+ self.mine_and_test_listunspent(unseen_anytime, 0)
+
+ # addwitnessaddress should refuse to return a witness address if an uncompressed key is used or the address is
+ # not in the wallet
+ # note that no witness address should be returned by unsolvable addresses
+ # the multisig_without_privkey_address will fail because its keys were not added with importpubkey
+ for i in uncompressed_spendable_address + uncompressed_solvable_address + unknown_address + unsolvable_address + [multisig_without_privkey_address]:
+ try:
+ self.nodes[0].addwitnessaddress(i)
+ except JSONRPCException as exp:
+ assert_equal(exp.error["message"], "Public key or redeemscript not known to wallet, or the key is uncompressed")
+ else:
+ assert(False)
+
+ for i in compressed_spendable_address + compressed_solvable_address:
+ witaddress = self.nodes[0].addwitnessaddress(i)
+ # addwitnessaddress should return the same address if it is a known P2SH-witness address
+ assert_equal(witaddress, self.nodes[0].addwitnessaddress(witaddress))
+
+ spendable_txid.append(self.mine_and_test_listunspent(spendable_anytime + spendable_after_importaddress, 2))
+ solvable_txid.append(self.mine_and_test_listunspent(solvable_anytime + solvable_after_importaddress, 1))
+ self.mine_and_test_listunspent(unsolvable_after_importaddress, 1)
+ self.mine_and_test_listunspent(unseen_anytime, 0)
+
+ # Repeat some tests. This time we don't add witness scripts with importaddress
+ # Import a compressed key and an uncompressed key, generate some multisig addresses
+ self.nodes[0].importprivkey("927pw6RW8ZekycnXqBQ2JS5nPyo1yRfGNN8oq74HeddWSpafDJH")
+ uncompressed_spendable_address = ["mguN2vNSCEUh6rJaXoAVwY3YZwZvEmf5xi"]
+ self.nodes[0].importprivkey("cMcrXaaUC48ZKpcyydfFo8PxHAjpsYLhdsp6nmtB3E2ER9UUHWnw")
+ compressed_spendable_address = ["n1UNmpmbVUJ9ytXYXiurmGPQ3TRrXqPWKL"]
+
+ self.nodes[0].importpubkey(pubkeys[5])
+ compressed_solvable_address = [key_to_p2pkh(pubkeys[5])]
+ self.nodes[0].importpubkey(pubkeys[6])
+ uncompressed_solvable_address = [key_to_p2pkh(pubkeys[6])]
+
+ spendable_after_addwitnessaddress = [] # These outputs should be seen after importaddress
+ solvable_after_addwitnessaddress=[] # These outputs should be seen after importaddress but not spendable
+ unseen_anytime = [] # These outputs should never be seen
+
+ uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], compressed_spendable_address[0]]))
+ uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], uncompressed_spendable_address[0]]))
+ compressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_spendable_address[0]]))
+ uncompressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_solvable_address[0], uncompressed_solvable_address[0]]))
+ compressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_solvable_address[0]]))
+
+ premature_witaddress = []
+
+ for i in compressed_spendable_address:
+ v = self.nodes[0].validateaddress(i)
+ if (v['isscript']):
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ # P2WSH and P2SH(P2WSH) multisig with compressed keys are spendable after addwitnessaddress
+ spendable_after_addwitnessaddress.extend([p2wsh, p2sh_p2wsh])
+ premature_witaddress.append(script_to_p2sh(p2wsh))
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # P2WPKH, P2SH_P2WPKH are spendable after addwitnessaddress
+ spendable_after_addwitnessaddress.extend([p2wpkh, p2sh_p2wpkh])
+ premature_witaddress.append(script_to_p2sh(p2wpkh))
+
+ for i in uncompressed_spendable_address + uncompressed_solvable_address:
+ v = self.nodes[0].validateaddress(i)
+ if (v['isscript']):
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen
+ unseen_anytime.extend([p2wsh, p2sh_p2wsh])
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # P2WPKH, P2SH_P2WPKH with uncompressed keys are never seen
+ unseen_anytime.extend([p2wpkh, p2sh_p2wpkh])
+
+ for i in compressed_solvable_address:
+ v = self.nodes[0].validateaddress(i)
+ if (v['isscript']):
+ # P2WSH multisig without private key are seen after addwitnessaddress
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ solvable_after_addwitnessaddress.extend([p2wsh, p2sh_p2wsh])
+ premature_witaddress.append(script_to_p2sh(p2wsh))
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # P2SH_P2PK, P2SH_P2PKH with compressed keys are seen after addwitnessaddress
+ solvable_after_addwitnessaddress.extend([p2wpkh, p2sh_p2wpkh])
+ premature_witaddress.append(script_to_p2sh(p2wpkh))
+
+ self.mine_and_test_listunspent(spendable_after_addwitnessaddress + solvable_after_addwitnessaddress + unseen_anytime, 0)
+
+ # addwitnessaddress should refuse to return a witness address if an uncompressed key is used
+ # note that a multisig address returned by addmultisigaddress is not solvable until it is added with importaddress
+ # premature_witaddress are not accepted until the script is added with addwitnessaddress first
+ for i in uncompressed_spendable_address + uncompressed_solvable_address + premature_witaddress + [compressed_solvable_address[1]]:
+ try:
+ self.nodes[0].addwitnessaddress(i)
+ except JSONRPCException as exp:
+ assert_equal(exp.error["message"], "Public key or redeemscript not known to wallet, or the key is uncompressed")
+ else:
+ assert(False)
+
+ # after importaddress it should pass addwitnessaddress
+ v = self.nodes[0].validateaddress(compressed_solvable_address[1])
+ self.nodes[0].importaddress(v['hex'],"",False,True)
+ for i in compressed_spendable_address + compressed_solvable_address + premature_witaddress:
+ witaddress = self.nodes[0].addwitnessaddress(i)
+ assert_equal(witaddress, self.nodes[0].addwitnessaddress(witaddress))
+
+ spendable_txid.append(self.mine_and_test_listunspent(spendable_after_addwitnessaddress, 2))
+ solvable_txid.append(self.mine_and_test_listunspent(solvable_after_addwitnessaddress, 1))
+ self.mine_and_test_listunspent(unseen_anytime, 0)
+
+ # Check that spendable outputs are really spendable
+ self.create_and_mine_tx_from_txids(spendable_txid)
+
+ # import all the private keys so solvable addresses become spendable
+ self.nodes[0].importprivkey("cPiM8Ub4heR9NBYmgVzJQiUH1if44GSBGiqaeJySuL2BKxubvgwb")
+ self.nodes[0].importprivkey("cPpAdHaD6VoYbW78kveN2bsvb45Q7G5PhaPApVUGwvF8VQ9brD97")
+ self.nodes[0].importprivkey("91zqCU5B9sdWxzMt1ca3VzbtVm2YM6Hi5Rxn4UDtxEaN9C9nzXV")
+ self.nodes[0].importprivkey("cPQFjcVRpAUBG8BA9hzr2yEzHwKoMgLkJZBBtK9vJnvGJgMjzTbd")
+ self.nodes[0].importprivkey("cQGtcm34xiLjB1v7bkRa4V3aAc9tS2UTuBZ1UnZGeSeNy627fN66")
+ self.nodes[0].importprivkey("cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K")
+ self.create_and_mine_tx_from_txids(solvable_txid)
+
+ def mine_and_test_listunspent(self, script_list, ismine):
+ utxo = find_unspent(self.nodes[0], 50)
+ tx = CTransaction()
+ tx.vin.append(CTxIn(COutPoint(int('0x'+utxo['txid'],0), utxo['vout'])))
+ for i in script_list:
+ tx.vout.append(CTxOut(10000000, i))
+ tx.rehash()
+ signresults = self.nodes[0].signrawtransaction(bytes_to_hex_str(tx.serialize_without_witness()))['hex']
+ txid = self.nodes[0].sendrawtransaction(signresults, True)
+ self.nodes[0].generate(1)
+ sync_blocks(self.nodes)
+ watchcount = 0
+ spendcount = 0
+ for i in self.nodes[0].listunspent():
+ if (i['txid'] == txid):
+ watchcount += 1
+ if (i['spendable'] == True):
+ spendcount += 1
+ if (ismine == 2):
+ assert_equal(spendcount, len(script_list))
+ elif (ismine == 1):
+ assert_equal(watchcount, len(script_list))
+ assert_equal(spendcount, 0)
+ else:
+ assert_equal(watchcount, 0)
+ return txid
+
+ def p2sh_address_to_script(self,v):
+ bare = CScript(hex_str_to_bytes(v['hex']))
+ p2sh = CScript(hex_str_to_bytes(v['scriptPubKey']))
+ p2wsh = CScript([OP_0, sha256(bare)])
+ p2sh_p2wsh = CScript([OP_HASH160, hash160(p2wsh), OP_EQUAL])
+ return([bare, p2sh, p2wsh, p2sh_p2wsh])
+
+ def p2pkh_address_to_script(self,v):
+ pubkey = hex_str_to_bytes(v['pubkey'])
+ p2wpkh = CScript([OP_0, hash160(pubkey)])
+ p2sh_p2wpkh = CScript([OP_HASH160, hash160(p2wpkh), OP_EQUAL])
+ p2pk = CScript([pubkey, OP_CHECKSIG])
+ p2pkh = CScript(hex_str_to_bytes(v['scriptPubKey']))
+ p2sh_p2pk = CScript([OP_HASH160, hash160(p2pk), OP_EQUAL])
+ p2sh_p2pkh = CScript([OP_HASH160, hash160(p2pkh), OP_EQUAL])
+ p2wsh_p2pk = CScript([OP_0, sha256(p2pk)])
+ p2wsh_p2pkh = CScript([OP_0, sha256(p2pkh)])
+ p2sh_p2wsh_p2pk = CScript([OP_HASH160, hash160(p2wsh_p2pk), OP_EQUAL])
+ p2sh_p2wsh_p2pkh = CScript([OP_HASH160, hash160(p2wsh_p2pkh), OP_EQUAL])
+ return [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh]
+
+ def create_and_mine_tx_from_txids(self, txids, success = True):
+ tx = CTransaction()
+ for i in txids:
+ txtmp = CTransaction()
+ txraw = self.nodes[0].getrawtransaction(i)
+ f = BytesIO(hex_str_to_bytes(txraw))
+ txtmp.deserialize(f)
+ for j in range(len(txtmp.vout)):
+ tx.vin.append(CTxIn(COutPoint(int('0x'+i,0), j)))
+ tx.vout.append(CTxOut(0, CScript()))
+ tx.rehash()
+ signresults = self.nodes[0].signrawtransaction(bytes_to_hex_str(tx.serialize_without_witness()))['hex']
+ self.nodes[0].sendrawtransaction(signresults, True)
+ self.nodes[0].generate(1)
+ sync_blocks(self.nodes)
+
+
if __name__ == '__main__':
SegWitTest().main()
diff --git a/qa/rpc-tests/sendheaders.py b/qa/rpc-tests/sendheaders.py
index c3f3180b6b..81b2442e6a 100755
--- a/qa/rpc-tests/sendheaders.py
+++ b/qa/rpc-tests/sendheaders.py
@@ -80,20 +80,19 @@ e. Announce one more that doesn't connect.
Expect: disconnect.
'''
-class BaseNode(NodeConnCB):
+direct_fetch_response_time = 0.05
+
+class BaseNode(SingleNodeConnCB):
def __init__(self):
- NodeConnCB.__init__(self)
- self.connection = None
+ SingleNodeConnCB.__init__(self)
self.last_inv = None
self.last_headers = None
self.last_block = None
- self.ping_counter = 1
- self.last_pong = msg_pong(0)
self.last_getdata = None
- self.sleep_time = 0.05
self.block_announced = False
self.last_getheaders = None
self.disconnected = False
+ self.last_blockhash_announced = None
def clear_last_announcement(self):
with mininode_lock:
@@ -101,9 +100,6 @@ class BaseNode(NodeConnCB):
self.last_inv = None
self.last_headers = None
- def add_connection(self, conn):
- self.connection = conn
-
# Request data for a list of block hashes
def get_data(self, block_hashes):
msg = msg_getdata()
@@ -122,17 +118,17 @@ class BaseNode(NodeConnCB):
msg.inv = [CInv(2, blockhash)]
self.connection.send_message(msg)
- # Wrapper for the NodeConn's send_message function
- def send_message(self, message):
- self.connection.send_message(message)
-
def on_inv(self, conn, message):
self.last_inv = message
self.block_announced = True
+ self.last_blockhash_announced = message.inv[-1].hash
def on_headers(self, conn, message):
self.last_headers = message
- self.block_announced = True
+ if len(message.headers):
+ self.block_announced = True
+ message.headers[-1].calc_sha256()
+ self.last_blockhash_announced = message.headers[-1].sha256
def on_block(self, conn, message):
self.last_block = message.block
@@ -141,9 +137,6 @@ class BaseNode(NodeConnCB):
def on_getdata(self, conn, message):
self.last_getdata = message
- def on_pong(self, conn, message):
- self.last_pong = message
-
def on_getheaders(self, conn, message):
self.last_getheaders = message
@@ -157,7 +150,7 @@ class BaseNode(NodeConnCB):
expect_headers = headers if headers != None else []
expect_inv = inv if inv != None else []
test_function = lambda: self.block_announced
- self.sync(test_function)
+ assert(wait_until(test_function, timeout=60))
with mininode_lock:
self.block_announced = False
@@ -180,30 +173,14 @@ class BaseNode(NodeConnCB):
return success
# Syncing helpers
- def sync(self, test_function, timeout=60):
- while timeout > 0:
- with mininode_lock:
- if test_function():
- return
- time.sleep(self.sleep_time)
- 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)
+ assert(wait_until(test_function, timeout=timeout))
return
def wait_for_getheaders(self, timeout=60):
test_function = lambda: self.last_getheaders != None
- self.sync(test_function, timeout)
+ assert(wait_until(test_function, timeout=timeout))
return
def wait_for_getdata(self, hash_list, timeout=60):
@@ -211,12 +188,17 @@ class BaseNode(NodeConnCB):
return
test_function = lambda: self.last_getdata != None and [x.hash for x in self.last_getdata.inv] == hash_list
- self.sync(test_function, timeout)
+ assert(wait_until(test_function, timeout=timeout))
return
def wait_for_disconnect(self, timeout=60):
test_function = lambda: self.disconnected
- self.sync(test_function, timeout)
+ assert(wait_until(test_function, timeout=timeout))
+ return
+
+ def wait_for_block_announcement(self, block_hash, timeout=60):
+ test_function = lambda: self.last_blockhash_announced == block_hash
+ assert(wait_until(test_function, timeout=timeout))
return
def send_header_for_blocks(self, new_blocks):
@@ -266,7 +248,9 @@ class SendHeadersTest(BitcoinTestFramework):
def mine_reorg(self, length):
self.nodes[0].generate(length) # make sure all invalidated blocks are node0's
sync_blocks(self.nodes, wait=0.1)
- [x.clear_last_announcement() for x in self.p2p_connections]
+ for x in self.p2p_connections:
+ x.wait_for_block_announcement(int(self.nodes[0].getbestblockhash(), 16))
+ x.clear_last_announcement()
tip_height = self.nodes[1].getblockcount()
hash_to_invalidate = self.nodes[1].getblockhash(tip_height-(length-1))
@@ -495,7 +479,7 @@ class SendHeadersTest(BitcoinTestFramework):
test_node.send_header_for_blocks(blocks)
test_node.sync_with_ping()
- test_node.wait_for_getdata([x.sha256 for x in blocks], timeout=test_node.sleep_time)
+ test_node.wait_for_getdata([x.sha256 for x in blocks], timeout=direct_fetch_response_time)
[ test_node.send_message(msg_block(x)) for x in blocks ]
@@ -526,13 +510,13 @@ class SendHeadersTest(BitcoinTestFramework):
# both blocks (same work as tip)
test_node.send_header_for_blocks(blocks[1:2])
test_node.sync_with_ping()
- test_node.wait_for_getdata([x.sha256 for x in blocks[0:2]], timeout=test_node.sleep_time)
+ test_node.wait_for_getdata([x.sha256 for x in blocks[0:2]], timeout=direct_fetch_response_time)
# Announcing 16 more headers should trigger direct fetch for 14 more
# blocks
test_node.send_header_for_blocks(blocks[2:18])
test_node.sync_with_ping()
- test_node.wait_for_getdata([x.sha256 for x in blocks[2:16]], timeout=test_node.sleep_time)
+ test_node.wait_for_getdata([x.sha256 for x in blocks[2:16]], timeout=direct_fetch_response_time)
# Announcing 1 more header should not trigger any response
test_node.last_getdata = None
diff --git a/qa/rpc-tests/test_framework/address.py b/qa/rpc-tests/test_framework/address.py
new file mode 100644
index 0000000000..50b999be61
--- /dev/null
+++ b/qa/rpc-tests/test_framework/address.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python3
+# Copyright (c) 2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#
+# address.py
+#
+# This file encodes and decodes BASE58 P2PKH and P2SH addresses
+#
+
+from .script import hash256, hash160, sha256, CScript, OP_0
+from .util import bytes_to_hex_str, hex_str_to_bytes
+
+chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
+
+def byte_to_base58(b, version):
+ result = ''
+ str = bytes_to_hex_str(b)
+ str = bytes_to_hex_str(chr(version).encode('latin-1')) + str
+ checksum = bytes_to_hex_str(hash256(hex_str_to_bytes(str)))
+ str += checksum[:8]
+ value = int('0x'+str,0)
+ while value > 0:
+ result = chars[value % 58] + result
+ value //= 58
+ while (str[:2] == '00'):
+ result = chars[0] + result
+ str = str[2:]
+ return result
+
+# TODO: def base58_decode
+
+def keyhash_to_p2pkh(hash, main = False):
+ assert (len(hash) == 20)
+ version = 0 if main else 111
+ return byte_to_base58(hash, version)
+
+def scripthash_to_p2sh(hash, main = False):
+ assert (len(hash) == 20)
+ version = 5 if main else 196
+ return byte_to_base58(hash, version)
+
+def key_to_p2pkh(key, main = False):
+ key = check_key(key)
+ return keyhash_to_p2pkh(hash160(key), main)
+
+def script_to_p2sh(script, main = False):
+ script = check_script(script)
+ return scripthash_to_p2sh(hash160(script), main)
+
+def key_to_p2sh_p2wpkh(key, main = False):
+ key = check_key(key)
+ p2shscript = CScript([OP_0, hash160(key)])
+ return script_to_p2sh(p2shscript, main)
+
+def script_to_p2sh_p2wsh(script, main = False):
+ script = check_script(script)
+ p2shscript = CScript([OP_0, sha256(script)])
+ return script_to_p2sh(p2shscript, main)
+
+def check_key(key):
+ if (type(key) is str):
+ key = hex_str_to_bytes(key) # Assuming this is hex string
+ if (type(key) is bytes and (len(key) == 33 or len(key) == 65)):
+ return key
+ assert(False)
+
+def check_script(script):
+ if (type(script) is str):
+ script = hex_str_to_bytes(script) # Assuming this is hex string
+ if (type(script) is bytes or type(script) is CScript):
+ return script
+ assert(False)
diff --git a/qa/rpc-tests/test_framework/authproxy.py b/qa/rpc-tests/test_framework/authproxy.py
index d095a56ce7..fd7f32b5c6 100644
--- a/qa/rpc-tests/test_framework/authproxy.py
+++ b/qa/rpc-tests/test_framework/authproxy.py
@@ -55,7 +55,11 @@ log = logging.getLogger("BitcoinRPC")
class JSONRPCException(Exception):
def __init__(self, rpc_error):
- Exception.__init__(self)
+ try:
+ errmsg = '%(message)s (%(code)i)' % rpc_error
+ except (KeyError, TypeError):
+ errmsg = ''
+ Exception.__init__(self, errmsg)
self.error = rpc_error
@@ -126,8 +130,9 @@ class AuthServiceProxy(object):
return self._get_response()
else:
raise
- except BrokenPipeError:
- # Python 3.5+ raises this instead of BadStatusLine when the connection was reset
+ except (BrokenPipeError,ConnectionResetError):
+ # Python 3.5+ raises BrokenPipeError instead of BadStatusLine when the connection was reset
+ # ConnectionResetError happens on FreeBSD with Python 3.4
self.__conn.close()
self.__conn.request(method, path, postdata, headers)
return self._get_response()
diff --git a/qa/rpc-tests/test_framework/blockstore.py b/qa/rpc-tests/test_framework/blockstore.py
index 6120dd574b..28a6b92b81 100644
--- a/qa/rpc-tests/test_framework/blockstore.py
+++ b/qa/rpc-tests/test_framework/blockstore.py
@@ -1,4 +1,7 @@
#!/usr/bin/env python3
+# Copyright (c) 2015-2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
# BlockStore: a helper class that keeps a map of blocks and implements
# helper functions for responding to getheaders and getdata,
# and for constructing a getheaders message
@@ -6,11 +9,11 @@
from .mininode import *
from io import BytesIO
-import dbm.ndbm
+import dbm.dumb as dbmd
class BlockStore(object):
def __init__(self, datadir):
- self.blockDB = dbm.ndbm.open(datadir + "/blocks", 'c')
+ self.blockDB = dbmd.open(datadir + "/blocks", 'c')
self.currentBlock = 0
self.headers_map = dict()
@@ -120,7 +123,7 @@ class BlockStore(object):
class TxStore(object):
def __init__(self, datadir):
- self.txDB = dbm.ndbm.open(datadir + "/transactions", 'c')
+ self.txDB = dbmd.open(datadir + "/transactions", 'c')
def close(self):
self.txDB.close()
diff --git a/qa/rpc-tests/test_framework/coverage.py b/qa/rpc-tests/test_framework/coverage.py
index 23fce61014..13b33869f5 100644
--- a/qa/rpc-tests/test_framework/coverage.py
+++ b/qa/rpc-tests/test_framework/coverage.py
@@ -50,7 +50,7 @@ class AuthServiceProxyWrapper(object):
rpc_method = self.auth_service_proxy_instance._service_name
if self.coverage_logfile:
- with open(self.coverage_logfile, 'a+') as f:
+ with open(self.coverage_logfile, 'a+', encoding='utf8') as f:
f.write("%s\n" % rpc_method)
return return_val
@@ -100,7 +100,7 @@ def write_all_rpc_commands(dirname, node):
if line and not line.startswith('='):
commands.add("%s\n" % line.split()[0])
- with open(filename, 'w') as f:
+ with open(filename, 'w', encoding='utf8') as f:
f.writelines(list(commands))
return True
diff --git a/qa/rpc-tests/test_framework/key.py b/qa/rpc-tests/test_framework/key.py
index ba3038fe04..c63a15c1e0 100644
--- a/qa/rpc-tests/test_framework/key.py
+++ b/qa/rpc-tests/test_framework/key.py
@@ -75,6 +75,9 @@ ssl.EC_POINT_mul.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p,
# this specifies the curve used with ECDSA.
NID_secp256k1 = 714 # from openssl/obj_mac.h
+SECP256K1_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
+SECP256K1_ORDER_HALF = SECP256K1_ORDER // 2
+
# Thx to Sam Devlin for the ctypes magic 64-bit fix.
def _check_result(val, func, args):
if val == 0:
@@ -147,7 +150,7 @@ class CECKey(object):
r = self.get_raw_ecdh_key(other_pubkey)
return kdf(r)
- def sign(self, hash):
+ def sign(self, hash, low_s = True):
# FIXME: need unit tests for below cases
if not isinstance(hash, bytes):
raise TypeError('Hash must be bytes instance; got %r' % hash.__class__)
@@ -159,7 +162,25 @@ class CECKey(object):
mb_sig = ctypes.create_string_buffer(sig_size0.value)
result = ssl.ECDSA_sign(0, hash, len(hash), mb_sig, ctypes.byref(sig_size0), self.k)
assert 1 == result
- return mb_sig.raw[:sig_size0.value]
+ assert mb_sig.raw[0] == 0x30
+ assert mb_sig.raw[1] == sig_size0.value - 2
+ total_size = mb_sig.raw[1]
+ assert mb_sig.raw[2] == 2
+ r_size = mb_sig.raw[3]
+ assert mb_sig.raw[4 + r_size] == 2
+ s_size = mb_sig.raw[5 + r_size]
+ s_value = int.from_bytes(mb_sig.raw[6+r_size:6+r_size+s_size], byteorder='big')
+ if (not low_s) or s_value <= SECP256K1_ORDER_HALF:
+ return mb_sig.raw[:sig_size0.value]
+ else:
+ low_s_value = SECP256K1_ORDER - s_value
+ low_s_bytes = (low_s_value).to_bytes(33, byteorder='big')
+ while len(low_s_bytes) > 1 and low_s_bytes[0] == 0 and low_s_bytes[1] < 0x80:
+ low_s_bytes = low_s_bytes[1:]
+ new_s_size = len(low_s_bytes)
+ new_total_size_byte = (total_size + new_s_size - s_size).to_bytes(1,byteorder='big')
+ new_s_size_byte = (new_s_size).to_bytes(1,byteorder='big')
+ return b'\x30' + new_total_size_byte + mb_sig.raw[2:5+r_size] + new_s_size_byte + low_s_bytes
def verify(self, hash, sig):
"""Verify a DER signature"""
diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py
index cdd5292cd6..4d238c08d9 100755
--- a/qa/rpc-tests/test_framework/mininode.py
+++ b/qa/rpc-tests/test_framework/mininode.py
@@ -36,9 +36,10 @@ from threading import RLock
from threading import Thread
import logging
import copy
+from test_framework.siphash import siphash256
BIP0031_VERSION = 60000
-MY_VERSION = 60001 # past bip-31 for ping/pong
+MY_VERSION = 70014 # past bip-31 for ping/pong
MY_SUBVERSION = b"/python-mininode-tester:0.0.3/"
MAX_INV_SZ = 50000
@@ -52,7 +53,7 @@ NODE_BLOOM = (1 << 2)
NODE_WITNESS = (1 << 3)
# Keep our own socket map for asyncore, so that we can track disconnects
-# ourselves (to workaround an issue with closing an asyncore socket when
+# ourselves (to workaround an issue with closing an asyncore socket when
# using select)
mininode_socket_map = dict()
@@ -74,8 +75,19 @@ def ripemd160(s):
def hash256(s):
return sha256(sha256(s))
+def ser_compact_size(l):
+ r = b""
+ if l < 253:
+ r = struct.pack("B", l)
+ elif l < 0x10000:
+ r = struct.pack("<BH", 253, l)
+ elif l < 0x100000000:
+ r = struct.pack("<BI", 254, l)
+ else:
+ r = struct.pack("<BQ", 255, l)
+ return r
-def deser_string(f):
+def deser_compact_size(f):
nit = struct.unpack("<B", f.read(1))[0]
if nit == 253:
nit = struct.unpack("<H", f.read(2))[0]
@@ -83,16 +95,14 @@ def deser_string(f):
nit = struct.unpack("<I", f.read(4))[0]
elif nit == 255:
nit = struct.unpack("<Q", f.read(8))[0]
+ return nit
+
+def deser_string(f):
+ nit = deser_compact_size(f)
return f.read(nit)
def ser_string(s):
- if len(s) < 253:
- return struct.pack("B", len(s)) + s
- elif len(s) < 0x10000:
- return struct.pack("<BH", 253, len(s)) + s
- elif len(s) < 0x100000000:
- return struct.pack("<BI", 254, len(s)) + s
- return struct.pack("<BQ", 255, len(s)) + s
+ return ser_compact_size(len(s)) + s
def deser_uint256(f):
r = 0
@@ -125,13 +135,7 @@ def uint256_from_compact(c):
def deser_vector(f, c):
- nit = struct.unpack("<B", f.read(1))[0]
- if nit == 253:
- nit = struct.unpack("<H", f.read(2))[0]
- elif nit == 254:
- nit = struct.unpack("<I", f.read(4))[0]
- elif nit == 255:
- nit = struct.unpack("<Q", f.read(8))[0]
+ nit = deser_compact_size(f)
r = []
for i in range(nit):
t = c()
@@ -144,15 +148,7 @@ def deser_vector(f, c):
# entries in the vector (we use this for serializing the vector of transactions
# for a witness block).
def ser_vector(l, ser_function_name=None):
- r = b""
- if len(l) < 253:
- r = struct.pack("B", len(l))
- elif len(l) < 0x10000:
- r = struct.pack("<BH", 253, len(l))
- elif len(l) < 0x100000000:
- r = struct.pack("<BI", 254, len(l))
- else:
- r = struct.pack("<BQ", 255, len(l))
+ r = ser_compact_size(len(l))
for i in l:
if ser_function_name:
r += getattr(i, ser_function_name)()
@@ -162,13 +158,7 @@ def ser_vector(l, ser_function_name=None):
def deser_uint256_vector(f):
- nit = struct.unpack("<B", f.read(1))[0]
- if nit == 253:
- nit = struct.unpack("<H", f.read(2))[0]
- elif nit == 254:
- nit = struct.unpack("<I", f.read(4))[0]
- elif nit == 255:
- nit = struct.unpack("<Q", f.read(8))[0]
+ nit = deser_compact_size(f)
r = []
for i in range(nit):
t = deser_uint256(f)
@@ -177,28 +167,14 @@ def deser_uint256_vector(f):
def ser_uint256_vector(l):
- r = b""
- if len(l) < 253:
- r = struct.pack("B", len(l))
- elif len(l) < 0x10000:
- r = struct.pack("<BH", 253, len(l))
- elif len(l) < 0x100000000:
- r = struct.pack("<BI", 254, len(l))
- else:
- r = struct.pack("<BQ", 255, len(l))
+ r = ser_compact_size(len(l))
for i in l:
r += ser_uint256(i)
return r
def deser_string_vector(f):
- nit = struct.unpack("<B", f.read(1))[0]
- if nit == 253:
- nit = struct.unpack("<H", f.read(2))[0]
- elif nit == 254:
- nit = struct.unpack("<I", f.read(4))[0]
- elif nit == 255:
- nit = struct.unpack("<Q", f.read(8))[0]
+ nit = deser_compact_size(f)
r = []
for i in range(nit):
t = deser_string(f)
@@ -207,28 +183,14 @@ def deser_string_vector(f):
def ser_string_vector(l):
- r = b""
- if len(l) < 253:
- r = struct.pack("B", len(l))
- elif len(l) < 0x10000:
- r = struct.pack("<BH", 253, len(l))
- elif len(l) < 0x100000000:
- r = struct.pack("<BI", 254, len(l))
- else:
- r = struct.pack("<BQ", 255, len(l))
+ r = ser_compact_size(len(l))
for sv in l:
r += ser_string(sv)
return r
def deser_int_vector(f):
- nit = struct.unpack("<B", f.read(1))[0]
- if nit == 253:
- nit = struct.unpack("<H", f.read(2))[0]
- elif nit == 254:
- nit = struct.unpack("<I", f.read(4))[0]
- elif nit == 255:
- nit = struct.unpack("<Q", f.read(8))[0]
+ nit = deser_compact_size(f)
r = []
for i in range(nit):
t = struct.unpack("<i", f.read(4))[0]
@@ -237,15 +199,7 @@ def deser_int_vector(f):
def ser_int_vector(l):
- r = b""
- if len(l) < 253:
- r = struct.pack("B", len(l))
- elif len(l) < 0x10000:
- r = struct.pack("<BH", 253, len(l))
- elif len(l) < 0x100000000:
- r = struct.pack("<BI", 254, len(l))
- else:
- r = struct.pack("<BQ", 255, len(l))
+ r = ser_compact_size(len(l))
for i in l:
r += struct.pack("<i", i)
return r
@@ -294,7 +248,8 @@ class CInv(object):
1: "TX",
2: "Block",
1|MSG_WITNESS_FLAG: "WitnessTx",
- 2|MSG_WITNESS_FLAG : "WitnessBlock"
+ 2|MSG_WITNESS_FLAG : "WitnessBlock",
+ 4: "CompactBlock"
}
def __init__(self, t=0, h=0):
@@ -497,7 +452,7 @@ class CTransaction(object):
else:
self.vout = deser_vector(f, CTxOut)
if flags != 0:
- self.wit.vtxinwit = [CTxInWitness()]*len(self.vin)
+ self.wit.vtxinwit = [CTxInWitness() for i in range(len(self.vin))]
self.wit.deserialize(f)
self.nLockTime = struct.unpack("<I", f.read(4))[0]
self.sha256 = None
@@ -563,8 +518,8 @@ class CTransaction(object):
return True
def __repr__(self):
- return "CTransaction(nVersion=%i vin=%s vout=%s nLockTime=%i)" \
- % (self.nVersion, repr(self.vin), repr(self.vout), self.nLockTime)
+ return "CTransaction(nVersion=%i vin=%s vout=%s wit=%s nLockTime=%i)" \
+ % (self.nVersion, repr(self.vin), repr(self.vout), repr(self.wit), self.nLockTime)
class CBlockHeader(object):
@@ -781,6 +736,208 @@ class CAlert(object):
% (len(self.vchMsg), len(self.vchSig))
+class PrefilledTransaction(object):
+ def __init__(self, index=0, tx = None):
+ self.index = index
+ self.tx = tx
+
+ def deserialize(self, f):
+ self.index = deser_compact_size(f)
+ self.tx = CTransaction()
+ self.tx.deserialize(f)
+
+ def serialize(self, with_witness=False):
+ r = b""
+ r += ser_compact_size(self.index)
+ if with_witness:
+ r += self.tx.serialize_with_witness()
+ else:
+ r += self.tx.serialize_without_witness()
+ return r
+
+ def serialize_with_witness(self):
+ return self.serialize(with_witness=True)
+
+ def __repr__(self):
+ return "PrefilledTransaction(index=%d, tx=%s)" % (self.index, repr(self.tx))
+
+# This is what we send on the wire, in a cmpctblock message.
+class P2PHeaderAndShortIDs(object):
+ def __init__(self):
+ self.header = CBlockHeader()
+ self.nonce = 0
+ self.shortids_length = 0
+ self.shortids = []
+ self.prefilled_txn_length = 0
+ self.prefilled_txn = []
+
+ def deserialize(self, f):
+ self.header.deserialize(f)
+ self.nonce = struct.unpack("<Q", f.read(8))[0]
+ self.shortids_length = deser_compact_size(f)
+ for i in range(self.shortids_length):
+ # shortids are defined to be 6 bytes in the spec, so append
+ # two zero bytes and read it in as an 8-byte number
+ self.shortids.append(struct.unpack("<Q", f.read(6) + b'\x00\x00')[0])
+ self.prefilled_txn = deser_vector(f, PrefilledTransaction)
+ self.prefilled_txn_length = len(self.prefilled_txn)
+
+ # When using version 2 compact blocks, we must serialize with_witness.
+ def serialize(self, with_witness=False):
+ r = b""
+ r += self.header.serialize()
+ r += struct.pack("<Q", self.nonce)
+ r += ser_compact_size(self.shortids_length)
+ for x in self.shortids:
+ # We only want the first 6 bytes
+ r += struct.pack("<Q", x)[0:6]
+ if with_witness:
+ r += ser_vector(self.prefilled_txn, "serialize_with_witness")
+ else:
+ r += ser_vector(self.prefilled_txn)
+ return r
+
+ def __repr__(self):
+ return "P2PHeaderAndShortIDs(header=%s, nonce=%d, shortids_length=%d, shortids=%s, prefilled_txn_length=%d, prefilledtxn=%s" % (repr(self.header), self.nonce, self.shortids_length, repr(self.shortids), self.prefilled_txn_length, repr(self.prefilled_txn))
+
+# P2P version of the above that will use witness serialization (for compact
+# block version 2)
+class P2PHeaderAndShortWitnessIDs(P2PHeaderAndShortIDs):
+ def serialize(self):
+ return super(P2PHeaderAndShortWitnessIDs, self).serialize(with_witness=True)
+
+# Calculate the BIP 152-compact blocks shortid for a given transaction hash
+def calculate_shortid(k0, k1, tx_hash):
+ expected_shortid = siphash256(k0, k1, tx_hash)
+ expected_shortid &= 0x0000ffffffffffff
+ return expected_shortid
+
+# This version gets rid of the array lengths, and reinterprets the differential
+# encoding into indices that can be used for lookup.
+class HeaderAndShortIDs(object):
+ def __init__(self, p2pheaders_and_shortids = None):
+ self.header = CBlockHeader()
+ self.nonce = 0
+ self.shortids = []
+ self.prefilled_txn = []
+ self.use_witness = False
+
+ if p2pheaders_and_shortids != None:
+ self.header = p2pheaders_and_shortids.header
+ self.nonce = p2pheaders_and_shortids.nonce
+ self.shortids = p2pheaders_and_shortids.shortids
+ last_index = -1
+ for x in p2pheaders_and_shortids.prefilled_txn:
+ self.prefilled_txn.append(PrefilledTransaction(x.index + last_index + 1, x.tx))
+ last_index = self.prefilled_txn[-1].index
+
+ def to_p2p(self):
+ if self.use_witness:
+ ret = P2PHeaderAndShortWitnessIDs()
+ else:
+ ret = P2PHeaderAndShortIDs()
+ ret.header = self.header
+ ret.nonce = self.nonce
+ ret.shortids_length = len(self.shortids)
+ ret.shortids = self.shortids
+ ret.prefilled_txn_length = len(self.prefilled_txn)
+ ret.prefilled_txn = []
+ last_index = -1
+ for x in self.prefilled_txn:
+ ret.prefilled_txn.append(PrefilledTransaction(x.index - last_index - 1, x.tx))
+ last_index = x.index
+ return ret
+
+ def get_siphash_keys(self):
+ header_nonce = self.header.serialize()
+ header_nonce += struct.pack("<Q", self.nonce)
+ hash_header_nonce_as_str = sha256(header_nonce)
+ key0 = struct.unpack("<Q", hash_header_nonce_as_str[0:8])[0]
+ key1 = struct.unpack("<Q", hash_header_nonce_as_str[8:16])[0]
+ return [ key0, key1 ]
+
+ # Version 2 compact blocks use wtxid in shortids (rather than txid)
+ def initialize_from_block(self, block, nonce=0, prefill_list = [0], use_witness = False):
+ self.header = CBlockHeader(block)
+ self.nonce = nonce
+ self.prefilled_txn = [ PrefilledTransaction(i, block.vtx[i]) for i in prefill_list ]
+ self.shortids = []
+ self.use_witness = use_witness
+ [k0, k1] = self.get_siphash_keys()
+ for i in range(len(block.vtx)):
+ if i not in prefill_list:
+ tx_hash = block.vtx[i].sha256
+ if use_witness:
+ tx_hash = block.vtx[i].calc_sha256(with_witness=True)
+ self.shortids.append(calculate_shortid(k0, k1, tx_hash))
+
+ def __repr__(self):
+ return "HeaderAndShortIDs(header=%s, nonce=%d, shortids=%s, prefilledtxn=%s" % (repr(self.header), self.nonce, repr(self.shortids), repr(self.prefilled_txn))
+
+
+class BlockTransactionsRequest(object):
+
+ def __init__(self, blockhash=0, indexes = None):
+ self.blockhash = blockhash
+ self.indexes = indexes if indexes != None else []
+
+ def deserialize(self, f):
+ self.blockhash = deser_uint256(f)
+ indexes_length = deser_compact_size(f)
+ for i in range(indexes_length):
+ self.indexes.append(deser_compact_size(f))
+
+ def serialize(self):
+ r = b""
+ r += ser_uint256(self.blockhash)
+ r += ser_compact_size(len(self.indexes))
+ for x in self.indexes:
+ r += ser_compact_size(x)
+ return r
+
+ # helper to set the differentially encoded indexes from absolute ones
+ def from_absolute(self, absolute_indexes):
+ self.indexes = []
+ last_index = -1
+ for x in absolute_indexes:
+ self.indexes.append(x-last_index-1)
+ last_index = x
+
+ def to_absolute(self):
+ absolute_indexes = []
+ last_index = -1
+ for x in self.indexes:
+ absolute_indexes.append(x+last_index+1)
+ last_index = absolute_indexes[-1]
+ return absolute_indexes
+
+ def __repr__(self):
+ return "BlockTransactionsRequest(hash=%064x indexes=%s)" % (self.blockhash, repr(self.indexes))
+
+
+class BlockTransactions(object):
+
+ def __init__(self, blockhash=0, transactions = None):
+ self.blockhash = blockhash
+ self.transactions = transactions if transactions != None else []
+
+ def deserialize(self, f):
+ self.blockhash = deser_uint256(f)
+ self.transactions = deser_vector(f, CTransaction)
+
+ def serialize(self, with_witness=False):
+ r = b""
+ r += ser_uint256(self.blockhash)
+ if with_witness:
+ r += ser_vector(self.transactions, "serialize_with_witness")
+ else:
+ r += ser_vector(self.transactions)
+ return r
+
+ def __repr__(self):
+ return "BlockTransactions(hash=%064x transactions=%s)" % (self.blockhash, repr(self.transactions))
+
+
# Objects that correspond to messages on the wire
class msg_version(object):
command = b"version"
@@ -1184,7 +1341,7 @@ class msg_reject(object):
% (self.message, self.code, self.reason, self.data)
# Helper function
-def wait_until(predicate, attempts=float('inf'), timeout=float('inf')):
+def wait_until(predicate, *, attempts=float('inf'), timeout=float('inf')):
attempt = 0
elapsed = 0
@@ -1215,6 +1372,85 @@ class msg_feefilter(object):
def __repr__(self):
return "msg_feefilter(feerate=%08x)" % self.feerate
+class msg_sendcmpct(object):
+ command = b"sendcmpct"
+
+ def __init__(self):
+ self.announce = False
+ self.version = 1
+
+ def deserialize(self, f):
+ self.announce = struct.unpack("<?", f.read(1))[0]
+ self.version = struct.unpack("<Q", f.read(8))[0]
+
+ def serialize(self):
+ r = b""
+ r += struct.pack("<?", self.announce)
+ r += struct.pack("<Q", self.version)
+ return r
+
+ def __repr__(self):
+ return "msg_sendcmpct(announce=%s, version=%lu)" % (self.announce, self.version)
+
+class msg_cmpctblock(object):
+ command = b"cmpctblock"
+
+ def __init__(self, header_and_shortids = None):
+ self.header_and_shortids = header_and_shortids
+
+ def deserialize(self, f):
+ self.header_and_shortids = P2PHeaderAndShortIDs()
+ self.header_and_shortids.deserialize(f)
+
+ def serialize(self):
+ r = b""
+ r += self.header_and_shortids.serialize()
+ return r
+
+ def __repr__(self):
+ return "msg_cmpctblock(HeaderAndShortIDs=%s)" % repr(self.header_and_shortids)
+
+class msg_getblocktxn(object):
+ command = b"getblocktxn"
+
+ def __init__(self):
+ self.block_txn_request = None
+
+ def deserialize(self, f):
+ self.block_txn_request = BlockTransactionsRequest()
+ self.block_txn_request.deserialize(f)
+
+ def serialize(self):
+ r = b""
+ r += self.block_txn_request.serialize()
+ return r
+
+ def __repr__(self):
+ return "msg_getblocktxn(block_txn_request=%s)" % (repr(self.block_txn_request))
+
+class msg_blocktxn(object):
+ command = b"blocktxn"
+
+ def __init__(self):
+ self.block_transactions = BlockTransactions()
+
+ def deserialize(self, f):
+ self.block_transactions.deserialize(f)
+
+ def serialize(self):
+ r = b""
+ r += self.block_transactions.serialize()
+ return r
+
+ def __repr__(self):
+ return "msg_blocktxn(block_transactions=%s)" % (repr(self.block_transactions))
+
+class msg_witness_blocktxn(msg_blocktxn):
+ def serialize(self):
+ r = b""
+ r += self.block_transactions.serialize(with_witness=True)
+ return r
+
# This is what a callback should look like for NodeConn
# Reimplement the on_* functions to provide handling for events
class NodeConnCB(object):
@@ -1295,6 +1531,10 @@ class NodeConnCB(object):
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):
@@ -1311,6 +1551,10 @@ class SingleNodeConnCB(NodeConnCB):
def send_message(self, message):
self.connection.send_message(message)
+ def send_and_ping(self, message):
+ self.send_message(message)
+ self.sync_with_ping()
+
def on_pong(self, conn, message):
self.last_pong = message
@@ -1319,7 +1563,7 @@ class SingleNodeConnCB(NodeConnCB):
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)
+ success = wait_until(received_pong, timeout=timeout)
self.ping_counter += 1
return success
@@ -1344,7 +1588,11 @@ class NodeConn(asyncore.dispatcher):
b"reject": msg_reject,
b"mempool": msg_mempool,
b"feefilter": msg_feefilter,
- b"sendheaders": msg_sendheaders
+ b"sendheaders": msg_sendheaders,
+ b"sendcmpct": msg_sendcmpct,
+ b"cmpctblock": msg_cmpctblock,
+ b"getblocktxn": msg_getblocktxn,
+ b"blocktxn": msg_blocktxn
}
MAGIC_BYTES = {
"mainnet": b"\xf9\xbe\xb4\xd9", # mainnet
diff --git a/qa/rpc-tests/test_framework/netutil.py b/qa/rpc-tests/test_framework/netutil.py
index 573b06772d..b92a9f6e1c 100644
--- a/qa/rpc-tests/test_framework/netutil.py
+++ b/qa/rpc-tests/test_framework/netutil.py
@@ -58,7 +58,7 @@ def netstat(typ='tcp'):
To get pid of all network process running on system, you must run this script
as superuser
'''
- with open('/proc/net/'+typ,'r') as f:
+ with open('/proc/net/'+typ,'r',encoding='utf8') as f:
content = f.readlines()
content.pop(0)
result = []
diff --git a/qa/rpc-tests/test_framework/script.py b/qa/rpc-tests/test_framework/script.py
index b46c643ccb..83bbf20479 100644
--- a/qa/rpc-tests/test_framework/script.py
+++ b/qa/rpc-tests/test_framework/script.py
@@ -882,7 +882,7 @@ def SignatureHash(script, txTo, inIdx, hashtype):
tmp = txtmp.vout[outIdx]
txtmp.vout = []
for i in range(outIdx):
- txtmp.vout.append(CTxOut())
+ txtmp.vout.append(CTxOut(-1))
txtmp.vout.append(tmp)
for i in range(len(txtmp.vin)):
diff --git a/qa/rpc-tests/test_framework/siphash.py b/qa/rpc-tests/test_framework/siphash.py
new file mode 100644
index 0000000000..9c0574bd93
--- /dev/null
+++ b/qa/rpc-tests/test_framework/siphash.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python3
+# Copyright (c) 2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#
+# siphash.py - Specialized SipHash-2-4 implementations
+#
+# This implements SipHash-2-4 for 256-bit integers.
+
+def rotl64(n, b):
+ return n >> (64 - b) | (n & ((1 << (64 - b)) - 1)) << b
+
+def siphash_round(v0, v1, v2, v3):
+ v0 = (v0 + v1) & ((1 << 64) - 1)
+ v1 = rotl64(v1, 13)
+ v1 ^= v0
+ v0 = rotl64(v0, 32)
+ v2 = (v2 + v3) & ((1 << 64) - 1)
+ v3 = rotl64(v3, 16)
+ v3 ^= v2
+ v0 = (v0 + v3) & ((1 << 64) - 1)
+ v3 = rotl64(v3, 21)
+ v3 ^= v0
+ v2 = (v2 + v1) & ((1 << 64) - 1)
+ v1 = rotl64(v1, 17)
+ v1 ^= v2
+ v2 = rotl64(v2, 32)
+ return (v0, v1, v2, v3)
+
+def siphash256(k0, k1, h):
+ n0 = h & ((1 << 64) - 1)
+ n1 = (h >> 64) & ((1 << 64) - 1)
+ n2 = (h >> 128) & ((1 << 64) - 1)
+ n3 = (h >> 192) & ((1 << 64) - 1)
+ v0 = 0x736f6d6570736575 ^ k0
+ v1 = 0x646f72616e646f6d ^ k1
+ v2 = 0x6c7967656e657261 ^ k0
+ v3 = 0x7465646279746573 ^ k1 ^ n0
+ v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
+ v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
+ v0 ^= n0
+ v3 ^= n1
+ v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
+ v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
+ v0 ^= n1
+ v3 ^= n2
+ v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
+ v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
+ v0 ^= n2
+ v3 ^= n3
+ v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
+ v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
+ v0 ^= n3
+ v3 ^= 0x2000000000000000
+ v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
+ v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
+ v0 ^= 0x2000000000000000
+ v2 ^= 0xFF
+ v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
+ v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
+ v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
+ v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
+ return v0 ^ v1 ^ v2 ^ v3
diff --git a/qa/rpc-tests/test_framework/test_framework.py b/qa/rpc-tests/test_framework/test_framework.py
index 0dfece6b27..e6fc5fd8a2 100755
--- a/qa/rpc-tests/test_framework/test_framework.py
+++ b/qa/rpc-tests/test_framework/test_framework.py
@@ -21,7 +21,6 @@ from .util import (
sync_mempools,
stop_nodes,
stop_node,
- wait_bitcoinds,
enable_coverage,
check_json_precision,
initialize_chain_clean,
@@ -48,7 +47,7 @@ class BitcoinTestFramework(object):
if self.setup_clean_chain:
initialize_chain_clean(self.options.tmpdir, self.num_nodes)
else:
- initialize_chain(self.options.tmpdir, self.num_nodes)
+ 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)
@@ -81,7 +80,6 @@ class BitcoinTestFramework(object):
"""
assert not self.is_network_split
stop_nodes(self.nodes)
- wait_bitcoinds()
self.setup_network(True)
def sync_all(self):
@@ -100,7 +98,6 @@ class BitcoinTestFramework(object):
"""
assert self.is_network_split
stop_nodes(self.nodes)
- wait_bitcoinds()
self.setup_network(False)
def main(self):
@@ -112,6 +109,8 @@ class BitcoinTestFramework(object):
help="Don't stop bitcoinds after the test execution")
parser.add_option("--srcdir", dest="srcdir", default=os.path.normpath(os.path.dirname(os.path.realpath(__file__))+"/../../../src"),
help="Source directory containing bitcoind/bitcoin-cli (default: %default)")
+ parser.add_option("--cachedir", dest="cachedir", default=os.path.normpath(os.path.dirname(os.path.realpath(__file__))+"/../../cache"),
+ help="Directory for caching pregenerated datadirs")
parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"),
help="Root directory for datadirs")
parser.add_option("--tracerpc", dest="trace_rpc", default=False, action="store_true",
@@ -123,7 +122,8 @@ class BitcoinTestFramework(object):
self.add_options(parser)
(self.options, self.args) = parser.parse_args()
- self.options.tmpdir += '/' + str(self.options.port_seed)
+ # backup dir variable for removal at cleanup
+ self.options.root, self.options.tmpdir = self.options.tmpdir, self.options.tmpdir + '/' + str(self.options.port_seed)
if self.options.trace_rpc:
logging.basicConfig(level=logging.DEBUG, stream=sys.stdout)
@@ -167,13 +167,14 @@ class BitcoinTestFramework(object):
if not self.options.noshutdown:
print("Stopping nodes")
stop_nodes(self.nodes)
- wait_bitcoinds()
else:
print("Note: bitcoinds were not stopped and may still be running")
if not self.options.nocleanup and not self.options.noshutdown and success:
print("Cleaning up")
shutil.rmtree(self.options.tmpdir)
+ if not os.listdir(self.options.root):
+ os.rmdir(self.options.root)
else:
print("Not cleaning up dir %s" % self.options.tmpdir)
diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py
index 32fe79efc3..c818af4bd7 100644
--- a/qa/rpc-tests/test_framework/util.py
+++ b/qa/rpc-tests/test_framework/util.py
@@ -125,14 +125,28 @@ def sync_blocks(rpc_connections, wait=1, timeout=60):
"""
Wait until everybody has the same tip
"""
+ maxheight = 0
while timeout > 0:
- tips = [ x.getbestblockhash() for x in rpc_connections ]
+ tips = [ x.waitforblockheight(maxheight, int(wait * 1000)) for x in rpc_connections ]
+ heights = [ x["height"] for x in tips ]
if tips == [ tips[0] ]*len(tips):
return True
- time.sleep(wait)
+ if heights == [ heights[0] ]*len(heights): #heights are the same but hashes are not
+ raise AssertionError("Block sync failed")
timeout -= wait
+ maxheight = max(heights)
raise AssertionError("Block sync failed")
+def sync_chain(rpc_connections, wait=1):
+ """
+ Wait until everybody has the same best block
+ """
+ while True:
+ counts = [ x.getbestblockhash() for x in rpc_connections ]
+ if counts == [ counts[0] ]*len(counts):
+ break
+ time.sleep(wait)
+
def sync_mempools(rpc_connections, wait=1, timeout=60):
"""
Wait until everybody has the same transactions in their memory
@@ -157,7 +171,7 @@ def initialize_datadir(dirname, n):
if not os.path.isdir(datadir):
os.makedirs(datadir)
rpc_u, rpc_p = rpc_auth_pair(n)
- with open(os.path.join(datadir, "bitcoin.conf"), 'w') as f:
+ with open(os.path.join(datadir, "bitcoin.conf"), 'w', encoding='utf8') as f:
f.write("regtest=1\n")
f.write("rpcuser=" + rpc_u + "\n")
f.write("rpcpassword=" + rpc_p + "\n")
@@ -171,7 +185,15 @@ def rpc_auth_pair(n):
def rpc_url(i, rpchost=None):
rpc_u, rpc_p = rpc_auth_pair(i)
- return "http://%s:%s@%s:%d" % (rpc_u, rpc_p, rpchost or '127.0.0.1', rpc_port(i))
+ host = '127.0.0.1'
+ port = rpc_port(i)
+ if rpchost:
+ parts = rpchost.split(':')
+ if len(parts) == 2:
+ host, port = parts
+ else:
+ host = rpchost
+ return "http://%s:%s@%s:%d" % (rpc_u, rpc_p, host, int(port))
def wait_for_bitcoind_start(process, url, i):
'''
@@ -193,7 +215,7 @@ def wait_for_bitcoind_start(process, url, i):
raise # unkown JSON RPC exception
time.sleep(0.25)
-def initialize_chain(test_dir, num_nodes):
+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
@@ -202,7 +224,7 @@ def initialize_chain(test_dir, num_nodes):
assert num_nodes <= MAX_NODES
create_cache = False
for i in range(MAX_NODES):
- if not os.path.isdir(os.path.join('cache', 'node'+str(i))):
+ if not os.path.isdir(os.path.join(cachedir, 'node'+str(i))):
create_cache = True
break
@@ -210,12 +232,12 @@ def initialize_chain(test_dir, num_nodes):
#find and delete old cache directories if any exist
for i in range(MAX_NODES):
- if os.path.isdir(os.path.join("cache","node"+str(i))):
- shutil.rmtree(os.path.join("cache","node"+str(i)))
+ 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("cache", i)
+ 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)))
@@ -254,16 +276,15 @@ def initialize_chain(test_dir, num_nodes):
# Shut them down, and clean up cache directories:
stop_nodes(rpcs)
- wait_bitcoinds()
disable_mocktime()
for i in range(MAX_NODES):
- os.remove(log_filename("cache", i, "debug.log"))
- os.remove(log_filename("cache", i, "db.log"))
- os.remove(log_filename("cache", i, "peers.dat"))
- os.remove(log_filename("cache", i, "fee_estimates.dat"))
+ 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("cache", "node"+str(i))
+ 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
@@ -353,6 +374,7 @@ def stop_nodes(nodes):
except http.client.CannotSendRequest as e:
print("WARN: Unable to stop node: " + repr(e))
del nodes[:] # Emptying array closes connections as a side effect
+ wait_bitcoinds()
def set_node_times(nodes, t):
for node in nodes:
diff --git a/qa/rpc-tests/wallet-accounts.py b/qa/rpc-tests/wallet-accounts.py
new file mode 100755
index 0000000000..c51181e4f8
--- /dev/null
+++ b/qa/rpc-tests/wallet-accounts.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python3
+# Copyright (c) 2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ start_nodes,
+ start_node,
+ assert_equal,
+ connect_nodes_bi,
+)
+
+
+class WalletAccountsTest(BitcoinTestFramework):
+
+ def __init__(self):
+ super().__init__()
+ self.setup_clean_chain = True
+ self.num_nodes = 1
+ self.node_args = [[]]
+
+ def setup_network(self):
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, self.node_args)
+ self.is_network_split = False
+
+ def run_test (self):
+ node = self.nodes[0]
+ # Check that there's no UTXO on any of the nodes
+ assert_equal(len(node.listunspent()), 0)
+
+ node.generate(101)
+
+ assert_equal(node.getbalance(), 50)
+
+ accounts = ["a","b","c","d","e"]
+ amount_to_send = 1.0
+ account_addresses = dict()
+ for account in accounts:
+ address = node.getaccountaddress(account)
+ account_addresses[account] = address
+
+ node.getnewaddress(account)
+ assert_equal(node.getaccount(address), account)
+ assert(address in node.getaddressesbyaccount(account))
+
+ node.sendfrom("", address, amount_to_send)
+
+ node.generate(1)
+
+ for i in range(len(accounts)):
+ from_account = accounts[i]
+ to_account = accounts[(i+1)%len(accounts)]
+ to_address = account_addresses[to_account]
+ node.sendfrom(from_account, to_address, amount_to_send)
+
+ node.generate(1)
+
+ for account in accounts:
+ address = node.getaccountaddress(account)
+ assert(address != account_addresses[account])
+ assert_equal(node.getreceivedbyaccount(account), 2)
+ node.move(account, "", node.getbalance(account))
+
+ node.generate(101)
+
+ expected_account_balances = {"": 5200}
+ for account in accounts:
+ expected_account_balances[account] = 0
+
+ assert_equal(node.listaccounts(), expected_account_balances)
+
+ assert_equal(node.getbalance(""), 5200)
+
+ for account in accounts:
+ address = node.getaccountaddress("")
+ node.setaccount(address, account)
+ assert(address in node.getaddressesbyaccount(account))
+ assert(address not in node.getaddressesbyaccount(""))
+
+ for account in accounts:
+ addresses = []
+ for x in range(10):
+ addresses.append(node.getnewaddress())
+ multisig_address = node.addmultisigaddress(5, addresses, account)
+ node.sendfrom("", multisig_address, 50)
+
+ node.generate(101)
+
+ for account in accounts:
+ assert_equal(node.getbalance(account), 50)
+
+if __name__ == '__main__':
+ WalletAccountsTest().main ()
diff --git a/qa/rpc-tests/wallet-dump.py b/qa/rpc-tests/wallet-dump.py
new file mode 100755
index 0000000000..a37096a40c
--- /dev/null
+++ b/qa/rpc-tests/wallet-dump.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python3
+# Copyright (c) 2016 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (start_nodes, start_node, assert_equal, bitcoind_processes)
+
+
+def read_dump(file_name, addrs, hd_master_addr_old):
+ """
+ Read the given dump, count the addrs that match, count change and reserve.
+ Also check that the old hd_master is inactive
+ """
+ with open(file_name, encoding='utf8') as inputfile:
+ found_addr = 0
+ found_addr_chg = 0
+ found_addr_rsv = 0
+ hd_master_addr_ret = None
+ for line in inputfile:
+ # only read non comment lines
+ if line[0] != "#" and len(line) > 10:
+ # split out some data
+ key_label, comment = line.split("#")
+ # key = key_label.split(" ")[0]
+ keytype = key_label.split(" ")[2]
+ if len(comment) > 1:
+ addr_keypath = comment.split(" addr=")[1]
+ addr = addr_keypath.split(" ")[0]
+ keypath = None
+ if keytype == "inactivehdmaster=1":
+ # ensure the old master is still available
+ assert(hd_master_addr_old == addr)
+ elif keytype == "hdmaster=1":
+ # ensure we have generated a new hd master key
+ assert(hd_master_addr_old != addr)
+ hd_master_addr_ret = addr
+ else:
+ keypath = addr_keypath.rstrip().split("hdkeypath=")[1]
+
+ # count key types
+ for addrObj in addrs:
+ if addrObj['address'] == addr and addrObj['hdkeypath'] == keypath and keytype == "label=":
+ found_addr += 1
+ break
+ elif keytype == "change=1":
+ found_addr_chg += 1
+ break
+ elif keytype == "reserve=1":
+ found_addr_rsv += 1
+ break
+ return found_addr, found_addr_chg, found_addr_rsv, hd_master_addr_ret
+
+
+class WalletDumpTest(BitcoinTestFramework):
+
+ def __init__(self):
+ super().__init__()
+ self.setup_clean_chain = False
+ self.num_nodes = 1
+ self.extra_args = [["-keypool=90"]]
+
+ def setup_network(self, split=False):
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args)
+
+ def run_test (self):
+ tmpdir = self.options.tmpdir
+
+ # generate 20 addresses to compare against the dump
+ test_addr_count = 20
+ addrs = []
+ for i in range(0,test_addr_count):
+ addr = self.nodes[0].getnewaddress()
+ vaddr= self.nodes[0].validateaddress(addr) #required to get hd keypath
+ addrs.append(vaddr)
+ # Should be a no-op:
+ self.nodes[0].keypoolrefill()
+
+ # dump unencrypted wallet
+ self.nodes[0].dumpwallet(tmpdir + "/node0/wallet.unencrypted.dump")
+
+ found_addr, found_addr_chg, found_addr_rsv, hd_master_addr_unenc = \
+ read_dump(tmpdir + "/node0/wallet.unencrypted.dump", addrs, None)
+ 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)
+
+ #encrypt wallet, restart, unlock and dump
+ self.nodes[0].encryptwallet('test')
+ bitcoind_processes[0].wait()
+ self.nodes[0] = start_node(0, self.options.tmpdir, self.extra_args[0])
+ self.nodes[0].walletpassphrase('test', 10)
+ # Should be a no-op:
+ self.nodes[0].keypoolrefill()
+ self.nodes[0].dumpwallet(tmpdir + "/node0/wallet.encrypted.dump")
+
+ 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)
+
+if __name__ == '__main__':
+ WalletDumpTest().main ()
diff --git a/qa/rpc-tests/wallet-hd.py b/qa/rpc-tests/wallet-hd.py
index c738ee2207..a49d91f6f4 100755
--- a/qa/rpc-tests/wallet-hd.py
+++ b/qa/rpc-tests/wallet-hd.py
@@ -31,7 +31,7 @@ class WalletHDTest(BitcoinTestFramework):
tmpdir = self.options.tmpdir
# Make sure we use hd, keep masterkeyid
- masterkeyid = self.nodes[1].getwalletinfo()['masterkeyid']
+ masterkeyid = self.nodes[1].getwalletinfo()['hdmasterkeyid']
assert_equal(len(masterkeyid), 40)
# Import a non-HD private key in the HD wallet
@@ -39,8 +39,8 @@ class WalletHDTest(BitcoinTestFramework):
self.nodes[1].importprivkey(self.nodes[0].dumpprivkey(non_hd_add))
# This should be enough to keep the master key and the non-HD key
- self.nodes[1].backupwallet(tmpdir + "hd.bak")
- #self.nodes[1].dumpwallet(tmpdir + "hd.dump")
+ self.nodes[1].backupwallet(tmpdir + "/hd.bak")
+ #self.nodes[1].dumpwallet(tmpdir + "/hd.dump")
# Derive some HD addresses and remember the last
# Also send funds to each add
@@ -63,7 +63,7 @@ class WalletHDTest(BitcoinTestFramework):
print("Restore backup ...")
self.stop_node(1)
os.remove(self.options.tmpdir + "/node1/regtest/wallet.dat")
- shutil.copyfile(tmpdir + "hd.bak", tmpdir + "/node1/regtest/wallet.dat")
+ shutil.copyfile(tmpdir + "/hd.bak", tmpdir + "/node1/regtest/wallet.dat")
self.nodes[1] = start_node(1, self.options.tmpdir, self.node_args[1])
#connect_nodes_bi(self.nodes, 0, 1)
diff --git a/qa/rpc-tests/wallet.py b/qa/rpc-tests/wallet.py
index 5d96e7a6e5..e43f6ea5d2 100755
--- a/qa/rpc-tests/wallet.py
+++ b/qa/rpc-tests/wallet.py
@@ -18,9 +18,10 @@ class WalletTest (BitcoinTestFramework):
super().__init__()
self.setup_clean_chain = True
self.num_nodes = 4
+ self.extra_args = [['-usehd={:d}'.format(i%2==0)] for i in range(4)]
def setup_network(self, split=False):
- self.nodes = start_nodes(3, self.options.tmpdir)
+ self.nodes = start_nodes(3, self.options.tmpdir, self.extra_args[:3])
connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,1,2)
connect_nodes_bi(self.nodes,0,2)
@@ -154,7 +155,7 @@ class WalletTest (BitcoinTestFramework):
txid2 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1)
sync_mempools(self.nodes)
- self.nodes.append(start_node(3, self.options.tmpdir))
+ self.nodes.append(start_node(3, self.options.tmpdir, self.extra_args[3]))
connect_nodes_bi(self.nodes, 0, 3)
sync_blocks(self.nodes)
@@ -198,7 +199,6 @@ class WalletTest (BitcoinTestFramework):
#do some -walletbroadcast tests
stop_nodes(self.nodes)
- wait_bitcoinds()
self.nodes = start_nodes(3, self.options.tmpdir, [["-walletbroadcast=0"],["-walletbroadcast=0"],["-walletbroadcast=0"]])
connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,1,2)
@@ -224,7 +224,6 @@ class WalletTest (BitcoinTestFramework):
#restart the nodes with -walletbroadcast=1
stop_nodes(self.nodes)
- wait_bitcoinds()
self.nodes = start_nodes(3, self.options.tmpdir)
connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,1,2)
@@ -334,7 +333,6 @@ class WalletTest (BitcoinTestFramework):
for m in maintenance:
print("check " + m)
stop_nodes(self.nodes)
- wait_bitcoinds()
self.nodes = start_nodes(3, self.options.tmpdir, [[m]] * 3)
while m == '-reindex' and [block_count] * 3 != [self.nodes[i].getblockcount() for i in range(3)]:
# reindex will leave rpc warm up "early"; Wait for it to finish
diff --git a/qa/rpc-tests/walletbackup.py b/qa/rpc-tests/walletbackup.py
index b991d5c761..e12cb10a50 100755
--- a/qa/rpc-tests/walletbackup.py
+++ b/qa/rpc-tests/walletbackup.py
@@ -45,12 +45,12 @@ class WalletBackupTest(BitcoinTestFramework):
super().__init__()
self.setup_clean_chain = True
self.num_nodes = 4
+ # nodes 1, 2,3 are spenders, let's give them a keypool=100
+ self.extra_args = [["-keypool=100"], ["-keypool=100"], ["-keypool=100"], []]
# This mirrors how the network was setup in the bash test
def setup_network(self, split=False):
- # nodes 1, 2,3 are spenders, let's give them a keypool=100
- extra_args = [["-keypool=100"], ["-keypool=100"], ["-keypool=100"], []]
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args)
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args)
connect_nodes(self.nodes[0], 3)
connect_nodes(self.nodes[1], 3)
connect_nodes(self.nodes[2], 3)
@@ -79,6 +79,7 @@ class WalletBackupTest(BitcoinTestFramework):
# Must sync mempools before mining.
sync_mempools(self.nodes)
self.nodes[3].generate(1)
+ sync_blocks(self.nodes)
# As above, this mirrors the original bash test.
def start_three(self):