diff options
Diffstat (limited to 'qa/rpc-tests')
33 files changed, 4091 insertions, 361 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/bip68-112-113-p2p.py b/qa/rpc-tests/bip68-112-113-p2p.py index 8ba0704384..55b3e2a04a 100755 --- a/qa/rpc-tests/bip68-112-113-p2p.py +++ b/qa/rpc-tests/bip68-112-113-p2p.py @@ -173,7 +173,7 @@ class BIP68_112_113Test(ComparisonTestFramework): tx = self.create_transaction(self.nodes[0], input, self.nodeaddress, Decimal("49.98")) tx.nVersion = txversion signtx = self.sign_transaction(self.nodes[0], tx) - signtx.vin[0].scriptSig = CScript([-1, OP_NOP3, OP_DROP] + list(CScript(signtx.vin[0].scriptSig))) + signtx.vin[0].scriptSig = CScript([-1, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(signtx.vin[0].scriptSig))) return signtx def create_bip112txs(self, bip112inputs, varyOP_CSV, txversion, locktime_delta = 0): @@ -196,9 +196,9 @@ class BIP68_112_113Test(ComparisonTestFramework): tx.nVersion = txversion signtx = self.sign_transaction(self.nodes[0], tx) if (varyOP_CSV): - signtx.vin[0].scriptSig = CScript([relative_locktimes[b31][b25][b22][b18], OP_NOP3, OP_DROP] + list(CScript(signtx.vin[0].scriptSig))) + signtx.vin[0].scriptSig = CScript([relative_locktimes[b31][b25][b22][b18], OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(signtx.vin[0].scriptSig))) else: - signtx.vin[0].scriptSig = CScript([base_relative_locktime, OP_NOP3, OP_DROP] + list(CScript(signtx.vin[0].scriptSig))) + signtx.vin[0].scriptSig = CScript([base_relative_locktime, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(signtx.vin[0].scriptSig))) b18txs.append(signtx) b22txs.append(b18txs) b25txs.append(b22txs) diff --git a/qa/rpc-tests/bip9-softforks.py b/qa/rpc-tests/bip9-softforks.py index d7e8e5e5a5..979d1410c2 100755 --- a/qa/rpc-tests/bip9-softforks.py +++ b/qa/rpc-tests/bip9-softforks.py @@ -9,7 +9,7 @@ from test_framework.util import * from test_framework.mininode import CTransaction, NetworkThread from test_framework.blocktools import create_coinbase, create_block from test_framework.comptool import TestInstance, TestManager -from test_framework.script import CScript, OP_1NEGATE, OP_NOP3, OP_DROP +from test_framework.script import CScript, OP_1NEGATE, OP_CHECKSEQUENCEVERIFY, OP_DROP from io import BytesIO import time import itertools @@ -220,7 +220,7 @@ class BIP9SoftForksTest(ComparisonTestFramework): '''Modify the signature in vin 0 of the tx to fail CSV Prepends -1 CSV DROP in the scriptSig itself. ''' - tx.vin[0].scriptSig = CScript([OP_1NEGATE, OP_NOP3, OP_DROP] + + tx.vin[0].scriptSig = CScript([OP_1NEGATE, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(tx.vin[0].scriptSig))) def sequence_lock_invalidate(self, tx): 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/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 5087f07620..125d4eb275 100755 --- a/qa/rpc-tests/maxuploadtarget.py +++ b/qa/rpc-tests/maxuploadtarget.py @@ -97,7 +97,7 @@ class MaxUploadTest(BitcoinTestFramework): def setup_network(self): # Start a node with maxuploadtarget of 200 MB (/24h) self.nodes = [] - self.nodes.append(start_node(0, self.options.tmpdir, ["-debug", "-maxuploadtarget=200", "-blockmaxsize=999000"])) + self.nodes.append(start_node(0, self.options.tmpdir, ["-debug", "-maxuploadtarget=800", "-blockmaxsize=999000"])) def mine_full_block(self, node, address): # Want to create a full block @@ -175,13 +175,13 @@ class MaxUploadTest(BitcoinTestFramework): getdata_request = msg_getdata() getdata_request.inv.append(CInv(2, big_old_block)) - max_bytes_per_day = 200*1024*1024 - daily_buffer = 144 * MAX_BLOCK_SIZE + max_bytes_per_day = 800*1024*1024 + daily_buffer = 144 * 4000000 max_bytes_available = max_bytes_per_day - daily_buffer success_count = max_bytes_available // old_block_size - # 144MB will be reserved for relaying new blocks, so expect this to - # succeed for ~70 tries. + # 576MB will be reserved for relaying new blocks, so expect this to + # succeed for ~235 tries. for i in range(success_count): test_nodes[0].send_message(getdata_request) test_nodes[0].sync_with_ping() @@ -198,9 +198,9 @@ class MaxUploadTest(BitcoinTestFramework): # Requesting the current block on test_nodes[1] should succeed indefinitely, # even when over the max upload target. - # We'll try 200 times + # We'll try 800 times getdata_request.inv = [CInv(2, big_new_block)] - for i in range(200): + for i in range(800): test_nodes[1].send_message(getdata_request) test_nodes[1].sync_with_ping() assert_equal(test_nodes[1].block_receive_map[big_new_block], i+1) diff --git a/qa/rpc-tests/nulldummy.py b/qa/rpc-tests/nulldummy.py new file mode 100755 index 0000000000..eaed7a8c78 --- /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 ComparisonTestFramework +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.comptool import TestManager +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(ComparisonTestFramework): + + def __init__(self): + super().__init__() + self.num_nodes = 1 + + 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) + + test = TestManager(self, self.options.tmpdir) + test.add_all_connections(self.nodes) + 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) + 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()
\ No newline at end of file diff --git a/qa/rpc-tests/p2p-compactblocks.py b/qa/rpc-tests/p2p-compactblocks.py new file mode 100755 index 0000000000..bf4fb43add --- /dev/null +++ b/qa/rpc-tests/p2p-compactblocks.py @@ -0,0 +1,624 @@ +#!/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 +from test_framework.siphash import siphash256 +from test_framework.script import CScript, OP_TRUE + +''' +CompactBlocksTest -- test compact blocks (BIP 152) +''' + + +# 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 = None + 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 + + def on_sendcmpct(self, conn, message): + self.last_sendcmpct = 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 + + def on_headers(self, conn, message): + self.last_headers = message + self.block_announced = True + + def on_inv(self, conn, message): + self.last_inv = message + self.block_announced = True + + 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() + + +class CompactBlocksTest(BitcoinTestFramework): + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 1 + self.utxos = [] + + def setup_network(self): + self.nodes = [] + + # Turn off segwit in this test, as compact blocks don't currently work + # with segwit. (After BIP 152 is updated to support segwit, we can + # test behavior with and without segwit enabled by adding a second node + # to the test.) + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, [["-debug", "-logtimemicros=1", "-bip9params=segwit:0:0"]]) + + def build_block_on_tip(self): + height = self.nodes[0].getblockcount() + tip = self.nodes[0].getbestblockhash() + mtp = self.nodes[0].getblockheader(tip)['mediantime'] + block = create_block(int(tip, 16), create_coinbase(height + 1), mtp + 1) + block.solve() + return block + + # Create 10 more anyone-can-spend utxo's for testing. + def make_utxos(self): + block = self.build_block_on_tip() + 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() + 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": + # - No compact block announcements or getdata(MSG_CMPCT_BLOCK) unless + # sendcmpct is sent. + # - If sendcmpct is sent with version > 1, 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. + def test_sendcmpct(self): + print("Testing SENDCMPCT p2p message... ") + + # Make sure we get a version 0 SENDCMPCT message from our peer + def received_sendcmpct(): + return (self.test_node.last_sendcmpct is not None) + got_message = wait_until(received_sendcmpct, timeout=30) + assert(received_sendcmpct()) + assert(got_message) + assert_equal(self.test_node.last_sendcmpct.version, 1) + + tip = int(self.nodes[0].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(self.nodes[0], self.test_node, lambda p: p.last_cmpctblock is None) + + # Try one more time, this time after requesting headers. + self.test_node.request_headers_and_sync(locator=[tip]) + check_announcement_of_new_block(self.nodes[0], self.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. + self.test_node.request_headers_and_sync(locator=[tip]) + + # Now try a SENDCMPCT message with too-high version + sendcmpct = msg_sendcmpct() + sendcmpct.version = 2 + self.test_node.send_and_ping(sendcmpct) + check_announcement_of_new_block(self.nodes[0], self.test_node, lambda p: p.last_cmpctblock is None) + + # Headers sync before next test. + self.test_node.request_headers_and_sync(locator=[tip]) + + # Now try a SENDCMPCT message with valid version, but announce=False + self.test_node.send_and_ping(msg_sendcmpct()) + check_announcement_of_new_block(self.nodes[0], self.test_node, lambda p: p.last_cmpctblock is None) + + # Headers sync before next test. + self.test_node.request_headers_and_sync(locator=[tip]) + + # Finally, try a SENDCMPCT message with announce=True + sendcmpct.version = 1 + sendcmpct.announce = True + self.test_node.send_and_ping(sendcmpct) + check_announcement_of_new_block(self.nodes[0], self.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(self.nodes[0], self.test_node, lambda p: p.last_cmpctblock is not None) + + # Try one more time, after turning on sendheaders + self.test_node.send_and_ping(msg_sendheaders()) + check_announcement_of_new_block(self.nodes[0], self.test_node, lambda p: p.last_cmpctblock is not None) + + # Now turn off announcements + sendcmpct.announce = False + self.test_node.send_and_ping(sendcmpct) + check_announcement_of_new_block(self.nodes[0], self.test_node, lambda p: p.last_cmpctblock is None and p.last_headers is not None) + + # This test actually causes bitcoind to (reasonably!) disconnect us, so do this last. + def test_invalid_cmpctblock_message(self): + print("Testing invalid index in cmpctblock message...") + self.nodes[0].generate(101) + block = self.build_block_on_tip() + + 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): + print("Testing compactblock headers and shortIDs are correct...") + + # Generate a bunch of transactions. + self.nodes[0].generate(101) + num_transactions = 25 + address = self.nodes[0].getnewaddress() + for i in range(num_transactions): + self.nodes[0].sendtoaddress(address, 0.1) + + # Now mine a block, and look at the resulting compact block. + self.test_node.clear_block_announcement() + block_hash = int(self.nodes[0].generate(1)[0], 16) + + # Store the raw block in our internal format. + block = FromHex(CBlock(), self.nodes[0].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(self.test_node.received_block_announcement, timeout=30) + + with mininode_lock: + if self.test_node.last_cmpctblock is None: + self.test_node.clear_block_announcement() + inv = CInv(4, block_hash) # 4 == "CompactBlock" + self.test_node.send_message(msg_getdata([inv])) + + wait_until(self.test_node.received_block_announcement, timeout=30) + + # Now we should have the compactblock + header_and_shortids = None + with mininode_lock: + assert(self.test_node.last_cmpctblock is not None) + # Convert the on-the-wire representation to absolute indexes + header_and_shortids = HeaderAndShortIDs(self.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() + assert_equal(entry.tx.sha256, block.vtx[entry.index].sha256) + + # 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: + shortid = calculate_shortid(k0, k1, block.vtx[index].sha256) + 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. + def test_compactblock_requests(self): + print("Testing compactblock requests... ") + + # Try announcing a block with an inv or header, expect a compactblock + # request + for announce in ["inv", "header"]: + block = self.build_block_on_tip() + with mininode_lock: + self.test_node.last_getdata = None + + if announce == "inv": + self.test_node.send_message(msg_inv([CInv(2, block.sha256)])) + else: + self.test_node.send_header_for_blocks([block]) + success = wait_until(lambda: self.test_node.last_getdata is not None, timeout=30) + assert(success) + assert_equal(len(self.test_node.last_getdata.inv), 1) + assert_equal(self.test_node.last_getdata.inv[0].type, 4) + assert_equal(self.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 + comp_block.shortids = [1] # this is useless, and wrong + self.test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p())) + assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.hashPrevBlock) + # Expect a getblocktxn message. + with mininode_lock: + assert(self.test_node.last_getblocktxn is not None) + absolute_indexes = self.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. + msg = msg_blocktxn() + msg.block_transactions.blockhash = block.sha256 + msg.block_transactions.transactions = [block.vtx[0]] + self.test_node.send_and_ping(msg) + assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256) + + # Create a chain of transactions from given utxo, and add to a new block. + def build_block_with_transactions(self, utxo, num_transactions): + block = self.build_block_on_tip() + + 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): + print("Testing getblocktxn requests...") + + # 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(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) + + self.test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p())) + with mininode_lock: + assert(self.test_node.last_getblocktxn is not None) + absolute_indexes = self.test_node.last_getblocktxn.block_txn_request.to_absolute() + assert_equal(absolute_indexes, [1, 2, 3, 4, 5]) + msg = msg_blocktxn() + msg.block_transactions = BlockTransactions(block.sha256, block.vtx[1:]) + self.test_node.send_and_ping(msg) + assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256) + + utxo = self.utxos.pop(0) + block = self.build_block_with_transactions(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]) + self.test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p())) + with mininode_lock: + assert(self.test_node.last_getblocktxn is not None) + absolute_indexes = self.test_node.last_getblocktxn.block_txn_request.to_absolute() + assert_equal(absolute_indexes, [2, 3, 4]) + msg.block_transactions = BlockTransactions(block.sha256, block.vtx[2:5]) + self.test_node.send_and_ping(msg) + assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256) + + # Now try giving one transaction ahead of time. + utxo = self.utxos.pop(0) + block = self.build_block_with_transactions(utxo, 5) + self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue]) + self.test_node.send_and_ping(msg_tx(block.vtx[1])) + assert(block.vtx[1].hash in self.nodes[0].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]) + self.test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p())) + with mininode_lock: + assert(self.test_node.last_getblocktxn is not None) + absolute_indexes = self.test_node.last_getblocktxn.block_txn_request.to_absolute() + assert_equal(absolute_indexes, [5]) + + msg.block_transactions = BlockTransactions(block.sha256, [block.vtx[5]]) + self.test_node.send_and_ping(msg) + assert_equal(int(self.nodes[0].getbestblockhash(), 16), 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(utxo, 10) + self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue]) + for tx in block.vtx[1:]: + self.test_node.send_message(msg_tx(tx)) + self.test_node.sync_with_ping() + # Make sure all transactions were accepted. + mempool = self.nodes[0].getrawmempool() + for tx in block.vtx[1:]: + assert(tx.hash in mempool) + + # Clear out last request. + with mininode_lock: + self.test_node.last_getblocktxn = None + + # Send compact block + comp_block.initialize_from_block(block, prefill_list=[0]) + self.test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p())) + with mininode_lock: + # Shouldn't have gotten a request for any transaction + assert(self.test_node.last_getblocktxn is None) + # Tip should have updated + assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256) + + # Incorrectly responding to a getblocktxn shouldn't cause the block to be + # permanently failed. + def test_incorrect_blocktxn_response(self): + print("Testing handling of incorrect blocktxn responses...") + + if (len(self.utxos) == 0): + self.make_utxos() + utxo = self.utxos.pop(0) + + block = self.build_block_with_transactions(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]: + self.test_node.send_message(msg_tx(tx)) + self.test_node.sync_with_ping() + # Make sure all transactions were accepted. + mempool = self.nodes[0].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]) + self.test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p())) + absolute_indexes = [] + with mininode_lock: + assert(self.test_node.last_getblocktxn is not None) + absolute_indexes = self.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() + msg.block_transactions = BlockTransactions(block.sha256, [block.vtx[5]] + block.vtx[7:]) + self.test_node.send_and_ping(msg) + + # Tip should not have updated + assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.hashPrevBlock) + + # We should receive a getdata request + success = wait_until(lambda: self.test_node.last_getdata is not None, timeout=10) + assert(success) + assert_equal(len(self.test_node.last_getdata.inv), 1) + assert_equal(self.test_node.last_getdata.inv[0].type, 2) + assert_equal(self.test_node.last_getdata.inv[0].hash, block.sha256) + + # Deliver the block + self.test_node.send_and_ping(msg_block(block)) + assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256) + + def test_getblocktxn_handler(self): + print("Testing getblocktxn handler...") + + # bitcoind won't respond for blocks whose height is more than 15 blocks + # deep. + MAX_GETBLOCKTXN_DEPTH = 15 + chain_height = self.nodes[0].getblockcount() + current_height = chain_height + while (current_height >= chain_height - MAX_GETBLOCKTXN_DEPTH): + block_hash = self.nodes[0].getblockhash(current_height) + block = FromHex(CBlock(), self.nodes[0].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))) + self.test_node.send_message(msg) + success = wait_until(lambda: self.test_node.last_blocktxn is not None, timeout=10) + assert(success) + + [tx.calc_sha256() for tx in block.vtx] + with mininode_lock: + assert_equal(self.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 = self.test_node.last_blocktxn.block_transactions.transactions.pop(0) + tx.calc_sha256() + assert_equal(tx.sha256, block.vtx[index].sha256) + self.test_node.last_blocktxn = None + current_height -= 1 + + # Next request should be ignored, as we're past the allowed depth. + block_hash = self.nodes[0].getblockhash(current_height) + msg.block_txn_request = BlockTransactionsRequest(int(block_hash, 16), [0]) + self.test_node.send_and_ping(msg) + with mininode_lock: + assert_equal(self.test_node.last_blocktxn, None) + + def test_compactblocks_not_at_tip(self): + print("Testing compactblock requests/announcements not at chain tip...") + + # Test that requesting old compactblocks doesn't work. + MAX_CMPCTBLOCK_DEPTH = 11 + new_blocks = [] + for i in range(MAX_CMPCTBLOCK_DEPTH): + self.test_node.clear_block_announcement() + new_blocks.append(self.nodes[0].generate(1)[0]) + wait_until(self.test_node.received_block_announcement, timeout=30) + + self.test_node.clear_block_announcement() + self.test_node.send_message(msg_getdata([CInv(4, int(new_blocks[0], 16))])) + success = wait_until(lambda: self.test_node.last_cmpctblock is not None, timeout=30) + assert(success) + + self.test_node.clear_block_announcement() + self.nodes[0].generate(1) + wait_until(self.test_node.received_block_announcement, timeout=30) + self.test_node.clear_block_announcement() + self.test_node.send_message(msg_getdata([CInv(4, int(new_blocks[0], 16))])) + success = wait_until(lambda: self.test_node.last_block is not None, timeout=30) + assert(success) + with mininode_lock: + self.test_node.last_block.block.calc_sha256() + assert_equal(self.test_node.last_block.block.sha256, int(new_blocks[0], 16)) + + # Generate an old compactblock, and verify that it's not accepted. + cur_height = self.nodes[0].getblockcount() + hashPrevBlock = int(self.nodes[0].getblockhash(cur_height-5), 16) + block = self.build_block_on_tip() + block.hashPrevBlock = hashPrevBlock + block.solve() + + comp_block = HeaderAndShortIDs() + comp_block.initialize_from_block(block) + self.test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p())) + + tips = self.nodes[0].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: + self.test_node.last_blocktxn = None + self.test_node.send_and_ping(msg) + with mininode_lock: + assert(self.test_node.last_blocktxn is None) + + def run_test(self): + # Setup the p2p connections and start up the network thread. + self.test_node = TestNode() + + connections = [] + connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.test_node)) + self.test_node.add_connection(connections[0]) + + 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() + + self.test_sendcmpct() + self.test_compactblock_construction() + self.test_compactblock_requests() + self.test_getblocktxn_requests() + self.test_getblocktxn_handler() + self.test_compactblocks_not_at_tip() + self.test_incorrect_blocktxn_response() + 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..5c5d778f42 100755 --- a/qa/rpc-tests/p2p-mempool.py +++ b/qa/rpc-tests/p2p-mempool.py @@ -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 new file mode 100755 index 0000000000..22ec0ad8c8 --- /dev/null +++ b/qa/rpc-tests/p2p-segwit.py @@ -0,0 +1,1795 @@ +#!/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.script import * +from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment, WITNESS_COMMITMENT_HEADER +from test_framework.key import CECKey, CPubKey +import time +import random +from binascii import hexlify + +# The versionbit bit used to signal activation of SegWit +VB_WITNESS_BIT = 1 +VB_PERIOD = 144 +VB_ACTIVATION_THRESHOLD = 108 +VB_TOP_BITS = 0x20000000 + +MAX_SIGOP_COST = 80000 + +''' +SegWit p2p test. +''' + +# Calculate the virtual size of a witness block: +# (base + witness/4) +def get_virtual_size(witness_block): + base_size = len(witness_block.serialize()) + total_size = len(witness_block.serialize(with_witness=True)) + # the "+3" is so we round up + vsize = int((3*base_size + total_size + 3)/4) + return vsize + +# Note: we can reduce code by using SingleNodeConnCB (in master, not 0.12) +class TestNode(NodeConnCB): + def __init__(self): + NodeConnCB.__init__(self) + self.connection = None + self.ping_counter = 1 + self.last_pong = msg_pong(0) + self.sleep_time = 0.05 + self.getdataset = set() + self.last_reject = None + + def add_connection(self, conn): + self.connection = conn + + # 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 + + def on_block(self, conn, message): + self.last_block = message.block + self.last_block.calc_sha256() + + def on_getdata(self, conn, message): + for inv in message.inv: + self.getdataset.add(inv.hash) + self.last_getdata = message + + def on_pong(self, conn, message): + self.last_pong = message + + def on_reject(self, conn, message): + self.last_reject = message + #print (message) + + # 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) + return + + def wait_for_getdata(self, timeout=60): + test_function = lambda: self.last_getdata != None + self.sync(test_function, timeout) + + def wait_for_inv(self, expected_inv, timeout=60): + test_function = lambda: self.last_inv != expected_inv + self.sync(test_function, timeout) + + def announce_tx_and_wait_for_getdata(self, tx, timeout=60): + with mininode_lock: + self.last_getdata = None + self.send_message(msg_inv(inv=[CInv(1, tx.sha256)])) + self.wait_for_getdata(timeout) + return + + def announce_block_and_wait_for_getdata(self, block, use_header, timeout=60): + with mininode_lock: + self.last_getdata = None + if use_header: + msg = msg_headers() + msg.headers = [ CBlockHeader(block) ] + self.send_message(msg) + else: + self.send_message(msg_inv(inv=[CInv(2, block.sha256)])) + self.wait_for_getdata() + return + + def announce_block(self, block, use_header): + with mininode_lock: + self.last_getdata = None + if use_header: + msg = msg_headers() + msg.headers = [ CBlockHeader(block) ] + self.send_message(msg) + else: + self.send_message(msg_inv(inv=[CInv(2, block.sha256)])) + + def request_block(self, blockhash, inv_type, timeout=60): + with mininode_lock: + self.last_block = None + self.send_message(msg_getdata(inv=[CInv(inv_type, blockhash)])) + self.wait_for_block(blockhash, timeout) + return self.last_block + + def test_transaction_acceptance(self, tx, with_witness, accepted, reason=None): + tx_message = msg_tx(tx) + if with_witness: + tx_message = msg_witness_tx(tx) + self.send_message(tx_message) + self.sync_with_ping() + assert_equal(tx.hash in self.connection.rpc.getrawmempool(), accepted) + if (reason != None and not accepted): + # Check the rejection reason as well. + with mininode_lock: + assert_equal(self.last_reject.reason, reason) + + # Test whether a witness block had the correct effect on the tip + def test_witness_block(self, block, accepted, with_witness=True): + if with_witness: + self.send_message(msg_witness_block(block)) + else: + self.send_message(msg_block(block)) + self.sync_with_ping() + assert_equal(self.connection.rpc.getbestblockhash() == block.hash, accepted) + + +# Used to keep track of anyone-can-spend outputs that we can use in the tests +class UTXO(object): + def __init__(self, sha256, n, nValue): + self.sha256 = sha256 + self.n = n + self.nValue = nValue + + +class SegWitTest(BitcoinTestFramework): + + 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", + default=None, + help="pre-segwit bitcoind binary for upgrade testing") + + def setup_network(self): + self.nodes = [] + self.nodes.append(start_node(0, self.options.tmpdir, ["-debug", "-logtimemicros=1", "-whitelist=127.0.0.1"])) + # Start a node for testing IsStandard rules. + self.nodes.append(start_node(1, self.options.tmpdir, ["-debug", "-logtimemicros=1", "-whitelist=127.0.0.1", "-acceptnonstdtxn=0"])) + connect_nodes(self.nodes[0], 1) + + # If an old bitcoind is given, do the upgrade-after-activation test. + self.test_upgrade = False + if (self.options.oldbinary != None): + self.nodes.append(start_node(2, self.options.tmpdir, ["-debug", "-whitelist=127.0.0.1"], binary=self.options.oldbinary)) + connect_nodes(self.nodes[0], 2) + self.test_upgrade = True + + ''' Helpers ''' + # Build a block on top of node0's tip. + def build_next_block(self, nVersion=4): + tip = self.nodes[0].getbestblockhash() + height = self.nodes[0].getblockcount() + 1 + block_time = self.nodes[0].getblockheader(tip)["mediantime"] + 1 + block = create_block(int(tip, 16), create_coinbase(height), block_time) + block.nVersion = nVersion + block.rehash() + return block + + # Adds list of transactions to block, adds witness commitment, then solves. + def update_witness_block_with_transactions(self, block, tx_list, nonce=0): + block.vtx.extend(tx_list) + add_witness_commitment(block, nonce) + block.solve() + return + + ''' Individual tests ''' + def test_witness_services(self): + print("\tVerifying NODE_WITNESS service bit") + assert((self.test_node.connection.nServices & NODE_WITNESS) != 0) + + + # See if sending a regular transaction works, and create a utxo + # to use in later tests. + def test_non_witness_transaction(self): + # Mine a block with an anyone-can-spend coinbase, + # let it mature, then try to spend it. + print("\tTesting non-witness transaction") + block = self.build_next_block(nVersion=1) + block.solve() + self.test_node.send_message(msg_block(block)) + self.test_node.sync_with_ping() # make sure the block was processed + txid = block.vtx[0].sha256 + + self.nodes[0].generate(99) # let the block mature + + # Create a transaction that spends the coinbase + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(txid, 0), b"")) + tx.vout.append(CTxOut(49*100000000, CScript([OP_TRUE]))) + tx.calc_sha256() + + # Check that serializing it with or without witness is the same + # This is a sanity check of our testing framework. + assert_equal(msg_tx(tx).serialize(), msg_witness_tx(tx).serialize()) + + self.test_node.send_message(msg_witness_tx(tx)) + self.test_node.sync_with_ping() # make sure the tx was processed + assert(tx.hash in self.nodes[0].getrawmempool()) + # Save this transaction for later + self.utxo.append(UTXO(tx.sha256, 0, 49*100000000)) + self.nodes[0].generate(1) + + + # Verify that blocks with witnesses are rejected before activation. + def test_unnecessary_witness_before_segwit_activation(self): + print("\tTesting behavior of unnecessary witnesses") + # For now, rely on earlier tests to have created at least one utxo for + # us to use + assert(len(self.utxo) > 0) + assert(get_bip9_status(self.nodes[0], 'segwit')['status'] != 'active') + + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) + tx.vout.append(CTxOut(self.utxo[0].nValue-1000, CScript([OP_TRUE]))) + tx.wit.vtxinwit.append(CTxInWitness()) + tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([CScriptNum(1)])] + + # Verify the hash with witness differs from the txid + # (otherwise our testing framework must be broken!) + tx.rehash() + assert(tx.sha256 != tx.calc_sha256(with_witness=True)) + + # Construct a segwit-signaling block that includes the transaction. + block = self.build_next_block(nVersion=(VB_TOP_BITS|(1 << VB_WITNESS_BIT))) + self.update_witness_block_with_transactions(block, [tx]) + # Sending witness data before activation is not allowed (anti-spam + # rule). + self.test_node.test_witness_block(block, accepted=False) + # TODO: fix synchronization so we can test reject reason + # Right now, bitcoind delays sending reject messages for blocks + # until the future, making synchronization here difficult. + #assert_equal(self.test_node.last_reject.reason, "unexpected-witness") + + # But it should not be permanently marked bad... + # Resend without witness information. + self.test_node.send_message(msg_block(block)) + self.test_node.sync_with_ping() + assert_equal(self.nodes[0].getbestblockhash(), block.hash) + + sync_blocks(self.nodes) + + # Create a p2sh output -- this is so we can pass the standardness + # rules (an anyone-can-spend OP_TRUE would be rejected, if not wrapped + # in P2SH). + p2sh_program = CScript([OP_TRUE]) + p2sh_pubkey = hash160(p2sh_program) + scriptPubKey = CScript([OP_HASH160, p2sh_pubkey, OP_EQUAL]) + + # Now check that unnecessary witnesses can't be used to blind a node + # to a transaction, eg by violating standardness checks. + tx2 = CTransaction() + tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b"")) + tx2.vout.append(CTxOut(tx.vout[0].nValue-1000, scriptPubKey)) + tx2.rehash() + self.test_node.test_transaction_acceptance(tx2, False, True) + self.nodes[0].generate(1) + sync_blocks(self.nodes) + + # We'll add an unnecessary witness to this transaction that would cause + # 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. + self.std_node.test_transaction_acceptance(tx3, False, True) + + # Now create a new anyone-can-spend utxo for the next test. + tx4 = CTransaction() + tx4.vin.append(CTxIn(COutPoint(tx3.sha256, 0), CScript([p2sh_program]))) + tx4.vout.append(CTxOut(tx3.vout[0].nValue-1000, CScript([OP_TRUE]))) + tx4.rehash() + self.test_node.test_transaction_acceptance(tx3, False, True) + self.test_node.test_transaction_acceptance(tx4, False, True) + + self.nodes[0].generate(1) + sync_blocks(self.nodes) + + # Update our utxo list; we spent the first entry. + self.utxo.pop(0) + self.utxo.append(UTXO(tx4.sha256, 0, tx4.vout[0].nValue)) + + + # Mine enough blocks for segwit's vb state to be 'started'. + def advance_to_segwit_started(self): + height = self.nodes[0].getblockcount() + # Will need to rewrite the tests here if we are past the first period + assert(height < VB_PERIOD - 1) + # Genesis block is 'defined'. + assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'defined') + # Advance to end of period, status should now be 'started' + self.nodes[0].generate(VB_PERIOD-height-1) + assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'started') + + # Mine enough blocks to lock in segwit, but don't activate. + # TODO: we could verify that lockin only happens at the right threshold of + # signalling blocks, rather than just at the right period boundary. + def advance_to_segwit_lockin(self): + height = self.nodes[0].getblockcount() + assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'started') + # Advance to end of period, and verify lock-in happens at the end + self.nodes[0].generate(VB_PERIOD-1) + height = self.nodes[0].getblockcount() + assert((height % VB_PERIOD) == VB_PERIOD - 2) + assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'started') + self.nodes[0].generate(1) + assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'locked_in') + + + # Mine enough blocks to activate segwit. + # TODO: we could verify that activation only happens at the right threshold + # of signalling blocks, rather than just at the right period boundary. + def advance_to_segwit_active(self): + assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'locked_in') + height = self.nodes[0].getblockcount() + self.nodes[0].generate(VB_PERIOD - (height%VB_PERIOD) - 2) + assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'locked_in') + self.nodes[0].generate(1) + assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'active') + + + # This test can only be run after segwit has activated + def test_witness_commitments(self): + print("\tTesting witness commitments") + + # First try a correct witness commitment. + block = self.build_next_block() + add_witness_commitment(block) + block.solve() + + # Test the test -- witness serialization should be different + assert(msg_witness_block(block).serialize() != msg_block(block).serialize()) + + # This empty block should be valid. + self.test_node.test_witness_block(block, accepted=True) + + # Try to tweak the nonce + block_2 = self.build_next_block() + add_witness_commitment(block_2, nonce=28) + block_2.solve() + + # The commitment should have changed! + assert(block_2.vtx[0].vout[-1] != block.vtx[0].vout[-1]) + + # This should also be valid. + self.test_node.test_witness_block(block_2, accepted=True) + + # Now test commitments with actual transactions + assert (len(self.utxo) > 0) + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) + + # Let's construct a witness program + witness_program = CScript([OP_TRUE]) + witness_hash = sha256(witness_program) + scriptPubKey = CScript([OP_0, witness_hash]) + tx.vout.append(CTxOut(self.utxo[0].nValue-1000, scriptPubKey)) + tx.rehash() + + # tx2 will spend tx1, and send back to a regular anyone-can-spend address + tx2 = CTransaction() + tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b"")) + tx2.vout.append(CTxOut(tx.vout[0].nValue-1000, witness_program)) + tx2.wit.vtxinwit.append(CTxInWitness()) + tx2.wit.vtxinwit[0].scriptWitness.stack = [witness_program] + tx2.rehash() + + block_3 = self.build_next_block() + self.update_witness_block_with_transactions(block_3, [tx, tx2], nonce=1) + # Add an extra OP_RETURN output that matches the witness commitment template, + # even though it has extra data after the incorrect commitment. + # This block should fail. + block_3.vtx[0].vout.append(CTxOut(0, CScript([OP_RETURN, WITNESS_COMMITMENT_HEADER + ser_uint256(2), 10]))) + block_3.vtx[0].rehash() + block_3.hashMerkleRoot = block_3.calc_merkle_root() + block_3.rehash() + block_3.solve() + + self.test_node.test_witness_block(block_3, accepted=False) + + # Add a different commitment with different nonce, but in the + # right location, and with some funds burned(!). + # This should succeed (nValue shouldn't affect finding the + # witness commitment). + add_witness_commitment(block_3, nonce=0) + block_3.vtx[0].vout[0].nValue -= 1 + block_3.vtx[0].vout[-1].nValue += 1 + block_3.vtx[0].rehash() + block_3.hashMerkleRoot = block_3.calc_merkle_root() + block_3.rehash() + assert(len(block_3.vtx[0].vout) == 4) # 3 OP_returns + block_3.solve() + self.test_node.test_witness_block(block_3, accepted=True) + + # Finally test that a block with no witness transactions can + # omit the commitment. + block_4 = self.build_next_block() + tx3 = CTransaction() + tx3.vin.append(CTxIn(COutPoint(tx2.sha256, 0), b"")) + tx3.vout.append(CTxOut(tx.vout[0].nValue-1000, witness_program)) + tx3.rehash() + block_4.vtx.append(tx3) + block_4.hashMerkleRoot = block_4.calc_merkle_root() + block_4.solve() + self.test_node.test_witness_block(block_4, with_witness=False, accepted=True) + + # Update available utxo's for use in later test. + self.utxo.pop(0) + self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue)) + + + def test_block_malleability(self): + print("\tTesting witness block malleability") + + # Make sure that a block that has too big a virtual size + # because of a too-large coinbase witness is not permanently + # marked bad. + block = self.build_next_block() + add_witness_commitment(block) + block.solve() + + block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.append(b'a'*5000000) + assert(get_virtual_size(block) > MAX_BLOCK_SIZE) + + # We can't send over the p2p network, because this is too big to relay + # TODO: repeat this test with a block that can be relayed + self.nodes[0].submitblock(bytes_to_hex_str(block.serialize(True))) + + assert(self.nodes[0].getbestblockhash() != block.hash) + + block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.pop() + assert(get_virtual_size(block) < MAX_BLOCK_SIZE) + self.nodes[0].submitblock(bytes_to_hex_str(block.serialize(True))) + + assert(self.nodes[0].getbestblockhash() == block.hash) + + # Now make sure that malleating the witness nonce doesn't + # result in a block permanently marked bad. + block = self.build_next_block() + add_witness_commitment(block) + block.solve() + + # Change the nonce -- should not cause the block to be permanently + # failed + block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ ser_uint256(1) ] + self.test_node.test_witness_block(block, accepted=False) + + # Changing the witness nonce doesn't change the block hash + block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ ser_uint256(0) ] + self.test_node.test_witness_block(block, accepted=True) + + + def test_witness_block_size(self): + print("\tTesting witness block size limit") + # TODO: Test that non-witness carrying blocks can't exceed 1MB + # Skipping this test for now; this is covered in p2p-fullblocktest.py + + # Test that witness-bearing blocks are limited at ceil(base + wit/4) <= 1MB. + block = self.build_next_block() + + assert(len(self.utxo) > 0) + + # Create a P2WSH transaction. + # The witness program will be a bunch of OP_2DROP's, followed by OP_TRUE. + # This should give us plenty of room to tweak the spending tx's + # virtual size. + NUM_DROPS = 200 # 201 max ops per script! + NUM_OUTPUTS = 50 + + witness_program = CScript([OP_2DROP]*NUM_DROPS + [OP_TRUE]) + witness_hash = uint256_from_str(sha256(witness_program)) + scriptPubKey = CScript([OP_0, ser_uint256(witness_hash)]) + + prevout = COutPoint(self.utxo[0].sha256, self.utxo[0].n) + value = self.utxo[0].nValue + + parent_tx = CTransaction() + parent_tx.vin.append(CTxIn(prevout, b"")) + child_value = int(value/NUM_OUTPUTS) + for i in range(NUM_OUTPUTS): + parent_tx.vout.append(CTxOut(child_value, scriptPubKey)) + parent_tx.vout[0].nValue -= 50000 + assert(parent_tx.vout[0].nValue > 0) + parent_tx.rehash() + + child_tx = CTransaction() + for i in range(NUM_OUTPUTS): + child_tx.vin.append(CTxIn(COutPoint(parent_tx.sha256, i), b"")) + child_tx.vout = [CTxOut(value - 100000, CScript([OP_TRUE]))] + for i in range(NUM_OUTPUTS): + child_tx.wit.vtxinwit.append(CTxInWitness()) + child_tx.wit.vtxinwit[-1].scriptWitness.stack = [b'a'*195]*(2*NUM_DROPS) + [witness_program] + child_tx.rehash() + self.update_witness_block_with_transactions(block, [parent_tx, child_tx]) + + vsize = get_virtual_size(block) + additional_bytes = (MAX_BLOCK_SIZE - vsize)*4 + i = 0 + while additional_bytes > 0: + # Add some more bytes to each input until we hit MAX_BLOCK_SIZE+1 + extra_bytes = min(additional_bytes+1, 55) + block.vtx[-1].wit.vtxinwit[int(i/(2*NUM_DROPS))].scriptWitness.stack[i%(2*NUM_DROPS)] = b'a'*(195+extra_bytes) + additional_bytes -= extra_bytes + i += 1 + + block.vtx[0].vout.pop() # Remove old commitment + add_witness_commitment(block) + block.solve() + vsize = get_virtual_size(block) + assert_equal(vsize, MAX_BLOCK_SIZE + 1) + # Make sure that our test case would exceed the old max-network-message + # limit + assert(len(block.serialize(True)) > 2*1024*1024) + + self.test_node.test_witness_block(block, accepted=False) + + # Now resize the second transaction to make the block fit. + cur_length = len(block.vtx[-1].wit.vtxinwit[0].scriptWitness.stack[0]) + block.vtx[-1].wit.vtxinwit[0].scriptWitness.stack[0] = b'a'*(cur_length-1) + block.vtx[0].vout.pop() + add_witness_commitment(block) + block.solve() + assert(get_virtual_size(block) == MAX_BLOCK_SIZE) + + self.test_node.test_witness_block(block, accepted=True) + + # Update available utxo's + self.utxo.pop(0) + self.utxo.append(UTXO(block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue)) + + + # submitblock will try to add the nonce automatically, so that mining + # software doesn't need to worry about doing so itself. + def test_submit_block(self): + block = self.build_next_block() + + # Try using a custom nonce and then don't supply it. + # This shouldn't possibly work. + add_witness_commitment(block, nonce=1) + block.vtx[0].wit = CTxWitness() # drop the nonce + block.solve() + self.nodes[0].submitblock(bytes_to_hex_str(block.serialize(True))) + assert(self.nodes[0].getbestblockhash() != block.hash) + + # Now redo commitment with the standard nonce, but let bitcoind fill it in. + add_witness_commitment(block, nonce=0) + block.vtx[0].wit = CTxWitness() + block.solve() + self.nodes[0].submitblock(bytes_to_hex_str(block.serialize(True))) + assert_equal(self.nodes[0].getbestblockhash(), block.hash) + + # This time, add a tx with non-empty witness, but don't supply + # the commitment. + block_2 = self.build_next_block() + + add_witness_commitment(block_2) + + block_2.solve() + + # Drop commitment and nonce -- submitblock should not fill in. + block_2.vtx[0].vout.pop() + block_2.vtx[0].wit = CTxWitness() + + self.nodes[0].submitblock(bytes_to_hex_str(block_2.serialize(True))) + # Tip should not advance! + assert(self.nodes[0].getbestblockhash() != block_2.hash) + + + # Consensus tests of extra witness data in a transaction. + def test_extra_witness_data(self): + print("\tTesting extra witness data in tx") + + assert(len(self.utxo) > 0) + + block = self.build_next_block() + + witness_program = CScript([OP_DROP, OP_TRUE]) + witness_hash = sha256(witness_program) + scriptPubKey = CScript([OP_0, witness_hash]) + + # First try extra witness data on a tx that doesn't require a witness + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) + tx.vout.append(CTxOut(self.utxo[0].nValue-2000, scriptPubKey)) + tx.vout.append(CTxOut(1000, CScript([OP_TRUE]))) # non-witness output + tx.wit.vtxinwit.append(CTxInWitness()) + tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([])] + tx.rehash() + self.update_witness_block_with_transactions(block, [tx]) + + # Extra witness data should not be allowed. + self.test_node.test_witness_block(block, accepted=False) + + # Try extra signature data. Ok if we're not spending a witness output. + block.vtx[1].wit.vtxinwit = [] + block.vtx[1].vin[0].scriptSig = CScript([OP_0]) + block.vtx[1].rehash() + add_witness_commitment(block) + block.solve() + + self.test_node.test_witness_block(block, accepted=True) + + # Now try extra witness/signature data on an input that DOES require a + # witness + tx2 = CTransaction() + tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b"")) # witness output + tx2.vin.append(CTxIn(COutPoint(tx.sha256, 1), b"")) # non-witness + tx2.vout.append(CTxOut(tx.vout[0].nValue, CScript([OP_TRUE]))) + tx2.wit.vtxinwit.extend([CTxInWitness(), CTxInWitness()]) + tx2.wit.vtxinwit[0].scriptWitness.stack = [ CScript([CScriptNum(1)]), CScript([CScriptNum(1)]), witness_program ] + tx2.wit.vtxinwit[1].scriptWitness.stack = [ CScript([OP_TRUE]) ] + + block = self.build_next_block() + self.update_witness_block_with_transactions(block, [tx2]) + + # This has extra witness data, so it should fail. + self.test_node.test_witness_block(block, accepted=False) + + # Now get rid of the extra witness, but add extra scriptSig data + tx2.vin[0].scriptSig = CScript([OP_TRUE]) + tx2.vin[1].scriptSig = CScript([OP_TRUE]) + tx2.wit.vtxinwit[0].scriptWitness.stack.pop(0) + tx2.wit.vtxinwit[1].scriptWitness.stack = [] + tx2.rehash() + add_witness_commitment(block) + block.solve() + + # This has extra signature data for a witness input, so it should fail. + self.test_node.test_witness_block(block, accepted=False) + + # Now get rid of the extra scriptsig on the witness input, and verify + # success (even with extra scriptsig data in the non-witness input) + tx2.vin[0].scriptSig = b"" + tx2.rehash() + add_witness_commitment(block) + block.solve() + + self.test_node.test_witness_block(block, accepted=True) + + # Update utxo for later tests + self.utxo.pop(0) + self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue)) + + + def test_max_witness_push_length(self): + ''' Should only allow up to 520 byte pushes in witness stack ''' + print("\tTesting maximum witness push size") + MAX_SCRIPT_ELEMENT_SIZE = 520 + assert(len(self.utxo)) + + block = self.build_next_block() + + witness_program = CScript([OP_DROP, OP_TRUE]) + witness_hash = sha256(witness_program) + scriptPubKey = CScript([OP_0, witness_hash]) + + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) + tx.vout.append(CTxOut(self.utxo[0].nValue-1000, scriptPubKey)) + tx.rehash() + + tx2 = CTransaction() + tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b"")) + tx2.vout.append(CTxOut(tx.vout[0].nValue-1000, CScript([OP_TRUE]))) + tx2.wit.vtxinwit.append(CTxInWitness()) + # First try a 521-byte stack element + tx2.wit.vtxinwit[0].scriptWitness.stack = [ b'a'*(MAX_SCRIPT_ELEMENT_SIZE+1), witness_program ] + tx2.rehash() + + self.update_witness_block_with_transactions(block, [tx, tx2]) + self.test_node.test_witness_block(block, accepted=False) + + # Now reduce the length of the stack element + tx2.wit.vtxinwit[0].scriptWitness.stack[0] = b'a'*(MAX_SCRIPT_ELEMENT_SIZE) + + add_witness_commitment(block) + block.solve() + self.test_node.test_witness_block(block, accepted=True) + + # Update the utxo for later tests + self.utxo.pop() + self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue)) + + def test_max_witness_program_length(self): + # Can create witness outputs that are long, but can't be greater than + # 10k bytes to successfully spend + print("\tTesting maximum witness program length") + assert(len(self.utxo)) + MAX_PROGRAM_LENGTH = 10000 + + # This program is 19 max pushes (9937 bytes), then 64 more opcode-bytes. + long_witness_program = CScript([b'a'*520]*19 + [OP_DROP]*63 + [OP_TRUE]) + assert(len(long_witness_program) == MAX_PROGRAM_LENGTH+1) + long_witness_hash = sha256(long_witness_program) + long_scriptPubKey = CScript([OP_0, long_witness_hash]) + + block = self.build_next_block() + + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) + tx.vout.append(CTxOut(self.utxo[0].nValue-1000, long_scriptPubKey)) + tx.rehash() + + tx2 = CTransaction() + tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b"")) + tx2.vout.append(CTxOut(tx.vout[0].nValue-1000, CScript([OP_TRUE]))) + tx2.wit.vtxinwit.append(CTxInWitness()) + tx2.wit.vtxinwit[0].scriptWitness.stack = [b'a']*44 + [long_witness_program] + tx2.rehash() + + self.update_witness_block_with_transactions(block, [tx, tx2]) + + self.test_node.test_witness_block(block, accepted=False) + + # Try again with one less byte in the witness program + witness_program = CScript([b'a'*520]*19 + [OP_DROP]*62 + [OP_TRUE]) + assert(len(witness_program) == MAX_PROGRAM_LENGTH) + witness_hash = sha256(witness_program) + scriptPubKey = CScript([OP_0, witness_hash]) + + tx.vout[0] = CTxOut(tx.vout[0].nValue, scriptPubKey) + tx.rehash() + tx2.vin[0].prevout.hash = tx.sha256 + tx2.wit.vtxinwit[0].scriptWitness.stack = [b'a']*43 + [witness_program] + tx2.rehash() + block.vtx = [block.vtx[0]] + self.update_witness_block_with_transactions(block, [tx, tx2]) + self.test_node.test_witness_block(block, accepted=True) + + self.utxo.pop() + self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue)) + + + def test_witness_input_length(self): + ''' Ensure that vin length must match vtxinwit length ''' + print("\tTesting witness input length") + assert(len(self.utxo)) + + witness_program = CScript([OP_DROP, OP_TRUE]) + witness_hash = sha256(witness_program) + scriptPubKey = CScript([OP_0, witness_hash]) + + # Create a transaction that splits our utxo into many outputs + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) + nValue = self.utxo[0].nValue + for i in range(10): + tx.vout.append(CTxOut(int(nValue/10), scriptPubKey)) + tx.vout[0].nValue -= 1000 + assert(tx.vout[0].nValue >= 0) + + block = self.build_next_block() + self.update_witness_block_with_transactions(block, [tx]) + self.test_node.test_witness_block(block, accepted=True) + + # Try various ways to spend tx that should all break. + # This "broken" transaction serializer will not normalize + # the length of vtxinwit. + class BrokenCTransaction(CTransaction): + def serialize_with_witness(self): + flags = 0 + if not self.wit.is_null(): + flags |= 1 + r = b"" + r += struct.pack("<i", self.nVersion) + if flags: + dummy = [] + r += ser_vector(dummy) + r += struct.pack("<B", flags) + r += ser_vector(self.vin) + r += ser_vector(self.vout) + if flags & 1: + r += self.wit.serialize() + r += struct.pack("<I", self.nLockTime) + return r + + tx2 = BrokenCTransaction() + for i in range(10): + tx2.vin.append(CTxIn(COutPoint(tx.sha256, i), b"")) + tx2.vout.append(CTxOut(nValue-3000, CScript([OP_TRUE]))) + + # First try using a too long vtxinwit + for i in range(11): + tx2.wit.vtxinwit.append(CTxInWitness()) + tx2.wit.vtxinwit[i].scriptWitness.stack = [b'a', witness_program] + + block = self.build_next_block() + self.update_witness_block_with_transactions(block, [tx2]) + self.test_node.test_witness_block(block, accepted=False) + + # Now try using a too short vtxinwit + tx2.wit.vtxinwit.pop() + tx2.wit.vtxinwit.pop() + + block.vtx = [block.vtx[0]] + self.update_witness_block_with_transactions(block, [tx2]) + self.test_node.test_witness_block(block, accepted=False) + + # Now make one of the intermediate witnesses be incorrect + tx2.wit.vtxinwit.append(CTxInWitness()) + tx2.wit.vtxinwit[-1].scriptWitness.stack = [b'a', witness_program] + tx2.wit.vtxinwit[5].scriptWitness.stack = [ witness_program ] + + block.vtx = [block.vtx[0]] + self.update_witness_block_with_transactions(block, [tx2]) + self.test_node.test_witness_block(block, accepted=False) + + # Fix the broken witness and the block should be accepted. + tx2.wit.vtxinwit[5].scriptWitness.stack = [b'a', witness_program] + block.vtx = [block.vtx[0]] + self.update_witness_block_with_transactions(block, [tx2]) + self.test_node.test_witness_block(block, accepted=True) + + self.utxo.pop() + self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue)) + + + def test_witness_tx_relay_before_segwit_activation(self): + print("\tTesting relay of witness transactions") + # Generate a transaction that doesn't require a witness, but send it + # with a witness. Should be rejected for premature-witness, but should + # not be added to recently rejected list. + assert(len(self.utxo)) + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) + tx.vout.append(CTxOut(self.utxo[0].nValue-1000, CScript([OP_TRUE]))) + tx.wit.vtxinwit.append(CTxInWitness()) + tx.wit.vtxinwit[0].scriptWitness.stack = [ b'a' ] + tx.rehash() + + tx_hash = tx.sha256 + tx_value = tx.vout[0].nValue + + # Verify that if a peer doesn't set nServices to include NODE_WITNESS, + # the getdata is just for the non-witness portion. + self.old_node.announce_tx_and_wait_for_getdata(tx) + assert(self.old_node.last_getdata.inv[0].type == 1) + + # Since we haven't delivered the tx yet, inv'ing the same tx from + # a witness transaction ought not result in a getdata. + try: + self.test_node.announce_tx_and_wait_for_getdata(tx, timeout=2) + print("Error: duplicate tx getdata!") + assert(False) + except AssertionError as e: + pass + + # Delivering this transaction with witness should fail (no matter who + # its from) + assert_equal(len(self.nodes[0].getrawmempool()), 0) + assert_equal(len(self.nodes[1].getrawmempool()), 0) + self.old_node.test_transaction_acceptance(tx, with_witness=True, accepted=False) + self.test_node.test_transaction_acceptance(tx, with_witness=True, accepted=False) + + # 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) + + self.utxo.pop(0) + self.utxo.append(UTXO(tx_hash, 0, tx_value)) + + + # After segwit activates, verify that mempool: + # - rejects transactions with unnecessary/extra witnesses + # - accepts transactions with valid witnesses + # and that witness transactions are relayed to non-upgraded peers. + def test_tx_relay_after_segwit_activation(self): + print("\tTesting relay of witness transactions") + # Generate a transaction that doesn't require a witness, but send it + # with a witness. Should be rejected because we can't use a witness + # when spending a non-witness output. + assert(len(self.utxo)) + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) + tx.vout.append(CTxOut(self.utxo[0].nValue-1000, CScript([OP_TRUE]))) + tx.wit.vtxinwit.append(CTxInWitness()) + tx.wit.vtxinwit[0].scriptWitness.stack = [ b'a' ] + tx.rehash() + + tx_hash = tx.sha256 + tx_value = tx.vout[0].nValue + + # Verify that unnecessary witnesses are rejected. + self.test_node.announce_tx_and_wait_for_getdata(tx) + assert_equal(len(self.nodes[0].getrawmempool()), 0) + self.test_node.test_transaction_acceptance(tx, with_witness=True, accepted=False) + + # Verify that removing the witness succeeds. + 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. + witness_program = CScript([OP_TRUE]) + witness_hash = sha256(witness_program) + scriptPubKey = CScript([OP_0, witness_hash]) + tx2 = CTransaction() + tx2.vin.append(CTxIn(COutPoint(tx_hash, 0), b"")) + tx2.vout.append(CTxOut(tx.vout[0].nValue-1000, scriptPubKey)) + tx2.rehash() + + tx3 = CTransaction() + tx3.vin.append(CTxIn(COutPoint(tx2.sha256, 0), b"")) + 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() + + self.test_node.test_transaction_acceptance(tx2, with_witness=True, accepted=True) + self.test_node.test_transaction_acceptance(tx3, with_witness=True, accepted=False) + + # Get rid of the extra witness, and verify acceptance. + tx3.wit.vtxinwit[0].scriptWitness.stack = [ witness_program ] + # Also check that old_node gets a tx announcement, even though this is + # a witness transaction. + self.old_node.wait_for_inv(CInv(1, tx2.sha256)) # wait until tx2 was inv'ed + self.test_node.test_transaction_acceptance(tx3, with_witness=True, accepted=True) + self.old_node.wait_for_inv(CInv(1, tx3.sha256)) + + # Test that getrawtransaction returns correct witness information + # hash, size, vsize + raw_tx = self.nodes[0].getrawtransaction(tx3.hash, 1) + assert_equal(int(raw_tx["hash"], 16), tx3.calc_sha256(True)) + assert_equal(raw_tx["size"], len(tx3.serialize_with_witness())) + vsize = (len(tx3.serialize_with_witness()) + 3*len(tx3.serialize_without_witness()) + 3) / 4 + assert_equal(raw_tx["vsize"], vsize) + assert_equal(len(raw_tx["vin"][0]["txinwitness"]), 1) + assert_equal(raw_tx["vin"][0]["txinwitness"][0], hexlify(witness_program).decode('ascii')) + assert(vsize != raw_tx["size"]) + + # Cleanup: mine the transactions and update utxo for next test + self.nodes[0].generate(1) + assert_equal(len(self.nodes[0].getrawmempool()), 0) + + self.utxo.pop(0) + self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue)) + + + # Test that block requests to NODE_WITNESS peer are with MSG_WITNESS_FLAG + # This is true regardless of segwit activation. + # Also test that we don't ask for blocks from unupgraded peers + def test_block_relay(self, segwit_activated): + print("\tTesting block relay") + + blocktype = 2|MSG_WITNESS_FLAG if segwit_activated else 2 + + # test_node has set NODE_WITNESS, so all getdata requests should be for + # witness blocks. + # Test announcing a block via inv results in a getdata, and that + # announcing a version 4 or random VB block with a header results in a getdata + block1 = self.build_next_block() + block1.solve() + + self.test_node.announce_block_and_wait_for_getdata(block1, use_header=False) + assert(self.test_node.last_getdata.inv[0].type == blocktype) + self.test_node.test_witness_block(block1, True) + + block2 = self.build_next_block(nVersion=4) + block2.solve() + + self.test_node.announce_block_and_wait_for_getdata(block2, use_header=True) + assert(self.test_node.last_getdata.inv[0].type == blocktype) + self.test_node.test_witness_block(block2, True) + + block3 = self.build_next_block(nVersion=(VB_TOP_BITS | (1<<15))) + block3.solve() + self.test_node.announce_block_and_wait_for_getdata(block3, use_header=True) + assert(self.test_node.last_getdata.inv[0].type == blocktype) + self.test_node.test_witness_block(block3, True) + + # Check that we can getdata for witness blocks or regular blocks, + # and the right thing happens. + if segwit_activated == False: + # Before activation, we should be able to request old blocks with + # or without witness, and they should be the same. + chain_height = self.nodes[0].getblockcount() + # Pick 10 random blocks on main chain, and verify that getdata's + # for MSG_BLOCK, MSG_WITNESS_BLOCK, and rpc getblock() are equal. + all_heights = list(range(chain_height+1)) + random.shuffle(all_heights) + all_heights = all_heights[0:10] + for height in all_heights: + block_hash = self.nodes[0].getblockhash(height) + rpc_block = self.nodes[0].getblock(block_hash, False) + block_hash = int(block_hash, 16) + block = self.test_node.request_block(block_hash, 2) + wit_block = self.test_node.request_block(block_hash, 2|MSG_WITNESS_FLAG) + assert_equal(block.serialize(True), wit_block.serialize(True)) + assert_equal(block.serialize(), hex_str_to_bytes(rpc_block)) + else: + # After activation, witness blocks and non-witness blocks should + # be different. Verify rpc getblock() returns witness blocks, while + # getdata respects the requested type. + block = self.build_next_block() + self.update_witness_block_with_transactions(block, []) + # This gives us a witness commitment. + assert(len(block.vtx[0].wit.vtxinwit) == 1) + assert(len(block.vtx[0].wit.vtxinwit[0].scriptWitness.stack) == 1) + self.test_node.test_witness_block(block, accepted=True) + # Now try to retrieve it... + rpc_block = self.nodes[0].getblock(block.hash, False) + non_wit_block = self.test_node.request_block(block.sha256, 2) + wit_block = self.test_node.request_block(block.sha256, 2|MSG_WITNESS_FLAG) + assert_equal(wit_block.serialize(True), hex_str_to_bytes(rpc_block)) + assert_equal(wit_block.serialize(False), non_wit_block.serialize()) + assert_equal(wit_block.serialize(True), block.serialize(True)) + + # Test size, vsize, weight + rpc_details = self.nodes[0].getblock(block.hash, True) + assert_equal(rpc_details["size"], len(block.serialize(True))) + assert_equal(rpc_details["strippedsize"], len(block.serialize(False))) + weight = 3*len(block.serialize(False)) + len(block.serialize(True)) + assert_equal(rpc_details["weight"], weight) + + # Upgraded node should not ask for blocks from unupgraded + block4 = self.build_next_block(nVersion=4) + block4.solve() + self.old_node.getdataset = set() + # Blocks can be requested via direct-fetch (immediately upon processing the announcement) + # or via parallel download (with an indeterminate delay from processing the announcement) + # so to test that a block is NOT requested, we could guess a time period to sleep for, + # and then check. We can avoid the sleep() by taking advantage of transaction getdata's + # being processed after block getdata's, and announce a transaction as well, + # and then check to see if that particular getdata has been received. + self.old_node.announce_block(block4, use_header=False) + 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): + print("\tTesting standardness/consensus for segwit versions (0-16)") + assert(len(self.utxo)) + NUM_TESTS = 17 # will test OP_0, OP1, ..., OP_16 + if (len(self.utxo) < NUM_TESTS): + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) + split_value = (self.utxo[0].nValue - 4000) // NUM_TESTS + for i in range(NUM_TESTS): + tx.vout.append(CTxOut(split_value, CScript([OP_TRUE]))) + tx.rehash() + block = self.build_next_block() + self.update_witness_block_with_transactions(block, [tx]) + self.test_node.test_witness_block(block, accepted=True) + self.utxo.pop(0) + for i in range(NUM_TESTS): + self.utxo.append(UTXO(tx.sha256, i, split_value)) + + sync_blocks(self.nodes) + temp_utxo = [] + tx = CTransaction() + count = 0 + witness_program = CScript([OP_TRUE]) + witness_hash = sha256(witness_program) + assert_equal(len(self.nodes[1].getrawmempool()), 0) + for version in list(range(OP_1, OP_16+1)) + [OP_0]: + count += 1 + # First try to spend to a future version segwit scriptPubKey. + scriptPubKey = CScript([CScriptOp(version), witness_hash]) + tx.vin = [CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")] + tx.vout = [CTxOut(self.utxo[0].nValue-1000, scriptPubKey)] + tx.rehash() + self.std_node.test_transaction_acceptance(tx, with_witness=True, accepted=False) + self.test_node.test_transaction_acceptance(tx, with_witness=True, accepted=True) + self.utxo.pop(0) + temp_utxo.append(UTXO(tx.sha256, 0, tx.vout[0].nValue)) + + self.nodes[0].generate(1) # Mine all the transactions + sync_blocks(self.nodes) + assert(len(self.nodes[0].getrawmempool()) == 0) + + # Finally, verify that version 0 -> version 1 transactions + # are non-standard + scriptPubKey = CScript([CScriptOp(OP_1), witness_hash]) + tx2 = CTransaction() + tx2.vin = [CTxIn(COutPoint(tx.sha256, 0), b"")] + tx2.vout = [CTxOut(tx.vout[0].nValue-1000, scriptPubKey)] + tx2.wit.vtxinwit.append(CTxInWitness()) + tx2.wit.vtxinwit[0].scriptWitness.stack = [ witness_program ] + tx2.rehash() + # Gets accepted to test_node, because standardness of outputs isn't + # checked with fRequireStandard + self.test_node.test_transaction_acceptance(tx2, with_witness=True, accepted=True) + self.std_node.test_transaction_acceptance(tx2, with_witness=True, accepted=False) + temp_utxo.pop() # last entry in temp_utxo was the output we just spent + temp_utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue)) + + # Spend everything in temp_utxo back to an OP_TRUE output. + tx3 = CTransaction() + total_value = 0 + for i in temp_utxo: + tx3.vin.append(CTxIn(COutPoint(i.sha256, i.n), b"")) + tx3.wit.vtxinwit.append(CTxInWitness()) + total_value += i.nValue + tx3.wit.vtxinwit[-1].scriptWitness.stack = [witness_program] + tx3.vout.append(CTxOut(total_value - 1000, CScript([OP_TRUE]))) + tx3.rehash() + # Spending a higher version witness output is not allowed by policy, + # even with fRequireStandard=false. + self.test_node.test_transaction_acceptance(tx3, with_witness=True, accepted=False) + self.test_node.sync_with_ping() + with mininode_lock: + assert(b"reserved for soft-fork upgrades" in self.test_node.last_reject.reason) + + # Building a block with the transaction must be valid, however. + block = self.build_next_block() + self.update_witness_block_with_transactions(block, [tx2, tx3]) + self.test_node.test_witness_block(block, accepted=True) + sync_blocks(self.nodes) + + # Add utxo to our list + self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue)) + + + def test_premature_coinbase_witness_spend(self): + print("\tTesting premature coinbase witness spend") + block = self.build_next_block() + # Change the output of the block to be a witness output. + witness_program = CScript([OP_TRUE]) + witness_hash = sha256(witness_program) + scriptPubKey = CScript([OP_0, witness_hash]) + block.vtx[0].vout[0].scriptPubKey = scriptPubKey + # This next line will rehash the coinbase and update the merkle + # root, and solve. + self.update_witness_block_with_transactions(block, []) + self.test_node.test_witness_block(block, accepted=True) + + spend_tx = CTransaction() + spend_tx.vin = [CTxIn(COutPoint(block.vtx[0].sha256, 0), b"")] + spend_tx.vout = [CTxOut(block.vtx[0].vout[0].nValue, witness_program)] + spend_tx.wit.vtxinwit.append(CTxInWitness()) + spend_tx.wit.vtxinwit[0].scriptWitness.stack = [ witness_program ] + spend_tx.rehash() + + # Now test a premature spend. + self.nodes[0].generate(98) + sync_blocks(self.nodes) + block2 = self.build_next_block() + self.update_witness_block_with_transactions(block2, [spend_tx]) + self.test_node.test_witness_block(block2, accepted=False) + + # Advancing one more block should allow the spend. + self.nodes[0].generate(1) + block2 = self.build_next_block() + self.update_witness_block_with_transactions(block2, [spend_tx]) + self.test_node.test_witness_block(block2, accepted=True) + sync_blocks(self.nodes) + + + def test_signature_version_1(self): + print("\tTesting segwit signature hash version 1") + key = CECKey() + key.set_secretbytes(b"9") + pubkey = CPubKey(key.get_pubkey()) + + witness_program = CScript([pubkey, CScriptOp(OP_CHECKSIG)]) + witness_hash = sha256(witness_program) + scriptPubKey = CScript([OP_0, witness_hash]) + + # First create a witness output for use in the tests. + assert(len(self.utxo)) + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) + tx.vout.append(CTxOut(self.utxo[0].nValue-1000, scriptPubKey)) + tx.rehash() + + self.test_node.test_transaction_acceptance(tx, with_witness=True, accepted=True) + # Mine this transaction in preparation for following tests. + block = self.build_next_block() + self.update_witness_block_with_transactions(block, [tx]) + self.test_node.test_witness_block(block, accepted=True) + 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 ]: + for hashtype in [SIGHASH_ALL, SIGHASH_NONE, SIGHASH_SINGLE]: + hashtype |= sigflag + block = self.build_next_block() + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(prev_utxo.sha256, prev_utxo.n), b"")) + tx.vout.append(CTxOut(prev_utxo.nValue - 1000, scriptPubKey)) + tx.wit.vtxinwit.append(CTxInWitness()) + # Too-large input value + sign_P2PK_witness_input(witness_program, tx, 0, hashtype, prev_utxo.nValue+1, key) + self.update_witness_block_with_transactions(block, [tx]) + self.test_node.test_witness_block(block, accepted=False) + + # Too-small input value + sign_P2PK_witness_input(witness_program, tx, 0, hashtype, prev_utxo.nValue-1, key) + block.vtx.pop() # remove last tx + self.update_witness_block_with_transactions(block, [tx]) + self.test_node.test_witness_block(block, accepted=False) + + # Now try correct value + sign_P2PK_witness_input(witness_program, tx, 0, hashtype, prev_utxo.nValue, key) + block.vtx.pop() + self.update_witness_block_with_transactions(block, [tx]) + self.test_node.test_witness_block(block, accepted=True) + + prev_utxo = UTXO(tx.sha256, 0, tx.vout[0].nValue) + + # Test combinations of signature hashes. + # Split the utxo into a lot of outputs. + # Randomly choose up to 10 to spend, sign with different hashtypes, and + # output to a random number of outputs. Repeat NUM_TESTS times. + # Ensure that we've tested a situation where we use SIGHASH_SINGLE with + # an input index > number of outputs. + NUM_TESTS = 500 + temp_utxos = [] + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(prev_utxo.sha256, prev_utxo.n), b"")) + split_value = prev_utxo.nValue // NUM_TESTS + for i in range(NUM_TESTS): + tx.vout.append(CTxOut(split_value, scriptPubKey)) + tx.wit.vtxinwit.append(CTxInWitness()) + sign_P2PK_witness_input(witness_program, tx, 0, SIGHASH_ALL, prev_utxo.nValue, key) + for i in range(NUM_TESTS): + temp_utxos.append(UTXO(tx.sha256, i, split_value)) + + block = self.build_next_block() + self.update_witness_block_with_transactions(block, [tx]) + self.test_node.test_witness_block(block, accepted=True) + + block = self.build_next_block() + used_sighash_single_out_of_bounds = False + for i in range(NUM_TESTS): + # Choose random number of inputs to use. + num_inputs = random.randint(1, 10) + # Create a slight bias for producing more utxos + num_outputs = random.randint(1, 11) + random.shuffle(temp_utxos) + assert(len(temp_utxos) > num_inputs) + tx = CTransaction() + total_value = 0 + for i in range(num_inputs): + tx.vin.append(CTxIn(COutPoint(temp_utxos[i].sha256, temp_utxos[i].n), b"")) + tx.wit.vtxinwit.append(CTxInWitness()) + total_value += temp_utxos[i].nValue + split_value = total_value // num_outputs + for i in range(num_outputs): + tx.vout.append(CTxOut(split_value, scriptPubKey)) + for i in range(num_inputs): + # Now try to sign each input, using a random hashtype. + anyonecanpay = 0 + if random.randint(0, 1): + anyonecanpay = SIGHASH_ANYONECANPAY + hashtype = random.randint(1, 3) | anyonecanpay + sign_P2PK_witness_input(witness_program, tx, i, hashtype, temp_utxos[i].nValue, key) + if (hashtype == SIGHASH_SINGLE and i >= num_outputs): + used_sighash_single_out_of_bounds = True + tx.rehash() + for i in range(num_outputs): + temp_utxos.append(UTXO(tx.sha256, i, split_value)) + temp_utxos = temp_utxos[num_inputs:] + + block.vtx.append(tx) + + # Test the block periodically, if we're close to maxblocksize + if (get_virtual_size(block) > MAX_BLOCK_SIZE - 1000): + self.update_witness_block_with_transactions(block, []) + self.test_node.test_witness_block(block, accepted=True) + block = self.build_next_block() + + if (not used_sighash_single_out_of_bounds): + print("WARNING: this test run didn't attempt SIGHASH_SINGLE with out-of-bounds index value") + # Test the transactions we've added to the block + if (len(block.vtx) > 1): + self.update_witness_block_with_transactions(block, []) + self.test_node.test_witness_block(block, accepted=True) + + # Now test witness version 0 P2PKH transactions + pubkeyhash = hash160(pubkey) + scriptPKH = CScript([OP_0, pubkeyhash]) + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(temp_utxos[0].sha256, temp_utxos[0].n), b"")) + tx.vout.append(CTxOut(temp_utxos[0].nValue, scriptPKH)) + tx.wit.vtxinwit.append(CTxInWitness()) + sign_P2PK_witness_input(witness_program, tx, 0, SIGHASH_ALL, temp_utxos[0].nValue, key) + tx2 = CTransaction() + 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)]) + sig_hash = SegwitVersion1SignatureHash(script, tx2, 0, SIGHASH_ALL, tx.vout[0].nValue) + signature = key.sign(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL + + # Check that we can't have a scriptSig + tx2.vin[0].scriptSig = CScript([signature, pubkey]) + block = self.build_next_block() + self.update_witness_block_with_transactions(block, [tx, tx2]) + self.test_node.test_witness_block(block, accepted=False) + + # Move the signature to the witness. + block.vtx.pop() + tx2.wit.vtxinwit.append(CTxInWitness()) + tx2.wit.vtxinwit[0].scriptWitness.stack = [signature, pubkey] + tx2.vin[0].scriptSig = b"" + tx2.rehash() + + self.update_witness_block_with_transactions(block, [tx2]) + self.test_node.test_witness_block(block, accepted=True) + + temp_utxos.pop(0) + + # Update self.utxos for later tests. Just spend everything in + # temp_utxos to a corresponding entry in self.utxos + tx = CTransaction() + index = 0 + for i in temp_utxos: + # Just spend to our usual anyone-can-spend output + # Use SIGHASH_SINGLE|SIGHASH_ANYONECANPAY so we can build up + # the signatures as we go. + tx.vin.append(CTxIn(COutPoint(i.sha256, i.n), b"")) + tx.vout.append(CTxOut(i.nValue, CScript([OP_TRUE]))) + tx.wit.vtxinwit.append(CTxInWitness()) + sign_P2PK_witness_input(witness_program, tx, index, SIGHASH_SINGLE|SIGHASH_ANYONECANPAY, i.nValue, key) + index += 1 + block = self.build_next_block() + self.update_witness_block_with_transactions(block, [tx]) + self.test_node.test_witness_block(block, accepted=True) + + for i in range(len(tx.vout)): + self.utxo.append(UTXO(tx.sha256, i, tx.vout[i].nValue)) + + + # Test P2SH wrapped witness programs. + def test_p2sh_witness(self, segwit_activated): + print("\tTesting P2SH witness transactions") + + assert(len(self.utxo)) + + # Prepare the p2sh-wrapped witness output + witness_program = CScript([OP_DROP, OP_TRUE]) + witness_hash = sha256(witness_program) + p2wsh_pubkey = CScript([OP_0, witness_hash]) + p2sh_witness_hash = hash160(p2wsh_pubkey) + scriptPubKey = CScript([OP_HASH160, p2sh_witness_hash, OP_EQUAL]) + scriptSig = CScript([p2wsh_pubkey]) # a push of the redeem script + + # Fund the P2SH output + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) + tx.vout.append(CTxOut(self.utxo[0].nValue-1000, scriptPubKey)) + tx.rehash() + + # Verify mempool acceptance and block validity + self.test_node.test_transaction_acceptance(tx, with_witness=False, accepted=True) + block = self.build_next_block() + self.update_witness_block_with_transactions(block, [tx]) + self.test_node.test_witness_block(block, accepted=True, with_witness=segwit_activated) + sync_blocks(self.nodes) + + # Now test attempts to spend the output. + spend_tx = CTransaction() + spend_tx.vin.append(CTxIn(COutPoint(tx.sha256, 0), scriptSig)) + spend_tx.vout.append(CTxOut(tx.vout[0].nValue-1000, CScript([OP_TRUE]))) + spend_tx.rehash() + + # This transaction should not be accepted into the mempool pre- or + # post-segwit. Mempool acceptance will use SCRIPT_VERIFY_WITNESS which + # will require a witness to spend a witness program regardless of + # segwit activation. Note that older bitcoind's that are not + # segwit-aware would also reject this for failing CLEANSTACK. + self.test_node.test_transaction_acceptance(spend_tx, with_witness=False, accepted=False) + + # Try to put the witness script in the scriptSig, should also fail. + spend_tx.vin[0].scriptSig = CScript([p2wsh_pubkey, b'a']) + spend_tx.rehash() + self.test_node.test_transaction_acceptance(spend_tx, with_witness=False, accepted=False) + + # Now put the witness script in the witness, should succeed after + # segwit activates. + spend_tx.vin[0].scriptSig = scriptSig + spend_tx.rehash() + spend_tx.wit.vtxinwit.append(CTxInWitness()) + spend_tx.wit.vtxinwit[0].scriptWitness.stack = [ b'a', witness_program ] + + # Verify mempool acceptance + self.test_node.test_transaction_acceptance(spend_tx, with_witness=True, accepted=segwit_activated) + block = self.build_next_block() + self.update_witness_block_with_transactions(block, [spend_tx]) + + # If we're before activation, then sending this without witnesses + # should be valid. If we're after activation, then sending this with + # witnesses should be valid. + if segwit_activated: + self.test_node.test_witness_block(block, accepted=True) + else: + self.test_node.test_witness_block(block, accepted=True, with_witness=False) + + # Update self.utxo + self.utxo.pop(0) + self.utxo.append(UTXO(spend_tx.sha256, 0, spend_tx.vout[0].nValue)) + + # Test the behavior of starting up a segwit-aware node after the softfork + # has activated. As segwit requires different block data than pre-segwit + # nodes would have stored, this requires special handling. + # To enable this test, pass --oldbinary=<path-to-pre-segwit-bitcoind> to + # the test. + def test_upgrade_after_activation(self, node, node_id): + print("\tTesting software upgrade after softfork activation") + + assert(node_id != 0) # node0 is assumed to be a segwit-active bitcoind + + # Make sure the nodes are all up + sync_blocks(self.nodes) + + # Restart with the new binary + stop_node(node, node_id) + self.nodes[node_id] = start_node(node_id, self.options.tmpdir, ["-debug"]) + connect_nodes(self.nodes[0], node_id) + + sync_blocks(self.nodes) + + # Make sure that this peer thinks segwit has activated. + assert(get_bip9_status(node, 'segwit')['status'] == "active") + + # Make sure this peers blocks match those of node0. + height = node.getblockcount() + while height >= 0: + block_hash = node.getblockhash(height) + assert_equal(block_hash, self.nodes[0].getblockhash(height)) + assert_equal(self.nodes[0].getblock(block_hash), node.getblock(block_hash)) + height -= 1 + + + def test_witness_sigops(self): + '''Ensure sigop counting is correct inside witnesses.''' + print("\tTesting sigops limit") + + assert(len(self.utxo)) + + # Keep this under MAX_OPS_PER_SCRIPT (201) + witness_program = CScript([OP_TRUE, OP_IF, OP_TRUE, OP_ELSE] + [OP_CHECKMULTISIG]*5 + [OP_CHECKSIG]*193 + [OP_ENDIF]) + witness_hash = sha256(witness_program) + scriptPubKey = CScript([OP_0, witness_hash]) + + sigops_per_script = 20*5 + 193*1 + # We'll produce 2 extra outputs, one with a program that would take us + # over max sig ops, and one with a program that would exactly reach max + # sig ops + outputs = (MAX_SIGOP_COST // sigops_per_script) + 2 + extra_sigops_available = MAX_SIGOP_COST % sigops_per_script + + # We chose the number of checkmultisigs/checksigs to make this work: + assert(extra_sigops_available < 100) # steer clear of MAX_OPS_PER_SCRIPT + + # This script, when spent with the first + # N(=MAX_SIGOP_COST//sigops_per_script) outputs of our transaction, + # would push us just over the block sigop limit. + witness_program_toomany = CScript([OP_TRUE, OP_IF, OP_TRUE, OP_ELSE] + [OP_CHECKSIG]*(extra_sigops_available + 1) + [OP_ENDIF]) + witness_hash_toomany = sha256(witness_program_toomany) + scriptPubKey_toomany = CScript([OP_0, witness_hash_toomany]) + + # If we spend this script instead, we would exactly reach our sigop + # limit (for witness sigops). + witness_program_justright = CScript([OP_TRUE, OP_IF, OP_TRUE, OP_ELSE] + [OP_CHECKSIG]*(extra_sigops_available) + [OP_ENDIF]) + witness_hash_justright = sha256(witness_program_justright) + scriptPubKey_justright = CScript([OP_0, witness_hash_justright]) + + # First split our available utxo into a bunch of outputs + split_value = self.utxo[0].nValue // outputs + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) + for i in range(outputs): + tx.vout.append(CTxOut(split_value, scriptPubKey)) + tx.vout[-2].scriptPubKey = scriptPubKey_toomany + tx.vout[-1].scriptPubKey = scriptPubKey_justright + tx.rehash() + + block_1 = self.build_next_block() + self.update_witness_block_with_transactions(block_1, [tx]) + self.test_node.test_witness_block(block_1, accepted=True) + + tx2 = CTransaction() + # If we try to spend the first n-1 outputs from tx, that should be + # too many sigops. + total_value = 0 + for i in range(outputs-1): + tx2.vin.append(CTxIn(COutPoint(tx.sha256, i), b"")) + tx2.wit.vtxinwit.append(CTxInWitness()) + tx2.wit.vtxinwit[-1].scriptWitness.stack = [ witness_program ] + total_value += tx.vout[i].nValue + tx2.wit.vtxinwit[-1].scriptWitness.stack = [ witness_program_toomany ] + tx2.vout.append(CTxOut(total_value, CScript([OP_TRUE]))) + tx2.rehash() + + block_2 = self.build_next_block() + self.update_witness_block_with_transactions(block_2, [tx2]) + self.test_node.test_witness_block(block_2, accepted=False) + + # Try dropping the last input in tx2, and add an output that has + # too many sigops (contributing to legacy sigop count). + checksig_count = (extra_sigops_available // 4) + 1 + scriptPubKey_checksigs = CScript([OP_CHECKSIG]*checksig_count) + tx2.vout.append(CTxOut(0, scriptPubKey_checksigs)); + tx2.vin.pop() + tx2.wit.vtxinwit.pop() + tx2.vout[0].nValue -= tx.vout[-2].nValue + tx2.rehash() + block_3 = self.build_next_block() + self.update_witness_block_with_transactions(block_3, [tx2]) + self.test_node.test_witness_block(block_3, accepted=False) + + # If we drop the last checksig in this output, the tx should succeed. + block_4 = self.build_next_block() + tx2.vout[-1].scriptPubKey = CScript([OP_CHECKSIG]*(checksig_count-1)) + tx2.rehash() + self.update_witness_block_with_transactions(block_4, [tx2]) + self.test_node.test_witness_block(block_4, accepted=True) + + # Reset the tip back down for the next test + sync_blocks(self.nodes) + for x in self.nodes: + x.invalidateblock(block_4.hash) + + # Try replacing the last input of tx2 to be spending the last + # output of tx + block_5 = self.build_next_block() + tx2.vout.pop() + tx2.vin.append(CTxIn(COutPoint(tx.sha256, outputs-1), b"")) + tx2.wit.vtxinwit.append(CTxInWitness()) + tx2.wit.vtxinwit[-1].scriptWitness.stack = [ witness_program_justright ] + tx2.rehash() + self.update_witness_block_with_transactions(block_5, [tx2]) + self.test_node.test_witness_block(block_5, accepted=True) + + # TODO: test p2sh sigop counting + + def test_getblocktemplate_before_lockin(self): + print("\tTesting getblocktemplate setting of segwit versionbit (before lockin)") + block_version = (self.nodes[0].getblocktemplate())['version'] + assert_equal(block_version & (1 << VB_WITNESS_BIT), 0) + + # Workaround: + # Can either change the tip, or change the mempool and wait 5 seconds + # to trigger a recomputation of getblocktemplate. + self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1) + # Using mocktime lets us avoid sleep() + self.nodes[0].setmocktime(int(time.time())+10) + + block_version = self.nodes[0].getblocktemplate({"rules" : ["segwit"]})['version'] + assert(block_version & (1 << VB_WITNESS_BIT) != 0) + self.nodes[0].setmocktime(0) # undo mocktime + + def run_test(self): + # Setup the p2p connections and start up the network thread. + self.test_node = TestNode() # sets NODE_WITNESS|NODE_NETWORK + self.old_node = TestNode() # only NODE_NETWORK + self.std_node = TestNode() # for testing node1 (fRequireStandard=true) + + self.p2p_connections = [self.test_node, self.old_node] + + self.connections = [] + self.connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.test_node, services=NODE_NETWORK|NODE_WITNESS)) + self.connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.old_node, services=NODE_NETWORK)) + self.connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1], self.std_node, services=NODE_NETWORK|NODE_WITNESS)) + self.test_node.add_connection(self.connections[0]) + self.old_node.add_connection(self.connections[1]) + self.std_node.add_connection(self.connections[2]) + + NetworkThread().start() # Start up network handling in another thread + + # Keep a place to store utxo's that can be used in later tests + self.utxo = [] + + # Test logic begins here + self.test_node.wait_for_verack() + + print("\nStarting tests before segwit lock in:") + + self.test_witness_services() # Verifies NODE_WITNESS + self.test_non_witness_transaction() # non-witness tx's are accepted + self.test_unnecessary_witness_before_segwit_activation() + self.test_block_relay(segwit_activated=False) + + # Advance to segwit being 'started' + self.advance_to_segwit_started() + self.test_getblocktemplate_before_lockin() + + sync_blocks(self.nodes) + + # At lockin, nothing should change. + print("\nTesting behavior post lockin, pre-activation") + self.advance_to_segwit_lockin() + + # Retest unnecessary witnesses + self.test_unnecessary_witness_before_segwit_activation() + 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) + + # Now activate segwit + print("\nTesting behavior after segwit activation") + self.advance_to_segwit_active() + + sync_blocks(self.nodes) + + # Test P2SH witness handling again + self.test_p2sh_witness(segwit_activated=True) + self.test_witness_commitments() + self.test_block_malleability() + self.test_witness_block_size() + self.test_submit_block() + self.test_extra_witness_data() + self.test_max_witness_push_length() + self.test_max_witness_program_length() + 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_signature_version_1() + sync_blocks(self.nodes) + if self.test_upgrade: + self.test_upgrade_after_activation(self.nodes[2], 2) + else: + print("\tSkipping upgrade-after-activation test (use --oldbinary to enable)") + self.test_witness_sigops() + + +if __name__ == '__main__': + SegWitTest().main() diff --git a/qa/rpc-tests/pruning.py b/qa/rpc-tests/pruning.py index d225e29b50..7cbe69c29b 100755 --- a/qa/rpc-tests/pruning.py +++ b/qa/rpc-tests/pruning.py @@ -151,14 +151,14 @@ class PruneTest(BitcoinTestFramework): print("Reconnect nodes") connect_nodes(self.nodes[0], 1) connect_nodes(self.nodes[2], 1) - sync_blocks(self.nodes[0:3]) + sync_blocks(self.nodes[0:3], timeout=120) print("Verify height on node 2:",self.nodes[2].getblockcount()) 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 - sync_blocks(self.nodes[0:3]) + sync_blocks(self.nodes[0:3], timeout=300) usage = calc_usage(self.prunedir) print("Usage should be below target:", usage) 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..3ac32140ba 100755 --- a/qa/rpc-tests/rpcbind_test.py +++ b/qa/rpc-tests/rpcbind_test.py @@ -5,143 +5,106 @@ # 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) + wait_bitcoinds() + + def run_allowip_test(self, 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] + 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.getinfo() + finally: + node = None # make sure connection will be garbage collected and closed + stop_nodes(self.nodes) + wait_bitcoinds() + + 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 new file mode 100755 index 0000000000..745a1d4750 --- /dev/null +++ b/qa/rpc-tests/segwit.py @@ -0,0 +1,246 @@ +#!/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. + +# +# Test the SegWit changeover logic +# + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * +from test_framework.mininode import sha256, ripemd160 + +NODE_0 = 0 +NODE_1 = 1 +NODE_2 = 2 +WIT_V0 = 0 +WIT_V1 = 1 + +def witness_script(version, pubkey): + if (version == 0): + pubkeyhash = bytes_to_hex_str(ripemd160(sha256(hex_str_to_bytes(pubkey)))) + pkscript = "0014" + pubkeyhash + elif (version == 1): + # 1-of-1 multisig + scripthash = bytes_to_hex_str(sha256(hex_str_to_bytes("5121" + pubkey + "51ae"))) + pkscript = "0020" + scripthash + else: + assert("Wrong version" == "0 or 1") + return pkscript + +def addlength(script): + scriptlen = format(len(script)//2, 'x') + assert(len(scriptlen) == 2) + return scriptlen + script + +def create_witnessprogram(version, node, utxo, pubkey, encode_p2sh, amount): + pkscript = witness_script(version, pubkey); + if (encode_p2sh): + p2sh_hash = bytes_to_hex_str(ripemd160(sha256(hex_str_to_bytes(pkscript)))) + pkscript = "a914"+p2sh_hash+"87" + inputs = [] + outputs = {} + inputs.append({ "txid" : utxo["txid"], "vout" : utxo["vout"]} ) + DUMMY_P2SH = "2MySexEGVzZpRgNQ1JdjdP5bRETznm3roQ2" # P2SH of "OP_1 OP_DROP" + outputs[DUMMY_P2SH] = amount + tx_to_witness = node.createrawtransaction(inputs,outputs) + #replace dummy output with our own + tx_to_witness = tx_to_witness[0:110] + addlength(pkscript) + tx_to_witness[-8:] + return tx_to_witness + +def send_to_witness(version, node, utxo, pubkey, encode_p2sh, amount, sign=True, insert_redeem_script=""): + tx_to_witness = create_witnessprogram(version, node, utxo, pubkey, encode_p2sh, amount) + if (sign): + signed = node.signrawtransaction(tx_to_witness) + assert("errors" not in signed or len(["errors"]) == 0) + return node.sendrawtransaction(signed["hex"]) + else: + if (insert_redeem_script): + tx_to_witness = tx_to_witness[0:82] + addlength(insert_redeem_script) + tx_to_witness[84:] + + return node.sendrawtransaction(tx_to_witness) + +def getutxo(txid): + utxo = {} + utxo["vout"] = 0 + 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 __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 3 + + def setup_network(self): + self.nodes = [] + self.nodes.append(start_node(0, self.options.tmpdir, ["-logtimemicros", "-debug", "-walletprematurewitness"])) + self.nodes.append(start_node(1, self.options.tmpdir, ["-logtimemicros", "-debug", "-blockversion=4", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness"])) + self.nodes.append(start_node(2, self.options.tmpdir, ["-logtimemicros", "-debug", "-blockversion=536870915", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness"])) + connect_nodes(self.nodes[1], 0) + connect_nodes(self.nodes[2], 1) + connect_nodes(self.nodes[0], 2) + self.is_network_split = False + self.sync_all() + + def success_mine(self, node, txid, sign, redeem_script=""): + send_to_witness(1, node, getutxo(txid), self.pubkey[0], False, Decimal("49.998"), sign, redeem_script) + block = node.generate(1) + assert_equal(len(node.getblock(block[0])["tx"]), 2) + sync_blocks(self.nodes) + + def skip_mine(self, node, txid, sign, redeem_script=""): + send_to_witness(1, node, getutxo(txid), self.pubkey[0], False, Decimal("49.998"), sign, redeem_script) + block = node.generate(1) + assert_equal(len(node.getblock(block[0])["tx"]), 1) + sync_blocks(self.nodes) + + def fail_accept(self, node, txid, sign, redeem_script=""): + try: + send_to_witness(1, node, getutxo(txid), self.pubkey[0], False, Decimal("49.998"), sign, redeem_script) + except JSONRPCException as exp: + assert(exp.error["code"] == -26) + else: + raise AssertionError("Tx should not have been accepted") + + def fail_mine(self, node, txid, sign, redeem_script=""): + send_to_witness(1, node, getutxo(txid), self.pubkey[0], False, Decimal("49.998"), sign, redeem_script) + try: + node.generate(1) + except JSONRPCException as exp: + assert(exp.error["code"] == -1) + else: + raise AssertionError("Created valid block when TestBlockValidity should have failed") + sync_blocks(self.nodes) + + def run_test(self): + 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 + for i in range(3): + newaddress = self.nodes[i].getnewaddress() + self.pubkey.append(self.nodes[i].validateaddress(newaddress)["pubkey"]) + multiaddress = self.nodes[i].addmultisigaddress(1, [self.pubkey[-1]]) + self.nodes[i].addwitnessaddress(newaddress) + self.nodes[i].addwitnessaddress(multiaddress) + p2sh_ids.append([]) + wit_ids.append([]) + for v in range(2): + p2sh_ids[i].append([]) + wit_ids[i].append([]) + + 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], 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 163 + sync_blocks(self.nodes) + + # Make sure all nodes recognize the transactions as theirs + 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(260) #block 423 + sync_blocks(self.nodes) + + print("Verify default node can't accept any witness format txs before fork") + # unsigned, no scriptsig + self.fail_accept(self.nodes[0], wit_ids[NODE_0][WIT_V0][0], False) + self.fail_accept(self.nodes[0], wit_ids[NODE_0][WIT_V1][0], False) + self.fail_accept(self.nodes[0], p2sh_ids[NODE_0][WIT_V0][0], False) + self.fail_accept(self.nodes[0], p2sh_ids[NODE_0][WIT_V1][0], False) + # unsigned with redeem script + self.fail_accept(self.nodes[0], p2sh_ids[NODE_0][WIT_V0][0], False, addlength(witness_script(0, self.pubkey[0]))) + self.fail_accept(self.nodes[0], p2sh_ids[NODE_0][WIT_V1][0], False, addlength(witness_script(1, self.pubkey[0]))) + # signed + self.fail_accept(self.nodes[0], wit_ids[NODE_0][WIT_V0][0], True) + self.fail_accept(self.nodes[0], wit_ids[NODE_0][WIT_V1][0], True) + self.fail_accept(self.nodes[0], p2sh_ids[NODE_0][WIT_V0][0], True) + self.fail_accept(self.nodes[0], p2sh_ids[NODE_0][WIT_V1][0], True) + + print("Verify witness txs are skipped for mining before the fork") + self.skip_mine(self.nodes[2], wit_ids[NODE_2][WIT_V0][0], True) #block 424 + self.skip_mine(self.nodes[2], wit_ids[NODE_2][WIT_V1][0], True) #block 425 + self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V0][0], True) #block 426 + self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V1][0], True) #block 427 + + # TODO: An old node would see these txs without witnesses and be able to mine them + + print("Verify unsigned bare witness txs in versionbits-setting blocks are valid before the fork") + self.success_mine(self.nodes[2], wit_ids[NODE_2][WIT_V0][1], False) #block 428 + self.success_mine(self.nodes[2], wit_ids[NODE_2][WIT_V1][1], False) #block 429 + + print("Verify unsigned p2sh witness txs without a redeem script are invalid") + self.fail_accept(self.nodes[2], p2sh_ids[NODE_2][WIT_V0][1], False) + self.fail_accept(self.nodes[2], p2sh_ids[NODE_2][WIT_V1][1], False) + + print("Verify unsigned p2sh witness txs with a redeem script in versionbits-settings blocks are valid before the fork") + self.success_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V0][1], False, addlength(witness_script(0, self.pubkey[2]))) #block 430 + self.success_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V1][1], False, addlength(witness_script(1, self.pubkey[2]))) #block 431 + + print("Verify previous witness txs skipped for mining can now be mined") + assert_equal(len(self.nodes[2].getrawmempool()), 4) + block = self.nodes[2].generate(1) #block 432 (first block with new rules; 432 = 144 * 3) + sync_blocks(self.nodes) + assert_equal(len(self.nodes[2].getrawmempool()), 0) + assert_equal(len(self.nodes[2].getblock(block[0])["tx"]), 5) + + print("Verify witness txs without witness data are invalid after the fork") + self.fail_mine(self.nodes[2], wit_ids[NODE_2][WIT_V0][2], False) + self.fail_mine(self.nodes[2], wit_ids[NODE_2][WIT_V1][2], False) + self.fail_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V0][2], False, addlength(witness_script(0, self.pubkey[2]))) + self.fail_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V1][2], False, addlength(witness_script(1, self.pubkey[2]))) + + print("Verify default node can now use witness txs") + self.success_mine(self.nodes[0], wit_ids[NODE_0][WIT_V0][0], True) #block 432 + self.success_mine(self.nodes[0], wit_ids[NODE_0][WIT_V1][0], True) #block 433 + 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 + +if __name__ == '__main__': + SegWitTest().main() diff --git a/qa/rpc-tests/sendheaders.py b/qa/rpc-tests/sendheaders.py index 6ab17d59b3..c3f3180b6b 100755 --- a/qa/rpc-tests/sendheaders.py +++ b/qa/rpc-tests/sendheaders.py @@ -63,6 +63,21 @@ e. Announce 16 more headers that build on that fork. Expect: getdata request for 14 more blocks. f. Announce 1 more header that builds on that fork. Expect: no response. + +Part 5: Test handling of headers that don't connect. +a. Repeat 10 times: + 1. Announce a header that doesn't connect. + Expect: getheaders message + 2. Send headers chain. + Expect: getdata for the missing blocks, tip update. +b. Then send 9 more headers that don't connect. + Expect: getheaders message each time. +c. Announce a header that does connect. + Expect: no response. +d. Announce 49 headers that don't connect. + Expect: getheaders message each time. +e. Announce one more that doesn't connect. + Expect: disconnect. ''' class BaseNode(NodeConnCB): @@ -77,6 +92,8 @@ class BaseNode(NodeConnCB): self.last_getdata = None self.sleep_time = 0.05 self.block_announced = False + self.last_getheaders = None + self.disconnected = False def clear_last_announcement(self): with mininode_lock: @@ -127,6 +144,12 @@ class BaseNode(NodeConnCB): def on_pong(self, conn, message): self.last_pong = message + def on_getheaders(self, conn, message): + self.last_getheaders = message + + def on_close(self, conn): + self.disconnected = True + # Test whether the last announcement we received had the # right header or the right inv # inv and headers should be lists of block hashes @@ -178,6 +201,11 @@ class BaseNode(NodeConnCB): self.sync(test_function, timeout) return + def wait_for_getheaders(self, timeout=60): + test_function = lambda: self.last_getheaders != None + self.sync(test_function, timeout) + return + def wait_for_getdata(self, hash_list, timeout=60): if hash_list == []: return @@ -186,6 +214,11 @@ class BaseNode(NodeConnCB): self.sync(test_function, timeout) return + def wait_for_disconnect(self, timeout=60): + test_function = lambda: self.disconnected + self.sync(test_function, timeout) + return + def send_header_for_blocks(self, new_blocks): headers_message = msg_headers() headers_message.headers = [ CBlockHeader(b) for b in new_blocks ] @@ -510,6 +543,78 @@ class SendHeadersTest(BitcoinTestFramework): print("Part 4: success!") + # Now deliver all those blocks we announced. + [ test_node.send_message(msg_block(x)) for x in blocks ] + + print("Part 5: Testing handling of unconnecting headers") + # First we test that receipt of an unconnecting header doesn't prevent + # chain sync. + for i in range(10): + test_node.last_getdata = None + blocks = [] + # Create two more blocks. + for j in range(2): + blocks.append(create_block(tip, create_coinbase(height), block_time)) + blocks[-1].solve() + tip = blocks[-1].sha256 + block_time += 1 + height += 1 + # Send the header of the second block -> this won't connect. + with mininode_lock: + test_node.last_getheaders = None + test_node.send_header_for_blocks([blocks[1]]) + test_node.wait_for_getheaders(timeout=1) + test_node.send_header_for_blocks(blocks) + test_node.wait_for_getdata([x.sha256 for x in blocks]) + [ test_node.send_message(msg_block(x)) for x in blocks ] + test_node.sync_with_ping() + assert_equal(int(self.nodes[0].getbestblockhash(), 16), blocks[1].sha256) + + blocks = [] + # Now we test that if we repeatedly don't send connecting headers, we + # don't go into an infinite loop trying to get them to connect. + MAX_UNCONNECTING_HEADERS = 10 + for j in range(MAX_UNCONNECTING_HEADERS+1): + blocks.append(create_block(tip, create_coinbase(height), block_time)) + blocks[-1].solve() + tip = blocks[-1].sha256 + block_time += 1 + height += 1 + + for i in range(1, MAX_UNCONNECTING_HEADERS): + # Send a header that doesn't connect, check that we get a getheaders. + with mininode_lock: + test_node.last_getheaders = None + test_node.send_header_for_blocks([blocks[i]]) + test_node.wait_for_getheaders(timeout=1) + + # Next header will connect, should re-set our count: + test_node.send_header_for_blocks([blocks[0]]) + + # Remove the first two entries (blocks[1] would connect): + blocks = blocks[2:] + + # Now try to see how many unconnecting headers we can send + # before we get disconnected. Should be 5*MAX_UNCONNECTING_HEADERS + for i in range(5*MAX_UNCONNECTING_HEADERS - 1): + # Send a header that doesn't connect, check that we get a getheaders. + with mininode_lock: + test_node.last_getheaders = None + test_node.send_header_for_blocks([blocks[i%len(blocks)]]) + test_node.wait_for_getheaders(timeout=1) + + # Eventually this stops working. + with mininode_lock: + self.last_getheaders = None + test_node.send_header_for_blocks([blocks[-1]]) + + # Should get disconnected + test_node.wait_for_disconnect() + with mininode_lock: + self.last_getheaders = True + + print("Part 5: success!") + # Finally, check that the inv node never received a getdata request, # throughout the test assert_equal(inv_node.last_getdata, None) diff --git a/qa/rpc-tests/test_framework/blockstore.py b/qa/rpc-tests/test_framework/blockstore.py index 6120dd574b..1e2bbb277a 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 diff --git a/qa/rpc-tests/test_framework/blocktools.py b/qa/rpc-tests/test_framework/blocktools.py index 26cc396315..f69958823c 100644 --- a/qa/rpc-tests/test_framework/blocktools.py +++ b/qa/rpc-tests/test_framework/blocktools.py @@ -5,7 +5,7 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. from .mininode import * -from .script import CScript, OP_TRUE, OP_CHECKSIG +from .script import CScript, OP_TRUE, OP_CHECKSIG, OP_RETURN # Create a block (with regtest difficulty) def create_block(hashprev, coinbase, nTime=None): @@ -22,6 +22,29 @@ def create_block(hashprev, coinbase, nTime=None): block.calc_sha256() return block +# From BIP141 +WITNESS_COMMITMENT_HEADER = b"\xaa\x21\xa9\xed" + +# According to BIP141, blocks with witness rules active must commit to the +# hash of all in-block transactions including witness. +def add_witness_commitment(block, nonce=0): + # First calculate the merkle root of the block's + # transactions, with witnesses. + witness_nonce = nonce + witness_root = block.calc_witness_merkle_root() + witness_commitment = uint256_from_str(hash256(ser_uint256(witness_root)+ser_uint256(witness_nonce))) + # witness_nonce should go to coinbase witness. + block.vtx[0].wit.vtxinwit = [CTxInWitness()] + block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ser_uint256(witness_nonce)] + + # witness commitment is the last OP_RETURN output in coinbase + output_data = WITNESS_COMMITMENT_HEADER + ser_uint256(witness_commitment) + block.vtx[0].vout.append(CTxOut(0, CScript([OP_RETURN, output_data]))) + block.vtx[0].rehash() + block.hashMerkleRoot = block.calc_merkle_root() + block.rehash() + + def serialize_script_num(value): r = bytearray(0) if value == 0: diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index 6612b99b84..caffab3535 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -28,7 +28,7 @@ import asyncore import time import sys import random -from binascii import hexlify, unhexlify +from .util import hex_str_to_bytes, bytes_to_hex_str from io import BytesIO from codecs import encode import hashlib @@ -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 @@ -46,8 +47,13 @@ MAX_BLOCK_SIZE = 1000000 COIN = 100000000 # 1 btc in satoshis +NODE_NETWORK = (1 << 0) +NODE_GETUTXO = (1 << 1) +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() @@ -63,12 +69,25 @@ mininode_lock = RLock() def sha256(s): return hashlib.new('sha256', s).digest() +def ripemd160(s): + return hashlib.new('ripemd160', s).digest() 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] @@ -76,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 @@ -118,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() @@ -133,29 +144,21 @@ def deser_vector(f, c): return r -def ser_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)) +# ser_function_name: Allow for an alternate serialization function on the +# 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 = ser_compact_size(len(l)) for i in l: - r += i.serialize() + if ser_function_name: + r += getattr(i, ser_function_name)() + else: + r += i.serialize() return r 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) @@ -164,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) @@ -194,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] @@ -224,27 +199,19 @@ 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 # Deserialize from a hex string representation (eg from RPC) def FromHex(obj, hex_string): - obj.deserialize(BytesIO(unhexlify(hex_string.encode('ascii')))) + obj.deserialize(BytesIO(hex_str_to_bytes(hex_string))) return obj # Convert a binary-serializable object to hex (eg for submission via RPC) def ToHex(obj): - return hexlify(obj.serialize()).decode('ascii') + return bytes_to_hex_str(obj.serialize()) # Objects that map to bitcoind objects, which can be serialized/deserialized @@ -273,12 +240,17 @@ class CAddress(object): return "CAddress(nServices=%i ip=%s port=%i)" % (self.nServices, self.ip, self.port) +MSG_WITNESS_FLAG = 1<<30 class CInv(object): typemap = { 0: "Error", 1: "TX", - 2: "Block"} + 2: "Block", + 1|MSG_WITNESS_FLAG: "WitnessTx", + 2|MSG_WITNESS_FLAG : "WitnessBlock", + 4: "CompactBlock" + } def __init__(self, t=0, h=0): self.type = t @@ -362,7 +334,7 @@ class CTxIn(object): def __repr__(self): return "CTxIn(prevout=%s scriptSig=%s nSequence=%i)" \ - % (repr(self.prevout), hexlify(self.scriptSig), + % (repr(self.prevout), bytes_to_hex_str(self.scriptSig), self.nSequence) @@ -384,7 +356,67 @@ class CTxOut(object): def __repr__(self): return "CTxOut(nValue=%i.%08i scriptPubKey=%s)" \ % (self.nValue // COIN, self.nValue % COIN, - hexlify(self.scriptPubKey)) + bytes_to_hex_str(self.scriptPubKey)) + + +class CScriptWitness(object): + def __init__(self): + # stack is a vector of strings + self.stack = [] + + def __repr__(self): + return "CScriptWitness(%s)" % \ + (",".join([bytes_to_hex_str(x) for x in self.stack])) + + def is_null(self): + if self.stack: + return False + return True + + +class CTxInWitness(object): + def __init__(self): + self.scriptWitness = CScriptWitness() + + def deserialize(self, f): + self.scriptWitness.stack = deser_string_vector(f) + + def serialize(self): + return ser_string_vector(self.scriptWitness.stack) + + def __repr__(self): + return repr(self.scriptWitness) + + def is_null(self): + return self.scriptWitness.is_null() + + +class CTxWitness(object): + def __init__(self): + self.vtxinwit = [] + + def deserialize(self, f): + for i in range(len(self.vtxinwit)): + self.vtxinwit[i].deserialize(f) + + def serialize(self): + r = b"" + # This is different than the usual vector serialization -- + # we omit the length of the vector, which is required to be + # the same length as the transaction's vin vector. + for x in self.vtxinwit: + r += x.serialize() + return r + + def __repr__(self): + return "CTxWitness(%s)" % \ + (';'.join([repr(x) for x in self.vtxinwit])) + + def is_null(self): + for x in self.vtxinwit: + if not x.is_null(): + return False + return True class CTransaction(object): @@ -393,6 +425,7 @@ class CTransaction(object): self.nVersion = 1 self.vin = [] self.vout = [] + self.wit = CTxWitness() self.nLockTime = 0 self.sha256 = None self.hash = None @@ -401,18 +434,31 @@ class CTransaction(object): self.vin = copy.deepcopy(tx.vin) self.vout = copy.deepcopy(tx.vout) self.nLockTime = tx.nLockTime - self.sha256 = None - self.hash = None + self.sha256 = tx.sha256 + self.hash = tx.hash + self.wit = copy.deepcopy(tx.wit) def deserialize(self, f): self.nVersion = struct.unpack("<i", f.read(4))[0] self.vin = deser_vector(f, CTxIn) - self.vout = deser_vector(f, CTxOut) + flags = 0 + if len(self.vin) == 0: + flags = struct.unpack("<B", f.read(1))[0] + # Not sure why flags can't be zero, but this + # matches the implementation in bitcoind + if (flags != 0): + self.vin = deser_vector(f, CTxIn) + self.vout = deser_vector(f, CTxOut) + else: + self.vout = deser_vector(f, CTxOut) + if flags != 0: + self.wit.vtxinwit = [CTxInWitness()]*len(self.vin) + self.wit.deserialize(f) self.nLockTime = struct.unpack("<I", f.read(4))[0] self.sha256 = None self.hash = None - def serialize(self): + def serialize_without_witness(self): r = b"" r += struct.pack("<i", self.nVersion) r += ser_vector(self.vin) @@ -420,13 +466,48 @@ class CTransaction(object): r += struct.pack("<I", self.nLockTime) return r + # Only serialize with witness when explicitly called for + def serialize_with_witness(self): + flags = 0 + if not self.wit.is_null(): + flags |= 1 + r = b"" + r += struct.pack("<i", self.nVersion) + if flags: + dummy = [] + r += ser_vector(dummy) + r += struct.pack("<B", flags) + r += ser_vector(self.vin) + r += ser_vector(self.vout) + if flags & 1: + if (len(self.wit.vtxinwit) != len(self.vin)): + # vtxinwit must have the same length as vin + self.wit.vtxinwit = self.wit.vtxinwit[:len(self.vin)] + for i in range(len(self.wit.vtxinwit), len(self.vin)): + self.wit.vtxinwit.append(CTxInWitness()) + r += self.wit.serialize() + r += struct.pack("<I", self.nLockTime) + return r + + # Regular serialization is without witness -- must explicitly + # call serialize_with_witness to include witness data. + def serialize(self): + return self.serialize_without_witness() + + # Recalculate the txid (transaction hash without witness) def rehash(self): self.sha256 = None self.calc_sha256() - def calc_sha256(self): + # We will only cache the serialization without witness in + # self.sha256 and self.hash -- those are expected to be the txid. + def calc_sha256(self, with_witness=False): + if with_witness: + # Don't cache the result, just return it + return uint256_from_str(hash256(self.serialize_with_witness())) + if self.sha256 is None: - self.sha256 = uint256_from_str(hash256(self.serialize())) + self.sha256 = uint256_from_str(hash256(self.serialize_without_witness())) self.hash = encode(hash256(self.serialize())[::-1], 'hex_codec').decode('ascii') def is_valid(self): @@ -518,17 +599,17 @@ class CBlock(CBlockHeader): super(CBlock, self).deserialize(f) self.vtx = deser_vector(f, CTransaction) - def serialize(self): + def serialize(self, with_witness=False): r = b"" r += super(CBlock, self).serialize() - r += ser_vector(self.vtx) + if with_witness: + r += ser_vector(self.vtx, "serialize_with_witness") + else: + r += ser_vector(self.vtx) return r - def calc_merkle_root(self): - hashes = [] - for tx in self.vtx: - tx.calc_sha256() - hashes.append(ser_uint256(tx.sha256)) + # Calculate the merkle root given a vector of transaction hashes + def get_merkle_root(self, hashes): while len(hashes) > 1: newhashes = [] for i in range(0, len(hashes), 2): @@ -537,6 +618,24 @@ class CBlock(CBlockHeader): hashes = newhashes return uint256_from_str(hashes[0]) + def calc_merkle_root(self): + hashes = [] + for tx in self.vtx: + tx.calc_sha256() + hashes.append(ser_uint256(tx.sha256)) + return self.get_merkle_root(hashes) + + def calc_witness_merkle_root(self): + # For witness root purposes, the hash of the + # coinbase, with witness, is defined to be 0...0 + hashes = [ser_uint256(0)] + + for tx in self.vtx[1:]: + # Calculate the hashes with witness data + hashes.append(ser_uint256(tx.calc_sha256(True))) + + return self.get_merkle_root(hashes) + def is_valid(self): self.calc_sha256() target = uint256_from_compact(self.nBits) @@ -637,6 +736,187 @@ 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 __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) + + 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] + 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)) + + +# 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 = [] + + 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): + 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 ] + + def initialize_from_block(self, block, nonce=0, prefill_list = [0]): + self.header = CBlockHeader(block) + self.nonce = nonce + self.prefilled_txn = [ PrefilledTransaction(i, block.vtx[i]) for i in prefill_list ] + self.shortids = [] + [k0, k1] = self.get_siphash_keys() + for i in range(len(block.vtx)): + if i not in prefill_list: + self.shortids.append(calculate_shortid(k0, k1, block.vtx[i].sha256)) + + 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" @@ -812,11 +1092,16 @@ class msg_tx(object): self.tx.deserialize(f) def serialize(self): - return self.tx.serialize() + return self.tx.serialize_without_witness() def __repr__(self): return "msg_tx(tx=%s)" % (repr(self.tx)) +class msg_witness_tx(msg_tx): + + def serialize(self): + return self.tx.serialize_with_witness() + class msg_block(object): command = b"block" @@ -849,6 +1134,12 @@ class msg_generic(object): def __repr__(self): return "msg_generic()" +class msg_witness_block(msg_block): + + def serialize(self): + r = self.block.serialize(with_witness=True) + return r + class msg_getaddr(object): command = b"getaddr" @@ -947,6 +1238,7 @@ class msg_sendheaders(object): def __repr__(self): return "msg_sendheaders()" + # getheaders message has # number of entries # vector of hashes @@ -1059,6 +1351,79 @@ 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)) + # This is what a callback should look like for NodeConn # Reimplement the on_* functions to provide handling for events class NodeConnCB(object): @@ -1068,6 +1433,8 @@ class NodeConnCB(object): # tests; it causes message delivery to sleep for the specified time # before acquiring the global lock and delivering the next message. self.deliver_sleep_time = None + # Remember the services our peer has advertised + self.peer_services = None def set_deliver_sleep_time(self, value): with mininode_lock: @@ -1105,6 +1472,7 @@ class NodeConnCB(object): conn.ver_send = min(MY_VERSION, message.nVersion) if message.nVersion < 209: conn.ver_recv = conn.ver_send + conn.nServices = message.nServices def on_verack(self, conn, message): conn.ver_recv = conn.ver_send @@ -1135,6 +1503,11 @@ class NodeConnCB(object): def on_mempool(self, conn): pass def on_pong(self, conn, message): pass def on_feefilter(self, conn, message): pass + def on_sendheaders(self, conn, message): pass + def on_sendcmpct(self, conn, message): pass + def on_cmpctblock(self, conn, message): pass + def on_getblocktxn(self, conn, message): pass + def on_blocktxn(self, conn, message): pass # More useful callbacks and functions for NodeConnCB's which have a single NodeConn class SingleNodeConnCB(NodeConnCB): @@ -1151,6 +1524,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 @@ -1183,15 +1560,20 @@ class NodeConn(asyncore.dispatcher): b"getheaders": msg_getheaders, b"reject": msg_reject, b"mempool": msg_mempool, - b"feefilter": msg_feefilter + b"feefilter": msg_feefilter, + 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 "testnet3": b"\x0b\x11\x09\x07", # testnet3 - "regtest": b"\xfa\xbf\xb5\xda" # regtest + "regtest": b"\xfa\xbf\xb5\xda", # regtest } - def __init__(self, dstaddr, dstport, rpc, callback, net="regtest", services=1): + def __init__(self, dstaddr, dstport, rpc, callback, net="regtest", services=NODE_NETWORK): asyncore.dispatcher.__init__(self, map=mininode_socket_map) self.log = logging.getLogger("NodeConn(%s:%d)" % (dstaddr, dstport)) self.dstaddr = dstaddr @@ -1206,6 +1588,7 @@ class NodeConn(asyncore.dispatcher): self.network = net self.cb = callback self.disconnect = False + self.nServices = 0 # stuff version msg into sendbuf vt = msg_version() diff --git a/qa/rpc-tests/test_framework/script.py b/qa/rpc-tests/test_framework/script.py index 44a894fc8f..83bbf20479 100644 --- a/qa/rpc-tests/test_framework/script.py +++ b/qa/rpc-tests/test_framework/script.py @@ -15,8 +15,9 @@ Functionality to build scripts, as well as SignatureHash(). """ -from .mininode import CTransaction, CTxOut, hash256 +from .mininode import CTransaction, CTxOut, sha256, hash256, uint256_from_str, ser_uint256, ser_string from binascii import hexlify +import hashlib import sys bchr = chr @@ -36,6 +37,10 @@ MAX_SCRIPT_OPCODES = 201 OPCODE_NAMES = {} +def hash160(s): + return hashlib.new('ripemd160', sha256(s)).digest() + + _opcode_instances = [] class CScriptOp(int): """A single script opcode""" @@ -228,7 +233,7 @@ OP_CHECKMULTISIGVERIFY = CScriptOp(0xaf) # expansion OP_NOP1 = CScriptOp(0xb0) OP_CHECKLOCKTIMEVERIFY = CScriptOp(0xb1) -OP_NOP3 = CScriptOp(0xb2) +OP_CHECKSEQUENCEVERIFY = CScriptOp(0xb2) OP_NOP4 = CScriptOp(0xb3) OP_NOP5 = CScriptOp(0xb4) OP_NOP6 = CScriptOp(0xb5) @@ -355,7 +360,7 @@ VALID_OPCODES = { OP_NOP1, OP_CHECKLOCKTIMEVERIFY, - OP_NOP3, + OP_CHECKSEQUENCEVERIFY, OP_NOP4, OP_NOP5, OP_NOP6, @@ -474,7 +479,7 @@ OPCODE_NAMES.update({ OP_CHECKMULTISIGVERIFY : 'OP_CHECKMULTISIGVERIFY', OP_NOP1 : 'OP_NOP1', OP_CHECKLOCKTIMEVERIFY : 'OP_CHECKLOCKTIMEVERIFY', - OP_NOP3 : 'OP_NOP3', + OP_CHECKSEQUENCEVERIFY : 'OP_CHECKSEQUENCEVERIFY', OP_NOP4 : 'OP_NOP4', OP_NOP5 : 'OP_NOP5', OP_NOP6 : 'OP_NOP6', @@ -593,7 +598,7 @@ OPCODES_BY_NAME = { 'OP_CHECKMULTISIGVERIFY' : OP_CHECKMULTISIGVERIFY, 'OP_NOP1' : OP_NOP1, 'OP_CHECKLOCKTIMEVERIFY' : OP_CHECKLOCKTIMEVERIFY, - 'OP_NOP3' : OP_NOP3, + 'OP_CHECKSEQUENCEVERIFY' : OP_CHECKSEQUENCEVERIFY, 'OP_NOP4' : OP_NOP4, 'OP_NOP5' : OP_NOP5, 'OP_NOP6' : OP_NOP6, @@ -877,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)): @@ -895,3 +900,48 @@ def SignatureHash(script, txTo, inIdx, hashtype): hash = hash256(s) return (hash, None) + +# TODO: Allow cached hashPrevouts/hashSequence/hashOutputs to be provided. +# Performance optimization probably not necessary for python tests, however. +# Note that this corresponds to sigversion == 1 in EvalScript, which is used +# for version 0 witnesses. +def SegwitVersion1SignatureHash(script, txTo, inIdx, hashtype, amount): + + hashPrevouts = 0 + hashSequence = 0 + hashOutputs = 0 + + if not (hashtype & SIGHASH_ANYONECANPAY): + serialize_prevouts = bytes() + for i in txTo.vin: + serialize_prevouts += i.prevout.serialize() + hashPrevouts = uint256_from_str(hash256(serialize_prevouts)) + + if (not (hashtype & SIGHASH_ANYONECANPAY) and (hashtype & 0x1f) != SIGHASH_SINGLE and (hashtype & 0x1f) != SIGHASH_NONE): + serialize_sequence = bytes() + for i in txTo.vin: + serialize_sequence += struct.pack("<I", i.nSequence) + hashSequence = uint256_from_str(hash256(serialize_sequence)) + + if ((hashtype & 0x1f) != SIGHASH_SINGLE and (hashtype & 0x1f) != SIGHASH_NONE): + serialize_outputs = bytes() + for o in txTo.vout: + serialize_outputs += o.serialize() + hashOutputs = uint256_from_str(hash256(serialize_outputs)) + elif ((hashtype & 0x1f) == SIGHASH_SINGLE and inIdx < len(txTo.vout)): + serialize_outputs = txTo.vout[inIdx].serialize() + hashOutputs = uint256_from_str(hash256(serialize_outputs)) + + ss = bytes() + ss += struct.pack("<i", txTo.nVersion) + ss += ser_uint256(hashPrevouts) + ss += ser_uint256(hashSequence) + ss += txTo.vin[inIdx].prevout.serialize() + ss += ser_string(script) + ss += struct.pack("<q", amount) + ss += struct.pack("<I", txTo.vin[inIdx].nSequence) + ss += ser_uint256(hashOutputs) + ss += struct.pack("<i", txTo.nLockTime) + ss += struct.pack("<I", hashtype) + + return hash256(ss) 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 30e8b5755d..186cf866cf 100755 --- a/qa/rpc-tests/test_framework/test_framework.py +++ b/qa/rpc-tests/test_framework/test_framework.py @@ -20,6 +20,7 @@ from .util import ( sync_blocks, sync_mempools, stop_nodes, + stop_node, wait_bitcoinds, enable_coverage, check_json_precision, @@ -47,7 +48,10 @@ 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) def setup_nodes(self): return start_nodes(self.num_nodes, self.options.tmpdir) @@ -108,6 +112,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", @@ -119,7 +125,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) @@ -170,6 +177,8 @@ class BitcoinTestFramework(object): 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..eee77f1a10 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -125,12 +125,16 @@ 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_mempools(rpc_connections, wait=1, timeout=60): @@ -171,7 +175,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 +205,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 +214,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 +222,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))) @@ -257,13 +269,13 @@ def initialize_chain(test_dir, num_nodes): 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 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..6028d2c20b --- /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) 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 new file mode 100755 index 0000000000..a49d91f6f4 --- /dev/null +++ b/qa/rpc-tests/wallet-hd.py @@ -0,0 +1,87 @@ +#!/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, +) +import os +import shutil + + +class WalletHDTest(BitcoinTestFramework): + + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 2 + self.node_args = [['-usehd=0'], ['-usehd=1', '-keypool=0']] + + def setup_network(self): + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, self.node_args) + self.is_network_split = False + connect_nodes_bi(self.nodes, 0, 1) + + def run_test (self): + tmpdir = self.options.tmpdir + + # Make sure we use hd, keep masterkeyid + masterkeyid = self.nodes[1].getwalletinfo()['hdmasterkeyid'] + assert_equal(len(masterkeyid), 40) + + # Import a non-HD private key in the HD wallet + non_hd_add = self.nodes[0].getnewaddress() + self.nodes[1].importprivkey(self.nodes[0].dumpprivkey(non_hd_add)) + + # 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") + + # Derive some HD addresses and remember the last + # Also send funds to each add + self.nodes[0].generate(101) + hd_add = None + num_hd_adds = 300 + for i in range(num_hd_adds): + hd_add = self.nodes[1].getnewaddress() + hd_info = self.nodes[1].validateaddress(hd_add) + assert_equal(hd_info["hdkeypath"], "m/0'/0'/"+str(i+1)+"'") + assert_equal(hd_info["hdmasterkeyid"], masterkeyid) + self.nodes[0].sendtoaddress(hd_add, 1) + self.nodes[0].generate(1) + self.nodes[0].sendtoaddress(non_hd_add, 1) + self.nodes[0].generate(1) + + self.sync_all() + assert_equal(self.nodes[1].getbalance(), num_hd_adds + 1) + + 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") + self.nodes[1] = start_node(1, self.options.tmpdir, self.node_args[1]) + #connect_nodes_bi(self.nodes, 0, 1) + + # Assert that derivation is deterministic + hd_add_2 = None + for _ in range(num_hd_adds): + hd_add_2 = self.nodes[1].getnewaddress() + hd_info_2 = self.nodes[1].validateaddress(hd_add_2) + assert_equal(hd_info_2["hdkeypath"], "m/0'/0'/"+str(_+1)+"'") + assert_equal(hd_info_2["hdmasterkeyid"], masterkeyid) + assert_equal(hd_add, hd_add_2) + + # Needs rescan + self.stop_node(1) + self.nodes[1] = start_node(1, self.options.tmpdir, self.node_args[1] + ['-rescan']) + #connect_nodes_bi(self.nodes, 0, 1) + assert_equal(self.nodes[1].getbalance(), num_hd_adds + 1) + + +if __name__ == '__main__': + WalletHDTest().main () diff --git a/qa/rpc-tests/wallet.py b/qa/rpc-tests/wallet.py index 5d96e7a6e5..3420be1a2e 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) 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): |