diff options
Diffstat (limited to 'qa')
54 files changed, 985 insertions, 678 deletions
diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 5004b09c18..669c508ccd 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -22,6 +22,7 @@ For a description of arguments recognized by test scripts, see """ import os +import time import shutil import sys import subprocess @@ -47,6 +48,10 @@ opts = set() passOn = "" p = re.compile("^--") +bold = ("","") +if (os.name == 'posix'): + bold = ('\033[0m', '\033[1m') + for arg in sys.argv[1:]: if arg == '--coverage': ENABLE_COVERAGE = 1 @@ -57,8 +62,10 @@ for arg in sys.argv[1:]: #Set env vars buildDir = BUILDDIR -os.environ["BITCOIND"] = buildDir + '/src/bitcoind' + EXEEXT -os.environ["BITCOINCLI"] = buildDir + '/src/bitcoin-cli' + EXEEXT +if "BITCOIND" not in os.environ: + os.environ["BITCOIND"] = buildDir + '/src/bitcoind' + EXEEXT +if "BITCOINCLI" not in os.environ: + os.environ["BITCOINCLI"] = buildDir + '/src/bitcoin-cli' + EXEEXT #Disable Windows tests by default if EXEEXT == ".exe" and "-win" not in opts: @@ -77,8 +84,10 @@ testScripts = [ 'rawtransactions.py', 'rest.py', 'mempool_spendcoinbase.py', - 'mempool_coinbase_spends.py', + 'mempool_reorg.py', + 'mempool_limit.py', 'httpbasics.py', + 'multi_rpc.py', 'zapwallettxes.py', 'proxy_test.py', 'merkle_blocks.py', @@ -92,6 +101,10 @@ testScripts = [ 'blockchain.py', 'disablewallet.py', 'sendheaders.py', + 'keypool.py', + 'prioritise_transaction.py', + 'invalidblockrequest.py', + 'invalidtxrequest.py', ] testScriptsExt = [ 'bip65-cltv.py', @@ -105,11 +118,9 @@ testScriptsExt = [ 'pruning.py', 'forknotify.py', 'invalidateblock.py', - 'keypool.py', # 'rpcbind_test.py', #temporary, bug in libevent, see #6655 'smartfees.py', 'maxblocksinflight.py', - 'invalidblockrequest.py', 'p2p-acceptblock.py', 'mempool_packages.py', 'maxuploadtarget.py', @@ -126,7 +137,7 @@ def runtests(): if ENABLE_COVERAGE: coverage = RPCCoverage() - print("Initializing coverage directory at %s" % coverage.dir) + print("Initializing coverage directory at %s\n" % coverage.dir) if(ENABLE_WALLET == 1 and ENABLE_UTILS == 1 and ENABLE_BITCOIND == 1): rpcTestDir = buildDir + '/qa/rpc-tests/' @@ -141,10 +152,12 @@ def runtests(): or run_extended or testScripts[i] in opts or re.sub(".py$", "", testScripts[i]) in opts ): - print("Running testscript " + testScripts[i] + "...") + print("Running testscript %s%s%s ..." % (bold[1], testScripts[i], bold[0])) + time0 = time.time() subprocess.check_call( rpcTestDir + testScripts[i] + flags, shell=True) + print("Duration: %s s\n" % (int(time.time() - time0))) # exit if help is called so we print just one set of # instructions @@ -156,12 +169,14 @@ def runtests(): for i in range(len(testScriptsExt)): if (run_extended or testScriptsExt[i] in opts or re.sub(".py$", "", testScriptsExt[i]) in opts): + print( "Running 2nd level testscript " - + testScriptsExt[i] + "...") - + + "%s%s%s ..." % (bold[1], testScriptsExt[i], bold[0])) + time0 = time.time() subprocess.check_call( rpcTestDir + testScriptsExt[i] + flags, shell=True) + print("Duration: %s s\n" % (int(time.time() - time0))) if coverage: coverage.report_rpc_coverage() diff --git a/qa/replace-by-fee/.gitignore b/qa/replace-by-fee/.gitignore deleted file mode 100644 index b2c4f4657a..0000000000 --- a/qa/replace-by-fee/.gitignore +++ /dev/null @@ -1 +0,0 @@ -python-bitcoinlib diff --git a/qa/replace-by-fee/README.md b/qa/replace-by-fee/README.md deleted file mode 100644 index baad86de9a..0000000000 --- a/qa/replace-by-fee/README.md +++ /dev/null @@ -1,13 +0,0 @@ -Replace-by-fee regression tests -=============================== - -First get version v0.5.0 of the python-bitcoinlib library. In this directory -run: - - git clone -n https://github.com/petertodd/python-bitcoinlib - (cd python-bitcoinlib && git checkout 8270bfd9c6ac37907d75db3d8b9152d61c7255cd) - -Then run the tests themselves with a bitcoind available running in regtest -mode: - - ./rbf-tests.py diff --git a/qa/replace-by-fee/rbf-tests.py b/qa/replace-by-fee/rbf-tests.py deleted file mode 100755 index 1ee6c83875..0000000000 --- a/qa/replace-by-fee/rbf-tests.py +++ /dev/null @@ -1,360 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2015 The Bitcoin Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# -# Test replace-by-fee -# - -import os -import sys - -# Add python-bitcoinlib to module search path, prior to any system-wide -# python-bitcoinlib. -sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "python-bitcoinlib")) - -import unittest - -import bitcoin -bitcoin.SelectParams('regtest') - -import bitcoin.rpc - -from bitcoin.core import * -from bitcoin.core.script import * -from bitcoin.wallet import * - -MAX_REPLACEMENT_LIMIT = 100 - -class Test_ReplaceByFee(unittest.TestCase): - proxy = None - - @classmethod - def setUpClass(cls): - if cls.proxy is None: - cls.proxy = bitcoin.rpc.Proxy() - - @classmethod - def mine_mempool(cls): - """Mine until mempool is empty""" - mempool_size = 1 - while mempool_size: - cls.proxy.call('generate', 1) - new_mempool_size = len(cls.proxy.getrawmempool()) - - # It's possible to get stuck in a loop here if the mempool has - # transactions that can't be mined. - assert(new_mempool_size != mempool_size) - mempool_size = new_mempool_size - - @classmethod - def tearDownClass(cls): - # Make sure mining works - cls.mine_mempool() - - def make_txout(self, amount, confirmed=True, scriptPubKey=CScript([1])): - """Create a txout with a given amount and scriptPubKey - - Mines coins as needed. - - confirmed - txouts created will be confirmed in the blockchain; - unconfirmed otherwise. - """ - fee = 1*COIN - while self.proxy.getbalance() < amount + fee: - self.proxy.call('generate', 100) - - addr = P2SHBitcoinAddress.from_redeemScript(CScript([])) - txid = self.proxy.sendtoaddress(addr, amount + fee) - - tx1 = self.proxy.getrawtransaction(txid) - - i = None - for i, txout in enumerate(tx1.vout): - if txout.scriptPubKey == addr.to_scriptPubKey(): - break - assert i is not None - - tx2 = CTransaction([CTxIn(COutPoint(txid, i), CScript([1, CScript([])]), nSequence=0)], - [CTxOut(amount, scriptPubKey)]) - - tx2_txid = self.proxy.sendrawtransaction(tx2, True) - - # If requested, ensure txouts are confirmed. - if confirmed: - self.mine_mempool() - - return COutPoint(tx2_txid, 0) - - def test_simple_doublespend(self): - """Simple doublespend""" - tx0_outpoint = self.make_txout(1.1*COIN) - - tx1a = CTransaction([CTxIn(tx0_outpoint, nSequence=0)], - [CTxOut(1*COIN, CScript([b'a']))]) - tx1a_txid = self.proxy.sendrawtransaction(tx1a, True) - - # Should fail because we haven't changed the fee - tx1b = CTransaction([CTxIn(tx0_outpoint, nSequence=0)], - [CTxOut(1*COIN, CScript([b'b']))]) - - try: - tx1b_txid = self.proxy.sendrawtransaction(tx1b, True) - except bitcoin.rpc.JSONRPCException as exp: - self.assertEqual(exp.error['code'], -26) # insufficient fee - else: - self.fail() - - # Extra 0.1 BTC fee - tx1b = CTransaction([CTxIn(tx0_outpoint, nSequence=0)], - [CTxOut(0.9*COIN, CScript([b'b']))]) - tx1b_txid = self.proxy.sendrawtransaction(tx1b, True) - - # tx1a is in fact replaced - with self.assertRaises(IndexError): - self.proxy.getrawtransaction(tx1a_txid) - - self.assertEqual(tx1b, self.proxy.getrawtransaction(tx1b_txid)) - - def test_doublespend_chain(self): - """Doublespend of a long chain""" - - initial_nValue = 50*COIN - tx0_outpoint = self.make_txout(initial_nValue) - - prevout = tx0_outpoint - remaining_value = initial_nValue - chain_txids = [] - while remaining_value > 10*COIN: - remaining_value -= 1*COIN - tx = CTransaction([CTxIn(prevout, nSequence=0)], - [CTxOut(remaining_value, CScript([1]))]) - txid = self.proxy.sendrawtransaction(tx, True) - chain_txids.append(txid) - prevout = COutPoint(txid, 0) - - # Whether the double-spend is allowed is evaluated by including all - # child fees - 40 BTC - so this attempt is rejected. - dbl_tx = CTransaction([CTxIn(tx0_outpoint, nSequence=0)], - [CTxOut(initial_nValue - 30*COIN, CScript([1]))]) - - try: - self.proxy.sendrawtransaction(dbl_tx, True) - except bitcoin.rpc.JSONRPCException as exp: - self.assertEqual(exp.error['code'], -26) # insufficient fee - else: - self.fail() - - # Accepted with sufficient fee - dbl_tx = CTransaction([CTxIn(tx0_outpoint, nSequence=0)], - [CTxOut(1*COIN, CScript([1]))]) - self.proxy.sendrawtransaction(dbl_tx, True) - - for doublespent_txid in chain_txids: - with self.assertRaises(IndexError): - self.proxy.getrawtransaction(doublespent_txid) - - def test_doublespend_tree(self): - """Doublespend of a big tree of transactions""" - - initial_nValue = 50*COIN - tx0_outpoint = self.make_txout(initial_nValue) - - def branch(prevout, initial_value, max_txs, *, tree_width=5, fee=0.0001*COIN, _total_txs=None): - if _total_txs is None: - _total_txs = [0] - if _total_txs[0] >= max_txs: - return - - txout_value = (initial_value - fee) // tree_width - if txout_value < fee: - return - - vout = [CTxOut(txout_value, CScript([i+1])) - for i in range(tree_width)] - tx = CTransaction([CTxIn(prevout, nSequence=0)], - vout) - - self.assertTrue(len(tx.serialize()) < 100000) - txid = self.proxy.sendrawtransaction(tx, True) - yield tx - _total_txs[0] += 1 - - for i, txout in enumerate(tx.vout): - yield from branch(COutPoint(txid, i), txout_value, - max_txs, - tree_width=tree_width, fee=fee, - _total_txs=_total_txs) - - fee = 0.0001*COIN - n = MAX_REPLACEMENT_LIMIT - tree_txs = list(branch(tx0_outpoint, initial_nValue, n, fee=fee)) - self.assertEqual(len(tree_txs), n) - - # Attempt double-spend, will fail because too little fee paid - dbl_tx = CTransaction([CTxIn(tx0_outpoint, nSequence=0)], - [CTxOut(initial_nValue - fee*n, CScript([1]))]) - try: - self.proxy.sendrawtransaction(dbl_tx, True) - except bitcoin.rpc.JSONRPCException as exp: - self.assertEqual(exp.error['code'], -26) # insufficient fee - else: - self.fail() - - # 1 BTC fee is enough - dbl_tx = CTransaction([CTxIn(tx0_outpoint, nSequence=0)], - [CTxOut(initial_nValue - fee*n - 1*COIN, CScript([1]))]) - self.proxy.sendrawtransaction(dbl_tx, True) - - for tx in tree_txs: - with self.assertRaises(IndexError): - self.proxy.getrawtransaction(tx.GetHash()) - - # Try again, but with more total transactions than the "max txs - # double-spent at once" anti-DoS limit. - for n in (MAX_REPLACEMENT_LIMIT, MAX_REPLACEMENT_LIMIT*2): - fee = 0.0001*COIN - tx0_outpoint = self.make_txout(initial_nValue) - tree_txs = list(branch(tx0_outpoint, initial_nValue, n, fee=fee)) - self.assertEqual(len(tree_txs), n) - - dbl_tx = CTransaction([CTxIn(tx0_outpoint, nSequence=0)], - [CTxOut(initial_nValue - fee*n, CScript([1]))]) - try: - self.proxy.sendrawtransaction(dbl_tx, True) - except bitcoin.rpc.JSONRPCException as exp: - self.assertEqual(exp.error['code'], -26) - else: - self.fail() - - for tx in tree_txs: - self.proxy.getrawtransaction(tx.GetHash()) - - def test_replacement_feeperkb(self): - """Replacement requires fee-per-KB to be higher""" - tx0_outpoint = self.make_txout(1.1*COIN) - - tx1a = CTransaction([CTxIn(tx0_outpoint, nSequence=0)], - [CTxOut(1*COIN, CScript([b'a']))]) - tx1a_txid = self.proxy.sendrawtransaction(tx1a, True) - - # Higher fee, but the fee per KB is much lower, so the replacement is - # rejected. - tx1b = CTransaction([CTxIn(tx0_outpoint, nSequence=0)], - [CTxOut(0.001*COIN, - CScript([b'a'*999000]))]) - - try: - tx1b_txid = self.proxy.sendrawtransaction(tx1b, True) - except bitcoin.rpc.JSONRPCException as exp: - self.assertEqual(exp.error['code'], -26) # insufficient fee - else: - self.fail() - - def test_spends_of_conflicting_outputs(self): - """Replacements that spend conflicting tx outputs are rejected""" - utxo1 = self.make_txout(1.2*COIN) - utxo2 = self.make_txout(3.0*COIN) - - tx1a = CTransaction([CTxIn(utxo1, nSequence=0)], - [CTxOut(1.1*COIN, CScript([b'a']))]) - tx1a_txid = self.proxy.sendrawtransaction(tx1a, True) - - # Direct spend an output of the transaction we're replacing. - tx2 = CTransaction([CTxIn(utxo1, nSequence=0), CTxIn(utxo2, nSequence=0), - CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)], - tx1a.vout) - - try: - tx2_txid = self.proxy.sendrawtransaction(tx2, True) - except bitcoin.rpc.JSONRPCException as exp: - self.assertEqual(exp.error['code'], -26) - else: - self.fail() - - # Spend tx1a's output to test the indirect case. - tx1b = CTransaction([CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)], - [CTxOut(1.0*COIN, CScript([b'a']))]) - tx1b_txid = self.proxy.sendrawtransaction(tx1b, True) - - tx2 = CTransaction([CTxIn(utxo1, nSequence=0), CTxIn(utxo2, nSequence=0), - CTxIn(COutPoint(tx1b_txid, 0))], - tx1a.vout) - - try: - tx2_txid = self.proxy.sendrawtransaction(tx2, True) - except bitcoin.rpc.JSONRPCException as exp: - self.assertEqual(exp.error['code'], -26) - else: - self.fail() - - def test_new_unconfirmed_inputs(self): - """Replacements that add new unconfirmed inputs are rejected""" - confirmed_utxo = self.make_txout(1.1*COIN) - unconfirmed_utxo = self.make_txout(0.1*COIN, False) - - tx1 = CTransaction([CTxIn(confirmed_utxo)], - [CTxOut(1.0*COIN, CScript([b'a']))]) - tx1_txid = self.proxy.sendrawtransaction(tx1, True) - - tx2 = CTransaction([CTxIn(confirmed_utxo), CTxIn(unconfirmed_utxo)], - tx1.vout) - - try: - tx2_txid = self.proxy.sendrawtransaction(tx2, True) - except bitcoin.rpc.JSONRPCException as exp: - self.assertEqual(exp.error['code'], -26) - else: - self.fail() - - def test_too_many_replacements(self): - """Replacements that evict too many transactions are rejected""" - # Try directly replacing more than MAX_REPLACEMENT_LIMIT - # transactions - - # Start by creating a single transaction with many outputs - initial_nValue = 10*COIN - utxo = self.make_txout(initial_nValue) - fee = 0.0001*COIN - split_value = int((initial_nValue-fee)/(MAX_REPLACEMENT_LIMIT+1)) - actual_fee = initial_nValue - split_value*(MAX_REPLACEMENT_LIMIT+1) - - outputs = [] - for i in range(MAX_REPLACEMENT_LIMIT+1): - outputs.append(CTxOut(split_value, CScript([1]))) - - splitting_tx = CTransaction([CTxIn(utxo, nSequence=0)], outputs) - txid = self.proxy.sendrawtransaction(splitting_tx, True) - - # Now spend each of those outputs individually - for i in range(MAX_REPLACEMENT_LIMIT+1): - tx_i = CTransaction([CTxIn(COutPoint(txid, i), nSequence=0)], - [CTxOut(split_value-fee, CScript([b'a']))]) - self.proxy.sendrawtransaction(tx_i, True) - - # Now create doublespend of the whole lot, should fail - # Need a big enough fee to cover all spending transactions and have - # a higher fee rate - double_spend_value = (split_value-100*fee)*(MAX_REPLACEMENT_LIMIT+1) - inputs = [] - for i in range(MAX_REPLACEMENT_LIMIT+1): - inputs.append(CTxIn(COutPoint(txid, i), nSequence=0)) - double_tx = CTransaction(inputs, [CTxOut(double_spend_value, CScript([b'a']))]) - - try: - self.proxy.sendrawtransaction(double_tx, True) - except bitcoin.rpc.JSONRPCException as exp: - self.assertEqual(exp.error['code'], -26) - self.assertEqual("too many potential replacements" in exp.error['message'], True) - else: - self.fail() - - # If we remove an input, it should pass - double_tx = CTransaction(inputs[0:-1], - [CTxOut(double_spend_value, CScript([b'a']))]) - - self.proxy.sendrawtransaction(double_tx, True) - -if __name__ == '__main__': - unittest.main() diff --git a/qa/rpc-tests/README.md b/qa/rpc-tests/README.md index 898931936b..651b01f18a 100644 --- a/qa/rpc-tests/README.md +++ b/qa/rpc-tests/README.md @@ -47,10 +47,7 @@ implements the test logic. * ```NodeConn``` is the class used to connect to a bitcoind. If you implement a callback class that derives from ```NodeConnCB``` and pass that to the ```NodeConn``` object, your code will receive the appropriate callbacks when -events of interest arrive. NOTE: be sure to call -```self.create_callback_map()``` in your derived classes' ```__init__``` -function, so that the correct mappings are set up between p2p messages and your -callback functions. +events of interest arrive. * You can pass the same handler to multiple ```NodeConn```'s if you like, or pass different ones to each -- whatever makes the most sense for your test. diff --git a/qa/rpc-tests/bip65-cltv-p2p.py b/qa/rpc-tests/bip65-cltv-p2p.py index 9ca5c69f16..5bb41df1ad 100755 --- a/qa/rpc-tests/bip65-cltv-p2p.py +++ b/qa/rpc-tests/bip65-cltv-p2p.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_NOP2, OP_DROP +from test_framework.script import CScript, OP_1NEGATE, OP_CHECKLOCKTIMEVERIFY, OP_DROP from binascii import hexlify, unhexlify import cStringIO import time @@ -19,7 +19,7 @@ def cltv_invalidate(tx): Prepends -1 CLTV DROP in the scriptSig itself. ''' - tx.vin[0].scriptSig = CScript([OP_1NEGATE, OP_NOP2, OP_DROP] + + tx.vin[0].scriptSig = CScript([OP_1NEGATE, OP_CHECKLOCKTIMEVERIFY, OP_DROP] + list(CScript(tx.vin[0].scriptSig))) ''' diff --git a/qa/rpc-tests/bipdersig.py b/qa/rpc-tests/bipdersig.py index 243f816f65..5afc9ddde8 100755 --- a/qa/rpc-tests/bipdersig.py +++ b/qa/rpc-tests/bipdersig.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/qa/rpc-tests/blockchain.py b/qa/rpc-tests/blockchain.py index b7bfe36285..673f1cc545 100755 --- a/qa/rpc-tests/blockchain.py +++ b/qa/rpc-tests/blockchain.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/qa/rpc-tests/decodescript.py b/qa/rpc-tests/decodescript.py index 4bca623380..490808d49d 100755 --- a/qa/rpc-tests/decodescript.py +++ b/qa/rpc-tests/decodescript.py @@ -102,13 +102,13 @@ class DecodeScriptTest(BitcoinTestFramework): # OP_IF # <receiver-pubkey> OP_CHECKSIGVERIFY # OP_ELSE - # <lock-until> OP_NOP2 OP_DROP + # <lock-until> OP_CHECKLOCKTIMEVERIFY OP_DROP # OP_ENDIF # <sender-pubkey> OP_CHECKSIG # # lock until block 500,000 rpc_result = self.nodes[0].decodescript('63' + push_public_key + 'ad670320a107b17568' + push_public_key + 'ac') - assert_equal('OP_IF ' + public_key + ' OP_CHECKSIGVERIFY OP_ELSE 500000 OP_NOP2 OP_DROP OP_ENDIF ' + public_key + ' OP_CHECKSIG', rpc_result['asm']) + assert_equal('OP_IF ' + public_key + ' OP_CHECKSIGVERIFY OP_ELSE 500000 OP_CHECKLOCKTIMEVERIFY OP_DROP OP_ENDIF ' + public_key + ' OP_CHECKSIG', rpc_result['asm']) def decoderawtransaction_asm_sighashtype(self): """Tests decoding scripts via RPC command "decoderawtransaction". diff --git a/qa/rpc-tests/disablewallet.py b/qa/rpc-tests/disablewallet.py index 4cb01575e2..2112097af1 100755 --- a/qa/rpc-tests/disablewallet.py +++ b/qa/rpc-tests/disablewallet.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/qa/rpc-tests/forknotify.py b/qa/rpc-tests/forknotify.py index 0acef8e30b..2deede0c38 100755 --- a/qa/rpc-tests/forknotify.py +++ b/qa/rpc-tests/forknotify.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/qa/rpc-tests/fundrawtransaction.py b/qa/rpc-tests/fundrawtransaction.py index 93d13faa06..d6493dbb8a 100755 --- a/qa/rpc-tests/fundrawtransaction.py +++ b/qa/rpc-tests/fundrawtransaction.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/qa/rpc-tests/getblocktemplate_longpoll.py b/qa/rpc-tests/getblocktemplate_longpoll.py index 1ddff8a298..3e85957ae2 100755 --- a/qa/rpc-tests/getblocktemplate_longpoll.py +++ b/qa/rpc-tests/getblocktemplate_longpoll.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/qa/rpc-tests/getblocktemplate_proposals.py b/qa/rpc-tests/getblocktemplate_proposals.py index aca0cd7495..f83b5f140d 100755 --- a/qa/rpc-tests/getblocktemplate_proposals.py +++ b/qa/rpc-tests/getblocktemplate_proposals.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/qa/rpc-tests/getchaintips.py b/qa/rpc-tests/getchaintips.py index 6a2bcb2969..e8d2d8f3fd 100755 --- a/qa/rpc-tests/getchaintips.py +++ b/qa/rpc-tests/getchaintips.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/qa/rpc-tests/httpbasics.py b/qa/rpc-tests/httpbasics.py index 7888114c54..5b9fa00976 100755 --- a/qa/rpc-tests/httpbasics.py +++ b/qa/rpc-tests/httpbasics.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/qa/rpc-tests/invalidateblock.py b/qa/rpc-tests/invalidateblock.py index 2b9c8154e0..0e78a3c806 100755 --- a/qa/rpc-tests/invalidateblock.py +++ b/qa/rpc-tests/invalidateblock.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/qa/rpc-tests/invalidblockrequest.py b/qa/rpc-tests/invalidblockrequest.py index 6a7980cd45..a74ecb1288 100755 --- a/qa/rpc-tests/invalidblockrequest.py +++ b/qa/rpc-tests/invalidblockrequest.py @@ -6,7 +6,7 @@ from test_framework.test_framework import ComparisonTestFramework from test_framework.util import * -from test_framework.comptool import TestManager, TestInstance +from test_framework.comptool import TestManager, TestInstance, RejectResult from test_framework.mininode import * from test_framework.blocktools import * import logging @@ -97,7 +97,7 @@ class InvalidBlockRequestTest(ComparisonTestFramework): assert(block2_orig.vtx != block2.vtx) self.tip = block2.sha256 - yield TestInstance([[block2, False], [block2_orig, True]]) + yield TestInstance([[block2, RejectResult(16,'bad-txns-duplicate')], [block2_orig, True]]) height += 1 ''' @@ -112,7 +112,7 @@ class InvalidBlockRequestTest(ComparisonTestFramework): block3.rehash() block3.solve() - yield TestInstance([[block3, False]]) + yield TestInstance([[block3, RejectResult(16,'bad-cb-amount')]]) if __name__ == '__main__': diff --git a/qa/rpc-tests/invalidtxrequest.py b/qa/rpc-tests/invalidtxrequest.py new file mode 100755 index 0000000000..d17b3d0980 --- /dev/null +++ b/qa/rpc-tests/invalidtxrequest.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python2 +# +# Distributed under the MIT/X11 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.comptool import TestManager, TestInstance, RejectResult +from test_framework.mininode import * +from test_framework.blocktools import * +import logging +import copy +import time + + +''' +In this test we connect to one node over p2p, and test tx requests. +''' + +# Use the ComparisonTestFramework with 1 node: only use --testbinary. +class InvalidTxRequestTest(ComparisonTestFramework): + + ''' Can either run this test as 1 node with expected answers, or two and compare them. + Change the "outcome" variable from each TestInstance object to only do the comparison. ''' + def __init__(self): + self.num_nodes = 1 + + def run_test(self): + test = TestManager(self, self.options.tmpdir) + test.add_all_connections(self.nodes) + self.tip = None + self.block_time = None + NetworkThread().start() # Start up network handling in another thread + test.run() + + def get_tests(self): + if self.tip is None: + self.tip = int ("0x" + self.nodes[0].getbestblockhash() + "L", 0) + self.block_time = int(time.time())+1 + + ''' + Create a new block with an anyone-can-spend coinbase + ''' + height = 1 + block = create_block(self.tip, create_coinbase(height), self.block_time) + self.block_time += 1 + block.solve() + # Save the coinbase for later + self.block1 = block + self.tip = block.sha256 + height += 1 + yield TestInstance([[block, True]]) + + ''' + Now we need that block to mature so we can spend the coinbase. + ''' + test = TestInstance(sync_every_block=False) + for i in xrange(100): + block = create_block(self.tip, create_coinbase(height), self.block_time) + block.solve() + self.tip = block.sha256 + self.block_time += 1 + test.blocks_and_transactions.append([block, True]) + height += 1 + yield test + + # chr(100) is OP_NOTIF + # Transaction will be rejected with code 16 (REJECT_INVALID) + tx1 = create_transaction(self.block1.vtx[0], 0, chr(100), 50*100000000) + yield TestInstance([[tx1, RejectResult(16, 'mandatory-script-verify-flag-failed')]]) + + # TODO: test further transactions... + +if __name__ == '__main__': + InvalidTxRequestTest().main() diff --git a/qa/rpc-tests/keypool.py b/qa/rpc-tests/keypool.py index 5a67220021..c300bbc57e 100755 --- a/qa/rpc-tests/keypool.py +++ b/qa/rpc-tests/keypool.py @@ -1,20 +1,13 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. # Exercise the wallet keypool, and interaction with wallet encryption/locking # Add python-bitcoinrpc to module search path: -import os -import sys - -import json -import shutil -import subprocess -import tempfile -import traceback +from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * @@ -39,107 +32,65 @@ def check_array_result(object_array, to_match, expected): if num_matched == 0: raise AssertionError("No objects matched %s"%(str(to_match))) -def run_test(nodes, tmpdir): - # Encrypt wallet and wait to terminate - nodes[0].encryptwallet('test') - bitcoind_processes[0].wait() - # Restart node 0 - nodes[0] = start_node(0, tmpdir) - # Keep creating keys - addr = nodes[0].getnewaddress() - try: - addr = nodes[0].getnewaddress() - raise AssertionError('Keypool should be exhausted after one address') - except JSONRPCException,e: - assert(e.error['code']==-12) - - # put three new keys in the keypool - nodes[0].walletpassphrase('test', 12000) - nodes[0].keypoolrefill(3) - nodes[0].walletlock() - - # drain the keys - addr = set() - addr.add(nodes[0].getrawchangeaddress()) - addr.add(nodes[0].getrawchangeaddress()) - addr.add(nodes[0].getrawchangeaddress()) - addr.add(nodes[0].getrawchangeaddress()) - # assert that four unique addresses were returned - assert(len(addr) == 4) - # the next one should fail - try: - addr = nodes[0].getrawchangeaddress() - raise AssertionError('Keypool should be exhausted after three addresses') - except JSONRPCException,e: - assert(e.error['code']==-12) - - # refill keypool with three new addresses - nodes[0].walletpassphrase('test', 12000) - nodes[0].keypoolrefill(3) - nodes[0].walletlock() +class KeyPoolTest(BitcoinTestFramework): - # drain them by mining - nodes[0].generate(1) - nodes[0].generate(1) - nodes[0].generate(1) - nodes[0].generate(1) - try: + def run_test(self): + nodes = self.nodes + # Encrypt wallet and wait to terminate + nodes[0].encryptwallet('test') + bitcoind_processes[0].wait() + # Restart node 0 + nodes[0] = start_node(0, self.options.tmpdir) + # Keep creating keys + addr = nodes[0].getnewaddress() + try: + addr = nodes[0].getnewaddress() + raise AssertionError('Keypool should be exhausted after one address') + except JSONRPCException,e: + assert(e.error['code']==-12) + + # put three new keys in the keypool + nodes[0].walletpassphrase('test', 12000) + nodes[0].keypoolrefill(3) + nodes[0].walletlock() + + # drain the keys + addr = set() + addr.add(nodes[0].getrawchangeaddress()) + addr.add(nodes[0].getrawchangeaddress()) + addr.add(nodes[0].getrawchangeaddress()) + addr.add(nodes[0].getrawchangeaddress()) + # assert that four unique addresses were returned + assert(len(addr) == 4) + # the next one should fail + try: + addr = nodes[0].getrawchangeaddress() + raise AssertionError('Keypool should be exhausted after three addresses') + except JSONRPCException,e: + assert(e.error['code']==-12) + + # refill keypool with three new addresses + nodes[0].walletpassphrase('test', 12000) + nodes[0].keypoolrefill(3) + nodes[0].walletlock() + + # drain them by mining nodes[0].generate(1) - raise AssertionError('Keypool should be exhausted after three addesses') - except JSONRPCException,e: - assert(e.error['code']==-12) - -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) - - nodes = start_nodes(1, options.tmpdir) - - run_test(nodes, options.tmpdir) - - success = True - - except AssertionError as e: - print("Assertion failed: "+e.message) - except JSONRPCException as e: - print("JSONRPC error: "+e.error['message']) - traceback.print_tb(sys.exc_info()[2]) - except Exception as e: - print("Unexpected exception caught during testing: "+str(sys.exc_info()[0])) - traceback.print_tb(sys.exc_info()[2]) + nodes[0].generate(1) + nodes[0].generate(1) + nodes[0].generate(1) + try: + nodes[0].generate(1) + raise AssertionError('Keypool should be exhausted after three addesses') + except JSONRPCException,e: + assert(e.error['code']==-12) - if not options.nocleanup: - print("Cleaning up") - stop_nodes(nodes) - wait_bitcoinds() - shutil.rmtree(options.tmpdir) + def setup_chain(self): + print("Initializing test directory "+self.options.tmpdir) + initialize_chain(self.options.tmpdir) - if success: - print("Tests successful") - sys.exit(0) - else: - print("Failed") - sys.exit(1) + def setup_network(self): + self.nodes = start_nodes(1, self.options.tmpdir) if __name__ == '__main__': - main() + KeyPoolTest().main() diff --git a/qa/rpc-tests/listtransactions.py b/qa/rpc-tests/listtransactions.py index b30a6bc9d1..8a1e3dc4bc 100755 --- a/qa/rpc-tests/listtransactions.py +++ b/qa/rpc-tests/listtransactions.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/qa/rpc-tests/maxblocksinflight.py b/qa/rpc-tests/maxblocksinflight.py index a601147ce8..1a9ae480ab 100755 --- a/qa/rpc-tests/maxblocksinflight.py +++ b/qa/rpc-tests/maxblocksinflight.py @@ -34,7 +34,6 @@ class TestManager(NodeConnCB): def __init__(self): NodeConnCB.__init__(self) self.log = logging.getLogger("BlockRelayTest") - self.create_callback_map() def add_new_connection(self, connection): self.connection = connection diff --git a/qa/rpc-tests/maxuploadtarget.py b/qa/rpc-tests/maxuploadtarget.py index e714465db1..4d6b343f77 100755 --- a/qa/rpc-tests/maxuploadtarget.py +++ b/qa/rpc-tests/maxuploadtarget.py @@ -25,7 +25,6 @@ if uploadtarget has been reached. class TestNode(NodeConnCB): def __init__(self): NodeConnCB.__init__(self) - self.create_callback_map() self.connection = None self.ping_counter = 1 self.last_pong = msg_pong() @@ -85,22 +84,7 @@ class TestNode(NodeConnCB): class MaxUploadTest(BitcoinTestFramework): def __init__(self): self.utxo = [] - - # Some pre-processing to create a bunch of OP_RETURN txouts to insert into transactions we create - # So we have big transactions and full blocks to fill up our block files - # create one script_pubkey - script_pubkey = "6a4d0200" #OP_RETURN OP_PUSH2 512 bytes - for i in xrange (512): - script_pubkey = script_pubkey + "01" - # concatenate 128 txouts of above script_pubkey which we'll insert before the txout for change - self.txouts = "81" - for k in xrange(128): - # add txout value - self.txouts = self.txouts + "0000000000000000" - # add length of script_pubkey - self.txouts = self.txouts + "fd0402" - # add script_pubkey - self.txouts = self.txouts + script_pubkey + self.txouts = gen_return_txouts() def add_options(self, parser): parser.add_option("--testbinary", dest="testbinary", diff --git a/qa/rpc-tests/mempool_limit.py b/qa/rpc-tests/mempool_limit.py new file mode 100755 index 0000000000..3ba17ac4f1 --- /dev/null +++ b/qa/rpc-tests/mempool_limit.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python2 +# Copyright (c) 2014-2015 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# Test mempool limiting together/eviction with the wallet + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * + +class MempoolLimitTest(BitcoinTestFramework): + + def __init__(self): + self.txouts = gen_return_txouts() + + def setup_network(self): + self.nodes = [] + self.nodes.append(start_node(0, self.options.tmpdir, ["-maxmempool=5", "-spendzeroconfchange=0", "-debug"])) + self.is_network_split = False + self.sync_all() + self.relayfee = self.nodes[0].getnetworkinfo()['relayfee'] + + def setup_chain(self): + print("Initializing test directory "+self.options.tmpdir) + initialize_chain_clean(self.options.tmpdir, 2) + + def run_test(self): + txids = [] + utxos = create_confirmed_utxos(self.relayfee, self.nodes[0], 90) + + #create a mempool tx that will be evicted + us0 = utxos.pop() + inputs = [{ "txid" : us0["txid"], "vout" : us0["vout"]}] + outputs = {self.nodes[0].getnewaddress() : 0.0001} + tx = self.nodes[0].createrawtransaction(inputs, outputs) + txF = self.nodes[0].fundrawtransaction(tx) + txFS = self.nodes[0].signrawtransaction(txF['hex']) + txid = self.nodes[0].sendrawtransaction(txFS['hex']) + self.nodes[0].lockunspent(True, [us0]) + + relayfee = self.nodes[0].getnetworkinfo()['relayfee'] + base_fee = relayfee*100 + for i in xrange (4): + txids.append([]) + txids[i] = create_lots_of_big_transactions(self.nodes[0], self.txouts, utxos[30*i:30*i+30], (i+1)*base_fee) + + # by now, the tx should be evicted, check confirmation state + assert(txid not in self.nodes[0].getrawmempool()) + txdata = self.nodes[0].gettransaction(txid); + assert(txdata['confirmations'] == 0) #confirmation should still be 0 + +if __name__ == '__main__': + MempoolLimitTest().main() diff --git a/qa/rpc-tests/mempool_packages.py b/qa/rpc-tests/mempool_packages.py index 746c26ff5e..063308d394 100755 --- a/qa/rpc-tests/mempool_packages.py +++ b/qa/rpc-tests/mempool_packages.py @@ -8,9 +8,6 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * -def satoshi_round(amount): - return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN) - MAX_ANCESTORS = 25 MAX_DESCENDANTS = 25 @@ -67,17 +64,41 @@ class MempoolPackagesTest(BitcoinTestFramework): for x in reversed(chain): assert_equal(mempool[x]['descendantcount'], descendant_count) descendant_fees += mempool[x]['fee'] + assert_equal(mempool[x]['modifiedfee'], mempool[x]['fee']) assert_equal(mempool[x]['descendantfees'], SATOSHIS*descendant_fees) descendant_size += mempool[x]['size'] assert_equal(mempool[x]['descendantsize'], descendant_size) descendant_count += 1 + # Check that descendant modified fees includes fee deltas from + # prioritisetransaction + self.nodes[0].prioritisetransaction(chain[-1], 0, 1000) + mempool = self.nodes[0].getrawmempool(True) + + descendant_fees = 0 + for x in reversed(chain): + descendant_fees += mempool[x]['fee'] + assert_equal(mempool[x]['descendantfees'], SATOSHIS*descendant_fees+1000) + # Adding one more transaction on to the chain should fail. try: self.chain_transaction(self.nodes[0], txid, vout, value, fee, 1) except JSONRPCException as e: print "too-long-ancestor-chain successfully rejected" + # Check that prioritising a tx before it's added to the mempool works + self.nodes[0].generate(1) + self.nodes[0].prioritisetransaction(chain[-1], 0, 2000) + self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) + mempool = self.nodes[0].getrawmempool(True) + + descendant_fees = 0 + for x in reversed(chain): + descendant_fees += mempool[x]['fee'] + if (x == chain[-1]): + assert_equal(mempool[x]['modifiedfee'], mempool[x]['fee']+satoshi_round(0.00002)) + assert_equal(mempool[x]['descendantfees'], SATOSHIS*descendant_fees+2000) + # TODO: check that node1's mempool is as expected # TODO: test ancestor size limits diff --git a/qa/rpc-tests/mempool_coinbase_spends.py b/qa/rpc-tests/mempool_reorg.py index c64a15b9f5..d96a3f8266 100755 --- a/qa/rpc-tests/mempool_coinbase_spends.py +++ b/qa/rpc-tests/mempool_reorg.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -52,16 +52,25 @@ class MempoolCoinbaseTest(BitcoinTestFramework): # 3. Indirect (coinbase and child both in chain) : spend_103 and spend_103_1 # Use invalidatblock to make all of the above coinbase spends invalid (immature coinbase), # and make sure the mempool code behaves correctly. - b = [ self.nodes[0].getblockhash(n) for n in range(102, 105) ] + b = [ self.nodes[0].getblockhash(n) for n in range(101, 105) ] coinbase_txids = [ self.nodes[0].getblock(h)['tx'][0] for h in b ] - spend_101_raw = self.create_tx(coinbase_txids[0], node1_address, 50) - spend_102_raw = self.create_tx(coinbase_txids[1], node0_address, 50) - spend_103_raw = self.create_tx(coinbase_txids[2], node0_address, 50) + spend_101_raw = self.create_tx(coinbase_txids[1], node1_address, 50) + spend_102_raw = self.create_tx(coinbase_txids[2], node0_address, 50) + spend_103_raw = self.create_tx(coinbase_txids[3], node0_address, 50) + + # Create a block-height-locked transaction which will be invalid after reorg + timelock_tx = self.nodes[0].createrawtransaction([{"txid": coinbase_txids[0], "vout": 0}], {node0_address: 50}) + # Set the time lock + timelock_tx = timelock_tx.replace("ffffffff", "11111111", 1) + timelock_tx = timelock_tx[:-8] + hex(self.nodes[0].getblockcount() + 2)[2:] + "000000" + timelock_tx = self.nodes[0].signrawtransaction(timelock_tx)["hex"] + assert_raises(JSONRPCException, self.nodes[0].sendrawtransaction, timelock_tx) # Broadcast and mine spend_102 and 103: spend_102_id = self.nodes[0].sendrawtransaction(spend_102_raw) spend_103_id = self.nodes[0].sendrawtransaction(spend_103_raw) self.nodes[0].generate(1) + assert_raises(JSONRPCException, self.nodes[0].sendrawtransaction, timelock_tx) # Create 102_1 and 103_1: spend_102_1_raw = self.create_tx(spend_102_id, node1_address, 50) @@ -69,7 +78,8 @@ class MempoolCoinbaseTest(BitcoinTestFramework): # Broadcast and mine 103_1: spend_103_1_id = self.nodes[0].sendrawtransaction(spend_103_1_raw) - self.nodes[0].generate(1) + last_block = self.nodes[0].generate(1) + timelock_tx_id = self.nodes[0].sendrawtransaction(timelock_tx) # ... now put spend_101 and spend_102_1 in memory pools: spend_101_id = self.nodes[0].sendrawtransaction(spend_101_raw) @@ -77,7 +87,11 @@ class MempoolCoinbaseTest(BitcoinTestFramework): self.sync_all() - assert_equal(set(self.nodes[0].getrawmempool()), set([ spend_101_id, spend_102_1_id ])) + assert_equal(set(self.nodes[0].getrawmempool()), set([ spend_101_id, spend_102_1_id, timelock_tx_id ])) + + for node in self.nodes: + node.invalidateblock(last_block[0]) + assert_equal(set(self.nodes[0].getrawmempool()), set([ spend_101_id, spend_102_1_id, spend_103_1_id ])) # Use invalidateblock to re-org back and make all those coinbase spends # immature/invalid: diff --git a/qa/rpc-tests/mempool_resurrect_test.py b/qa/rpc-tests/mempool_resurrect_test.py index 19c74bb751..750953ee5e 100755 --- a/qa/rpc-tests/mempool_resurrect_test.py +++ b/qa/rpc-tests/mempool_resurrect_test.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/qa/rpc-tests/mempool_spendcoinbase.py b/qa/rpc-tests/mempool_spendcoinbase.py index fc17c50692..35ce76e244 100755 --- a/qa/rpc-tests/mempool_spendcoinbase.py +++ b/qa/rpc-tests/mempool_spendcoinbase.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/qa/rpc-tests/merkle_blocks.py b/qa/rpc-tests/merkle_blocks.py index 72a80ce6ca..08e5db45fa 100755 --- a/qa/rpc-tests/merkle_blocks.py +++ b/qa/rpc-tests/merkle_blocks.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/qa/rpc-tests/multi_rpc.py b/qa/rpc-tests/multi_rpc.py new file mode 100755 index 0000000000..62071d426e --- /dev/null +++ b/qa/rpc-tests/multi_rpc.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python2 +# Copyright (c) 2015 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# +# Test mulitple rpc user config option rpcauth +# + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * +import base64 + +try: + import http.client as httplib +except ImportError: + import httplib +try: + import urllib.parse as urlparse +except ImportError: + import urlparse + +class HTTPBasicsTest (BitcoinTestFramework): + def setup_nodes(self): + return start_nodes(4, self.options.tmpdir) + + def setup_chain(self): + print("Initializing test directory "+self.options.tmpdir) + initialize_chain(self.options.tmpdir) + #Append rpcauth to bitcoin.conf before initialization + rpcauth = "rpcauth=rt:93648e835a54c573682c2eb19f882535$7681e9c5b74bdd85e78166031d2058e1069b3ed7ed967c93fc63abba06f31144" + rpcauth2 = "rpcauth=rt2:f8607b1a88861fac29dfccf9b52ff9f$ff36a0c23c8c62b4846112e50fa888416e94c17bfd4c42f88fd8f55ec6a3137e" + with open(os.path.join(self.options.tmpdir+"/node0", "bitcoin.conf"), 'a') as f: + f.write(rpcauth+"\n") + f.write(rpcauth2+"\n") + + def run_test(self): + + ################################################## + # Check correctness of the rpcauth config option # + ################################################## + url = urlparse.urlparse(self.nodes[0].url) + + #Old authpair + authpair = url.username + ':' + url.password + + #New authpair generated via contrib/rpcuser tool + rpcauth = "rpcauth=rt:93648e835a54c573682c2eb19f882535$7681e9c5b74bdd85e78166031d2058e1069b3ed7ed967c93fc63abba06f31144" + password = "cA773lm788buwYe4g4WT+05pKyNruVKjQ25x3n0DQcM=" + + #Second authpair with different username + rpcauth2 = "rpcauth=rt2:f8607b1a88861fac29dfccf9b52ff9f$ff36a0c23c8c62b4846112e50fa888416e94c17bfd4c42f88fd8f55ec6a3137e" + password2 = "8/F3uMDw4KSEbw96U3CA1C4X05dkHDN2BPFjTgZW4KI=" + authpairnew = "rt:"+password + + headers = {"Authorization": "Basic " + base64.b64encode(authpair)} + + conn = httplib.HTTPConnection(url.hostname, url.port) + conn.connect() + conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) + resp = conn.getresponse() + assert_equal(resp.status==401, False) + conn.close() + + #Use new authpair to confirm both work + headers = {"Authorization": "Basic " + base64.b64encode(authpairnew)} + + conn = httplib.HTTPConnection(url.hostname, url.port) + conn.connect() + conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) + resp = conn.getresponse() + assert_equal(resp.status==401, False) + conn.close() + + #Wrong login name with rt's password + authpairnew = "rtwrong:"+password + headers = {"Authorization": "Basic " + base64.b64encode(authpairnew)} + + conn = httplib.HTTPConnection(url.hostname, url.port) + conn.connect() + conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) + resp = conn.getresponse() + assert_equal(resp.status==401, True) + conn.close() + + #Wrong password for rt + authpairnew = "rt:"+password+"wrong" + headers = {"Authorization": "Basic " + base64.b64encode(authpairnew)} + + conn = httplib.HTTPConnection(url.hostname, url.port) + conn.connect() + conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) + resp = conn.getresponse() + assert_equal(resp.status==401, True) + conn.close() + + #Correct for rt2 + authpairnew = "rt2:"+password2 + headers = {"Authorization": "Basic " + base64.b64encode(authpairnew)} + + conn = httplib.HTTPConnection(url.hostname, url.port) + conn.connect() + conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) + resp = conn.getresponse() + assert_equal(resp.status==401, False) + conn.close() + + #Wrong password for rt2 + authpairnew = "rt2:"+password2+"wrong" + headers = {"Authorization": "Basic " + base64.b64encode(authpairnew)} + + conn = httplib.HTTPConnection(url.hostname, url.port) + conn.connect() + conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) + resp = conn.getresponse() + assert_equal(resp.status==401, True) + conn.close() + + + +if __name__ == '__main__': + HTTPBasicsTest ().main () diff --git a/qa/rpc-tests/nodehandling.py b/qa/rpc-tests/nodehandling.py index e383a3a12c..3239dd0339 100755 --- a/qa/rpc-tests/nodehandling.py +++ b/qa/rpc-tests/nodehandling.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/qa/rpc-tests/p2p-acceptblock.py b/qa/rpc-tests/p2p-acceptblock.py index 700deab207..23872d8494 100755 --- a/qa/rpc-tests/p2p-acceptblock.py +++ b/qa/rpc-tests/p2p-acceptblock.py @@ -62,7 +62,6 @@ The test: class TestNode(NodeConnCB): def __init__(self): NodeConnCB.__init__(self) - self.create_callback_map() self.connection = None self.ping_counter = 1 self.last_pong = msg_pong() diff --git a/qa/rpc-tests/p2p-fullblocktest.py b/qa/rpc-tests/p2p-fullblocktest.py index 9555940cec..a6525e6793 100755 --- a/qa/rpc-tests/p2p-fullblocktest.py +++ b/qa/rpc-tests/p2p-fullblocktest.py @@ -7,7 +7,7 @@ from test_framework.test_framework import ComparisonTestFramework from test_framework.util import * -from test_framework.comptool import TestManager, TestInstance +from test_framework.comptool import TestManager, TestInstance, RejectResult from test_framework.mininode import * from test_framework.blocktools import * import logging @@ -15,7 +15,7 @@ import copy import time import numbers from test_framework.key import CECKey -from test_framework.script import CScript, CScriptOp, SignatureHash, SIGHASH_ALL, OP_TRUE +from test_framework.script import CScript, CScriptOp, SignatureHash, SIGHASH_ALL, OP_TRUE, OP_FALSE class PreviousSpendableOutput(object): def __init__(self, tx = CTransaction(), n = -1): @@ -122,13 +122,29 @@ class FullBlockTest(ComparisonTestFramework): return TestInstance([[self.tip, True]]) # returns a test case that asserts that the current tip was rejected - def rejected(): - return TestInstance([[self.tip, False]]) + def rejected(reject = None): + if reject is None: + return TestInstance([[self.tip, False]]) + else: + return TestInstance([[self.tip, reject]]) # move the tip back to a previous block def tip(number): self.tip = self.blocks[number] + # add transactions to a block produced by next_block + def update_block(block_number, new_transactions): + block = self.blocks[block_number] + old_hash = block.sha256 + self.add_transactions_to_block(block, new_transactions) + block.solve() + # Update the internal state just like in next_block + self.tip = block + self.block_heights[block.sha256] = self.block_heights[old_hash] + del self.block_heights[old_hash] + self.blocks[block_number] = block + return block + # creates a new block and advances the tip to that block block = self.next_block @@ -141,14 +157,15 @@ class FullBlockTest(ComparisonTestFramework): # Now we need that block to mature so we can spend the coinbase. test = TestInstance(sync_every_block=False) - for i in range(100): + for i in range(99): block(1000 + i) test.blocks_and_transactions.append([self.tip, True]) save_spendable_output() yield test - # Start by bulding a couple of blocks on top (which output is spent is in parentheses): + # Start by building a couple of blocks on top (which output is spent is + # in parentheses): # genesis -> b1 (0) -> b2 (1) out0 = get_spendable_output() block(1, spend=out0) @@ -156,8 +173,7 @@ class FullBlockTest(ComparisonTestFramework): yield accepted() out1 = get_spendable_output() - block(2, spend=out1) - # Inv again, then deliver twice (shouldn't break anything). + b2 = block(2, spend=out1) yield accepted() @@ -168,8 +184,8 @@ class FullBlockTest(ComparisonTestFramework): # # Nothing should happen at this point. We saw b2 first so it takes priority. tip(1) - block(3, spend=out1) - # Deliver twice (should still not break anything) + b3 = block(3, spend=out1) + txout_b3 = PreviousSpendableOutput(b3.vtx[1], 1) yield rejected() @@ -214,7 +230,7 @@ class FullBlockTest(ComparisonTestFramework): # \-> b3 (1) -> b4 (2) tip(6) block(9, spend=out4, additional_coinbase_value=1) - yield rejected() + yield rejected(RejectResult(16, 'bad-cb-amount')) # Create a fork that ends in a block with too much fee (the one that causes the reorg) @@ -226,7 +242,7 @@ class FullBlockTest(ComparisonTestFramework): yield rejected() block(11, spend=out4, additional_coinbase_value=1) - yield rejected() + yield rejected(RejectResult(16, 'bad-cb-amount')) # Try again, but with a valid fork first @@ -252,6 +268,10 @@ class FullBlockTest(ComparisonTestFramework): yield TestInstance([[b12, True, b13.sha256]]) # New tip should be b13. + # Add a block with MAX_BLOCK_SIGOPS and one with one more sigop + # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) + # \-> b12 (3) -> b13 (4) -> b15 (5) -> b16 (6) + # \-> b3 (1) -> b4 (2) # Test that a block with a lot of checksigs is okay lots_of_checksigs = CScript([OP_CHECKSIG] * (1000000 / 50 - 1)) @@ -264,8 +284,121 @@ class FullBlockTest(ComparisonTestFramework): out6 = get_spendable_output() too_many_checksigs = CScript([OP_CHECKSIG] * (1000000 / 50)) block(16, spend=out6, script=too_many_checksigs) + yield rejected(RejectResult(16, 'bad-blk-sigops')) + + + # Attempt to spend a transaction created on a different fork + # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) + # \-> b12 (3) -> b13 (4) -> b15 (5) -> b17 (b3.vtx[1]) + # \-> b3 (1) -> b4 (2) + tip(15) + block(17, spend=txout_b3) + yield rejected(RejectResult(16, 'bad-txns-inputs-missingorspent')) + + # Attempt to spend a transaction created on a different fork (on a fork this time) + # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) + # \-> b12 (3) -> b13 (4) -> b15 (5) + # \-> b18 (b3.vtx[1]) -> b19 (6) + # \-> b3 (1) -> b4 (2) + tip(13) + block(18, spend=txout_b3) + yield rejected() + + block(19, spend=out6) yield rejected() + # Attempt to spend a coinbase at depth too low + # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) + # \-> b12 (3) -> b13 (4) -> b15 (5) -> b20 (7) + # \-> b3 (1) -> b4 (2) + tip(15) + out7 = get_spendable_output() + block(20, spend=out7) + yield rejected(RejectResult(16, 'bad-txns-premature-spend-of-coinbase')) + + # Attempt to spend a coinbase at depth too low (on a fork this time) + # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) + # \-> b12 (3) -> b13 (4) -> b15 (5) + # \-> b21 (6) -> b22 (5) + # \-> b3 (1) -> b4 (2) + tip(13) + block(21, spend=out6) + yield rejected() + + block(22, spend=out5) + yield rejected() + + # Create a block on either side of MAX_BLOCK_SIZE and make sure its accepted/rejected + # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) + # \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6) + # \-> b24 (6) -> b25 (7) + # \-> b3 (1) -> b4 (2) + tip(15) + b23 = block(23, spend=out6) + old_hash = b23.sha256 + tx = CTransaction() + script_length = MAX_BLOCK_SIZE - len(b23.serialize()) - 69 + script_output = CScript([chr(0)*script_length]) + tx.vout.append(CTxOut(0, script_output)) + tx.vin.append(CTxIn(COutPoint(b23.vtx[1].sha256, 1))) + b23 = update_block(23, [tx]) + # Make sure the math above worked out to produce a max-sized block + assert_equal(len(b23.serialize()), MAX_BLOCK_SIZE) + yield accepted() + + # Make the next block one byte bigger and check that it fails + tip(15) + b24 = block(24, spend=out6) + script_length = MAX_BLOCK_SIZE - len(b24.serialize()) - 69 + script_output = CScript([chr(0)*(script_length+1)]) + tx.vout = [CTxOut(0, script_output)] + b24 = update_block(24, [tx]) + assert_equal(len(b24.serialize()), MAX_BLOCK_SIZE+1) + yield rejected(RejectResult(16, 'bad-blk-length')) + + b25 = block(25, spend=out7) + yield rejected() + + # Create blocks with a coinbase input script size out of range + # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) + # \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6) -> b30 (7) + # \-> ... (6) -> ... (7) + # \-> b3 (1) -> b4 (2) + tip(15) + b26 = block(26, spend=out6) + b26.vtx[0].vin[0].scriptSig = chr(0) + b26.vtx[0].rehash() + # update_block causes the merkle root to get updated, even with no new + # transactions, and updates the required state. + b26 = update_block(26, []) + yield rejected(RejectResult(16, 'bad-cb-length')) + + # Extend the b26 chain to make sure bitcoind isn't accepting b26 + b27 = block(27, spend=out7) + yield rejected() + + # Now try a too-large-coinbase script + tip(15) + b28 = block(28, spend=out6) + b28.vtx[0].vin[0].scriptSig = chr(0)*101 + b28.vtx[0].rehash() + b28 = update_block(28, []) + yield rejected(RejectResult(16, 'bad-cb-length')) + + # Extend the b28 chain to make sure bitcoind isn't accepted b28 + b29 = block(29, spend=out7) + # TODO: Should get a reject message back with "bad-prevblk", except + # there's a bug that prevents this from being detected. Just note + # failure for now, and add the reject result later. + yield rejected() + + # b30 has a max-sized coinbase scriptSig. + tip(23) + b30 = block(30) + b30.vtx[0].vin[0].scriptSig = chr(0)*100 + b30.vtx[0].rehash() + b30 = update_block(30, []) + yield accepted() if __name__ == '__main__': diff --git a/qa/rpc-tests/prioritise_transaction.py b/qa/rpc-tests/prioritise_transaction.py new file mode 100755 index 0000000000..4a79d38da0 --- /dev/null +++ b/qa/rpc-tests/prioritise_transaction.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python2 +# Copyright (c) 2015 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# +# Test PrioritiseTransaction code +# + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * + +COIN = 100000000 + +class PrioritiseTransactionTest(BitcoinTestFramework): + + def __init__(self): + self.txouts = gen_return_txouts() + + def setup_chain(self): + print("Initializing test directory "+self.options.tmpdir) + initialize_chain_clean(self.options.tmpdir, 1) + + def setup_network(self): + self.nodes = [] + self.is_network_split = False + + self.nodes.append(start_node(0, self.options.tmpdir, ["-debug", "-printpriority=1"])) + self.relayfee = self.nodes[0].getnetworkinfo()['relayfee'] + + def run_test(self): + utxos = create_confirmed_utxos(self.relayfee, self.nodes[0], 90) + base_fee = self.relayfee*100 # our transactions are smaller than 100kb + txids = [] + + # Create 3 batches of transactions at 3 different fee rate levels + for i in xrange(3): + txids.append([]) + txids[i] = create_lots_of_big_transactions(self.nodes[0], self.txouts, utxos[30*i:30*i+30], (i+1)*base_fee) + + # add a fee delta to something in the cheapest bucket and make sure it gets mined + # also check that a different entry in the cheapest bucket is NOT mined (lower + # the priority to ensure its not mined due to priority) + self.nodes[0].prioritisetransaction(txids[0][0], 0, int(3*base_fee*COIN)) + self.nodes[0].prioritisetransaction(txids[0][1], -1e15, 0) + + self.nodes[0].generate(1) + + mempool = self.nodes[0].getrawmempool() + print "Assert that prioritised transasction was mined" + assert(txids[0][0] not in mempool) + assert(txids[0][1] in mempool) + + high_fee_tx = None + for x in txids[2]: + if x not in mempool: + high_fee_tx = x + + # Something high-fee should have been mined! + assert(high_fee_tx != None) + + # Add a prioritisation before a tx is in the mempool (de-prioritising a + # high-fee transaction). + self.nodes[0].prioritisetransaction(high_fee_tx, -1e15, -int(2*base_fee*COIN)) + + # Add everything back to mempool + self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) + + # Check to make sure our high fee rate tx is back in the mempool + mempool = self.nodes[0].getrawmempool() + assert(high_fee_tx in mempool) + + # Now verify the high feerate transaction isn't mined. + self.nodes[0].generate(5) + + # High fee transaction should not have been mined, but other high fee rate + # transactions should have been. + mempool = self.nodes[0].getrawmempool() + print "Assert that de-prioritised transaction is still in mempool" + assert(high_fee_tx in mempool) + for x in txids[2]: + if (x != high_fee_tx): + assert(x not in mempool) + + # Create a free, low priority transaction. Should be rejected. + utxo_list = self.nodes[0].listunspent() + assert(len(utxo_list) > 0) + utxo = utxo_list[0] + + inputs = [] + outputs = {} + inputs.append({"txid" : utxo["txid"], "vout" : utxo["vout"]}) + outputs[self.nodes[0].getnewaddress()] = utxo["amount"] - self.relayfee + raw_tx = self.nodes[0].createrawtransaction(inputs, outputs) + tx_hex = self.nodes[0].signrawtransaction(raw_tx)["hex"] + txid = self.nodes[0].sendrawtransaction(tx_hex) + + # A tx that spends an in-mempool tx has 0 priority, so we can use it to + # test the effect of using prioritise transaction for mempool acceptance + inputs = [] + inputs.append({"txid": txid, "vout": 0}) + outputs = {} + outputs[self.nodes[0].getnewaddress()] = utxo["amount"] - self.relayfee + raw_tx2 = self.nodes[0].createrawtransaction(inputs, outputs) + tx2_hex = self.nodes[0].signrawtransaction(raw_tx2)["hex"] + tx2_id = self.nodes[0].decoderawtransaction(tx2_hex)["txid"] + + try: + self.nodes[0].sendrawtransaction(tx2_hex) + except JSONRPCException as exp: + assert_equal(exp.error['code'], -26) # insufficient fee + assert(tx2_id not in self.nodes[0].getrawmempool()) + else: + assert(False) + + # This is a less than 1000-byte transaction, so just set the fee + # to be the minimum for a 1000 byte transaction and check that it is + # accepted. + self.nodes[0].prioritisetransaction(tx2_id, 0, int(self.relayfee*COIN)) + + print "Assert that prioritised free transaction is accepted to mempool" + assert_equal(self.nodes[0].sendrawtransaction(tx2_hex), tx2_id) + assert(tx2_id in self.nodes[0].getrawmempool()) + +if __name__ == '__main__': + PrioritiseTransactionTest().main() diff --git a/qa/rpc-tests/pruning.py b/qa/rpc-tests/pruning.py index 21f8d69382..26ae4af010 100755 --- a/qa/rpc-tests/pruning.py +++ b/qa/rpc-tests/pruning.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -23,24 +23,7 @@ class PruneTest(BitcoinTestFramework): def __init__(self): self.utxo = [] self.address = ["",""] - - # Some pre-processing to create a bunch of OP_RETURN txouts to insert into transactions we create - # So we have big transactions and full blocks to fill up our block files - - # create one script_pubkey - script_pubkey = "6a4d0200" #OP_RETURN OP_PUSH2 512 bytes - for i in xrange (512): - script_pubkey = script_pubkey + "01" - # concatenate 128 txouts of above script_pubkey which we'll insert before the txout for change - self.txouts = "81" - for k in xrange(128): - # add txout value - self.txouts = self.txouts + "0000000000000000" - # add length of script_pubkey - self.txouts = self.txouts + "fd0402" - # add script_pubkey - self.txouts = self.txouts + script_pubkey - + self.txouts = gen_return_txouts() def setup_chain(self): print("Initializing test directory "+self.options.tmpdir) diff --git a/qa/rpc-tests/rawtransactions.py b/qa/rpc-tests/rawtransactions.py index 173faf736e..d77b41979b 100755 --- a/qa/rpc-tests/rawtransactions.py +++ b/qa/rpc-tests/rawtransactions.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/qa/rpc-tests/receivedby.py b/qa/rpc-tests/receivedby.py index 16d6bd4cf1..18af0e8102 100755 --- a/qa/rpc-tests/receivedby.py +++ b/qa/rpc-tests/receivedby.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/qa/rpc-tests/reindex.py b/qa/rpc-tests/reindex.py index f2e3f248ea..d90177a029 100755 --- a/qa/rpc-tests/reindex.py +++ b/qa/rpc-tests/reindex.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/qa/rpc-tests/replace-by-fee.py b/qa/rpc-tests/replace-by-fee.py index 537a1ed8d9..734db33b51 100755 --- a/qa/rpc-tests/replace-by-fee.py +++ b/qa/rpc-tests/replace-by-fee.py @@ -63,8 +63,14 @@ def make_utxo(node, amount, confirmed=True, scriptPubKey=CScript([1])): # If requested, ensure txouts are confirmed. if confirmed: - while len(node.getrawmempool()): + mempool_size = len(node.getrawmempool()) + while mempool_size > 0: node.generate(1) + new_size = len(node.getrawmempool()) + # Error out if we have something stuck in the mempool, as this + # would likely be a bug. + assert(new_size < mempool_size) + mempool_size = new_size return COutPoint(int(txid, 16), 0) @@ -72,8 +78,13 @@ class ReplaceByFeeTest(BitcoinTestFramework): def setup_network(self): self.nodes = [] - self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantx=1000", - "-relaypriority=0", "-whitelist=127.0.0.1"])) + self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantx=1000", "-debug", + "-relaypriority=0", "-whitelist=127.0.0.1", + "-limitancestorcount=50", + "-limitancestorsize=101", + "-limitdescendantcount=200", + "-limitdescendantsize=101" + ])) self.is_network_split = False def run_test(self): @@ -103,6 +114,9 @@ class ReplaceByFeeTest(BitcoinTestFramework): print "Running test opt-in..." self.test_opt_in() + print "Running test prioritised transactions..." + self.test_prioritised_transactions() + print "Passed\n" def test_simple_doublespend(self): @@ -508,5 +522,72 @@ class ReplaceByFeeTest(BitcoinTestFramework): # but make sure it is accepted anyway self.nodes[0].sendrawtransaction(tx3c_hex, True) + def test_prioritised_transactions(self): + # Ensure that fee deltas used via prioritisetransaction are + # correctly used by replacement logic + + # 1. Check that feeperkb uses modified fees + tx0_outpoint = make_utxo(self.nodes[0], 1.1*COIN) + + tx1a = CTransaction() + tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)] + tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))] + tx1a_hex = txToHex(tx1a) + tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) + + # Higher fee, but the actual fee per KB is much lower. + tx1b = CTransaction() + tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] + tx1b.vout = [CTxOut(0.001*COIN, CScript([b'a'*740000]))] + tx1b_hex = txToHex(tx1b) + + # Verify tx1b cannot replace tx1a. + try: + tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) + except JSONRPCException as exp: + assert_equal(exp.error['code'], -26) + else: + assert(False) + + # Use prioritisetransaction to set tx1a's fee to 0. + self.nodes[0].prioritisetransaction(tx1a_txid, 0, int(-0.1*COIN)) + + # Now tx1b should be able to replace tx1a + tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) + + assert(tx1b_txid in self.nodes[0].getrawmempool()) + + # 2. Check that absolute fee checks use modified fee. + tx1_outpoint = make_utxo(self.nodes[0], 1.1*COIN) + + tx2a = CTransaction() + tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0)] + tx2a.vout = [CTxOut(1*COIN, CScript([b'a']))] + tx2a_hex = txToHex(tx2a) + tx2a_txid = self.nodes[0].sendrawtransaction(tx2a_hex, True) + + # Lower fee, but we'll prioritise it + tx2b = CTransaction() + tx2b.vin = [CTxIn(tx1_outpoint, nSequence=0)] + tx2b.vout = [CTxOut(1.01*COIN, CScript([b'a']))] + tx2b.rehash() + tx2b_hex = txToHex(tx2b) + + # Verify tx2b cannot replace tx2a. + try: + tx2b_txid = self.nodes[0].sendrawtransaction(tx2b_hex, True) + except JSONRPCException as exp: + assert_equal(exp.error['code'], -26) + else: + assert(False) + + # Now prioritise tx2b to have a higher modified fee + self.nodes[0].prioritisetransaction(tx2b.hash, 0, int(0.1*COIN)) + + # tx2b should now be accepted + tx2b_txid = self.nodes[0].sendrawtransaction(tx2b_hex, True) + + assert(tx2b_txid in self.nodes[0].getrawmempool()) + if __name__ == '__main__': ReplaceByFeeTest().main() diff --git a/qa/rpc-tests/rest.py b/qa/rpc-tests/rest.py index e084ad55ab..682c531691 100755 --- a/qa/rpc-tests/rest.py +++ b/qa/rpc-tests/rest.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/qa/rpc-tests/rpcbind_test.py b/qa/rpc-tests/rpcbind_test.py index 7a9da66787..5f409ad616 100755 --- a/qa/rpc-tests/rpcbind_test.py +++ b/qa/rpc-tests/rpcbind_test.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/qa/rpc-tests/sendheaders.py b/qa/rpc-tests/sendheaders.py index d7f4292090..7572bc2776 100755 --- a/qa/rpc-tests/sendheaders.py +++ b/qa/rpc-tests/sendheaders.py @@ -70,7 +70,6 @@ f. Announce 1 more header that builds on that fork. class BaseNode(NodeConnCB): def __init__(self): NodeConnCB.__init__(self) - self.create_callback_map() self.connection = None self.last_inv = None self.last_headers = None @@ -221,18 +220,20 @@ class SendHeadersTest(BitcoinTestFramework): # mine count blocks and return the new tip def mine_blocks(self, count): + # Clear out last block announcement from each p2p listener + [ x.clear_last_announcement() for x in self.p2p_connections ] self.nodes[0].generate(count) return int(self.nodes[0].getbestblockhash(), 16) # mine a reorg that invalidates length blocks (replacing them with # length+1 blocks). - # peers is the p2p nodes we're using; we clear their state after the + # Note: we clear the state of our p2p connections after the # to-be-reorged-out blocks are mined, so that we don't break later tests. # return the list of block hashes newly mined - def mine_reorg(self, length, peers): + def mine_reorg(self, length): self.nodes[0].generate(length) # make sure all invalidated blocks are node0's sync_blocks(self.nodes, wait=0.1) - [x.clear_last_announcement() for x in peers] + [x.clear_last_announcement() for x in self.p2p_connections] tip_height = self.nodes[1].getblockcount() hash_to_invalidate = self.nodes[1].getblockhash(tip_height-(length-1)) @@ -246,6 +247,8 @@ class SendHeadersTest(BitcoinTestFramework): inv_node = InvNode() test_node = TestNode() + self.p2p_connections = [inv_node, test_node] + connections = [] connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], inv_node)) # Set nServices to 0 for test_node, so no block download will occur outside of @@ -304,7 +307,6 @@ class SendHeadersTest(BitcoinTestFramework): prev_tip = int(self.nodes[0].getbestblockhash(), 16) test_node.get_headers(locator=[prev_tip], hashstop=0L) test_node.sync_with_ping() - test_node.clear_last_announcement() # Clear out empty headers response # Now that we've synced headers, headers announcements should work tip = self.mine_blocks(1) @@ -353,8 +355,6 @@ class SendHeadersTest(BitcoinTestFramework): # broadcast it) assert_equal(inv_node.last_inv, None) assert_equal(inv_node.last_headers, None) - inv_node.clear_last_announcement() - test_node.clear_last_announcement() tip = self.mine_blocks(1) assert_equal(inv_node.check_last_announcement(inv=[tip]), True) assert_equal(test_node.check_last_announcement(headers=[tip]), True) @@ -369,7 +369,7 @@ class SendHeadersTest(BitcoinTestFramework): # getheaders or inv from peer. for j in xrange(2): # First try mining a reorg that can propagate with header announcement - new_block_hashes = self.mine_reorg(length=7, peers=[test_node, inv_node]) + new_block_hashes = self.mine_reorg(length=7) tip = new_block_hashes[-1] assert_equal(inv_node.check_last_announcement(inv=[tip]), True) assert_equal(test_node.check_last_announcement(headers=new_block_hashes), True) @@ -377,7 +377,7 @@ class SendHeadersTest(BitcoinTestFramework): block_time += 8 # Mine a too-large reorg, which should be announced with a single inv - new_block_hashes = self.mine_reorg(length=8, peers=[test_node, inv_node]) + new_block_hashes = self.mine_reorg(length=8) tip = new_block_hashes[-1] assert_equal(inv_node.check_last_announcement(inv=[tip]), True) assert_equal(test_node.check_last_announcement(inv=[tip]), True) @@ -389,7 +389,7 @@ class SendHeadersTest(BitcoinTestFramework): # Use getblocks/getdata test_node.send_getblocks(locator = [fork_point]) - assert_equal(test_node.check_last_announcement(inv=new_block_hashes[0:-1]), True) + assert_equal(test_node.check_last_announcement(inv=new_block_hashes), True) test_node.get_data(new_block_hashes) test_node.wait_for_block(new_block_hashes[-1]) @@ -408,7 +408,6 @@ class SendHeadersTest(BitcoinTestFramework): test_node.get_headers(locator=[fork_point], hashstop=new_block_hashes[1]) test_node.get_data([tip]) test_node.wait_for_block(tip) - test_node.clear_last_announcement() elif i == 2: test_node.get_data([tip]) test_node.wait_for_block(tip) diff --git a/qa/rpc-tests/smartfees.py b/qa/rpc-tests/smartfees.py index ecfffc1b45..b209ae0c16 100755 --- a/qa/rpc-tests/smartfees.py +++ b/qa/rpc-tests/smartfees.py @@ -19,9 +19,6 @@ P2SH_2 = "2NBdpwq8Aoo1EEKEXPNrKvr5xQr3M9UfcZA" # P2SH of "OP_2 OP_DROP" # 4 bytes of OP_TRUE and push 2-byte redeem script of "OP_1 OP_DROP" or "OP_2 OP_DROP" SCRIPT_SIG = ["0451025175", "0451025275"] -def satoshi_round(amount): - return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN) - def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee_increment): ''' Create and send a transaction with a random fee. diff --git a/qa/rpc-tests/test_framework/comptool.py b/qa/rpc-tests/test_framework/comptool.py index e0b3ce040d..badbc0a1fb 100755 --- a/qa/rpc-tests/test_framework/comptool.py +++ b/qa/rpc-tests/test_framework/comptool.py @@ -41,17 +41,32 @@ def wait_until(predicate, attempts=float('inf'), timeout=float('inf')): return False +class RejectResult(object): + ''' + Outcome that expects rejection of a transaction or block. + ''' + def __init__(self, code, reason=''): + self.code = code + self.reason = reason + def match(self, other): + if self.code != other.code: + return False + return other.reason.startswith(self.reason) + def __repr__(self): + return '%i:%s' % (self.code,self.reason or '*') + class TestNode(NodeConnCB): def __init__(self, block_store, tx_store): NodeConnCB.__init__(self) - self.create_callback_map() self.conn = None self.bestblockhash = None self.block_store = block_store self.block_request_map = {} self.tx_store = tx_store self.tx_request_map = {} + self.block_reject_map = {} + self.tx_reject_map = {} # When the pingmap is non-empty we're waiting for # a response @@ -95,6 +110,12 @@ class TestNode(NodeConnCB): except KeyError: raise AssertionError("Got pong for unknown ping [%s]" % repr(message)) + def on_reject(self, conn, message): + if message.message == 'tx': + self.tx_reject_map[message.data] = RejectResult(message.code, message.reason) + if message.message == 'block': + self.block_reject_map[message.data] = RejectResult(message.code, message.reason) + def send_inv(self, obj): mtype = 2 if isinstance(obj, CBlock) else 1 self.conn.send_message(msg_inv([CInv(mtype, obj.sha256)])) @@ -244,6 +265,15 @@ class TestManager(object): if outcome is None: if c.cb.bestblockhash != self.connections[0].cb.bestblockhash: return False + elif isinstance(outcome, RejectResult): # Check that block was rejected w/ code + if c.cb.bestblockhash == blockhash: + return False + if blockhash not in c.cb.block_reject_map: + print 'Block not in reject map: %064x' % (blockhash) + return False + if not outcome.match(c.cb.block_reject_map[blockhash]): + print 'Block rejected with %s instead of expected %s: %064x' % (c.cb.block_reject_map[blockhash], outcome, blockhash) + return False elif ((c.cb.bestblockhash == blockhash) != outcome): # print c.cb.bestblockhash, blockhash, outcome return False @@ -263,6 +293,15 @@ class TestManager(object): if c.cb.lastInv != self.connections[0].cb.lastInv: # print c.rpc.getrawmempool() return False + elif isinstance(outcome, RejectResult): # Check that tx was rejected w/ code + if txhash in c.cb.lastInv: + return False + if txhash not in c.cb.tx_reject_map: + print 'Tx not in reject map: %064x' % (txhash) + return False + if not outcome.match(c.cb.tx_reject_map[txhash]): + print 'Tx rejected with %s instead of expected %s: %064x' % (c.cb.tx_reject_map[txhash], outcome, txhash) + return False elif ((txhash in c.cb.lastInv) != outcome): # print c.rpc.getrawmempool(), c.cb.lastInv return False diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index 64985d58e2..ca65fb6e79 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -36,6 +36,7 @@ MY_VERSION = 60001 # past bip-31 for ping/pong MY_SUBVERSION = "/python-mininode-tester:0.0.1/" MAX_INV_SZ = 50000 +MAX_BLOCK_SIZE = 1000000 # Keep our own socket map for asyncore, so that we can track disconnects # ourselves (to workaround an issue with closing an asyncore socket when @@ -1003,6 +1004,18 @@ class msg_reject(object): class NodeConnCB(object): def __init__(self): self.verack_received = False + # deliver_sleep_time is helpful for debugging race conditions in p2p + # 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 + + def set_deliver_sleep_time(self, value): + with mininode_lock: + self.deliver_sleep_time = value + + def get_deliver_sleep_time(self): + with mininode_lock: + return self.deliver_sleep_time # Spin until verack message is received from the node. # Tests may want to use this as a signal that the test can begin. @@ -1015,32 +1028,13 @@ class NodeConnCB(object): return time.sleep(0.05) - # Derived classes should call this function once to set the message map - # which associates the derived classes' functions to incoming messages - def create_callback_map(self): - self.cbmap = { - "version": self.on_version, - "verack": self.on_verack, - "addr": self.on_addr, - "alert": self.on_alert, - "inv": self.on_inv, - "getdata": self.on_getdata, - "getblocks": self.on_getblocks, - "tx": self.on_tx, - "block": self.on_block, - "getaddr": self.on_getaddr, - "ping": self.on_ping, - "pong": self.on_pong, - "headers": self.on_headers, - "getheaders": self.on_getheaders, - "reject": self.on_reject, - "mempool": self.on_mempool - } - def deliver(self, conn, message): + deliver_sleep = self.get_deliver_sleep_time() + if deliver_sleep is not None: + time.sleep(deliver_sleep) with mininode_lock: try: - self.cbmap[message.command](conn, message) + getattr(self, 'on_' + message.command)(conn, message) except: print "ERROR delivering %s (%s)" % (repr(message), sys.exc_info()[0]) diff --git a/qa/rpc-tests/test_framework/netutil.py b/qa/rpc-tests/test_framework/netutil.py index b30a88a4f7..50daa87937 100644 --- a/qa/rpc-tests/test_framework/netutil.py +++ b/qa/rpc-tests/test_framework/netutil.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/qa/rpc-tests/test_framework/script.py b/qa/rpc-tests/test_framework/script.py index 0a78cf6fb1..0088876028 100644 --- a/qa/rpc-tests/test_framework/script.py +++ b/qa/rpc-tests/test_framework/script.py @@ -226,7 +226,7 @@ OP_CHECKMULTISIGVERIFY = CScriptOp(0xaf) # expansion OP_NOP1 = CScriptOp(0xb0) -OP_NOP2 = CScriptOp(0xb1) +OP_CHECKLOCKTIMEVERIFY = CScriptOp(0xb1) OP_NOP3 = CScriptOp(0xb2) OP_NOP4 = CScriptOp(0xb3) OP_NOP5 = CScriptOp(0xb4) @@ -353,7 +353,7 @@ VALID_OPCODES = { OP_CHECKMULTISIGVERIFY, OP_NOP1, - OP_NOP2, + OP_CHECKLOCKTIMEVERIFY, OP_NOP3, OP_NOP4, OP_NOP5, @@ -472,7 +472,7 @@ OPCODE_NAMES.update({ OP_CHECKMULTISIG : 'OP_CHECKMULTISIG', OP_CHECKMULTISIGVERIFY : 'OP_CHECKMULTISIGVERIFY', OP_NOP1 : 'OP_NOP1', - OP_NOP2 : 'OP_NOP2', + OP_CHECKLOCKTIMEVERIFY : 'OP_CHECKLOCKTIMEVERIFY', OP_NOP3 : 'OP_NOP3', OP_NOP4 : 'OP_NOP4', OP_NOP5 : 'OP_NOP5', @@ -591,7 +591,7 @@ OPCODES_BY_NAME = { 'OP_CHECKMULTISIG' : OP_CHECKMULTISIG, 'OP_CHECKMULTISIGVERIFY' : OP_CHECKMULTISIGVERIFY, 'OP_NOP1' : OP_NOP1, - 'OP_NOP2' : OP_NOP2, + 'OP_CHECKLOCKTIMEVERIFY' : OP_CHECKLOCKTIMEVERIFY, 'OP_NOP3' : OP_NOP3, 'OP_NOP4' : OP_NOP4, 'OP_NOP5' : OP_NOP5, diff --git a/qa/rpc-tests/test_framework/test_framework.py b/qa/rpc-tests/test_framework/test_framework.py index ae2d91ab60..584f318d0b 100755 --- a/qa/rpc-tests/test_framework/test_framework.py +++ b/qa/rpc-tests/test_framework/test_framework.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -120,7 +120,7 @@ class BitcoinTestFramework(object): if self.options.coveragedir: enable_coverage(self.options.coveragedir) - os.environ['PATH'] = self.options.srcdir+":"+os.environ['PATH'] + os.environ['PATH'] = self.options.srcdir+":"+self.options.srcdir+"/qt:"+os.environ['PATH'] check_json_precision() diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index 30dd5de585..0388e08115 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -1,4 +1,4 @@ -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. # @@ -67,6 +67,9 @@ def check_json_precision(): if satoshis != 2000000000000003: raise RuntimeError("JSON encode/decode loses precision") +def count_bytes(hex_string): + return len(bytearray.fromhex(hex_string)) + def sync_blocks(rpc_connections, wait=1): """ Wait until everybody has the same block count @@ -104,6 +107,7 @@ def initialize_datadir(dirname, n): f.write("rpcpassword=rt\n"); f.write("port="+str(p2p_port(n))+"\n"); f.write("rpcport="+str(rpc_port(n))+"\n"); + f.write("listenonion=0\n"); return datadir def initialize_chain(test_dir): @@ -127,7 +131,7 @@ def initialize_chain(test_dir): # Create cache directories, run bitcoinds: for i in range(4): datadir=initialize_datadir("cache", i) - args = [ os.getenv("BITCOIND", "bitcoind"), "-keypool=1", "-datadir="+datadir, "-discover=0" ] + args = [ os.getenv("BITCOIND", "bitcoind"), "-server", "-keypool=1", "-datadir="+datadir, "-discover=0" ] if i > 0: args.append("-connect=127.0.0.1:"+str(p2p_port(0))) bitcoind_processes[i] = subprocess.Popen(args) @@ -214,7 +218,8 @@ def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary= datadir = os.path.join(dirname, "node"+str(i)) if binary is None: binary = os.getenv("BITCOIND", "bitcoind") - args = [ binary, "-datadir="+datadir, "-keypool=1", "-discover=0", "-rest" ] + # RPC tests still depend on free transactions + args = [ binary, "-datadir="+datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-blockprioritysize=50000" ] if extra_args is not None: args.extend(extra_args) bitcoind_processes[i] = subprocess.Popen(args) devnull = open(os.devnull, "w") @@ -401,3 +406,70 @@ def assert_raises(exc, fun, *args, **kwds): raise AssertionError("Unexpected exception raised: "+type(e).__name__) else: raise AssertionError("No exception raised") + +def satoshi_round(amount): + return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN) + +def create_confirmed_utxos(fee, node, count): + node.generate(int(0.5*count)+101) + utxos = node.listunspent() + iterations = count - len(utxos) + addr1 = node.getnewaddress() + addr2 = node.getnewaddress() + if iterations <= 0: + return utxos + for i in xrange(iterations): + t = utxos.pop() + inputs = [] + inputs.append({ "txid" : t["txid"], "vout" : t["vout"]}) + outputs = {} + send_value = t['amount'] - fee + outputs[addr1] = satoshi_round(send_value/2) + outputs[addr2] = satoshi_round(send_value/2) + raw_tx = node.createrawtransaction(inputs, outputs) + signed_tx = node.signrawtransaction(raw_tx)["hex"] + txid = node.sendrawtransaction(signed_tx) + + while (node.getmempoolinfo()['size'] > 0): + node.generate(1) + + utxos = node.listunspent() + assert(len(utxos) >= count) + return utxos + +def gen_return_txouts(): + # Some pre-processing to create a bunch of OP_RETURN txouts to insert into transactions we create + # So we have big transactions (and therefore can't fit very many into each block) + # create one script_pubkey + script_pubkey = "6a4d0200" #OP_RETURN OP_PUSH2 512 bytes + for i in xrange (512): + script_pubkey = script_pubkey + "01" + # concatenate 128 txouts of above script_pubkey which we'll insert before the txout for change + txouts = "81" + for k in xrange(128): + # add txout value + txouts = txouts + "0000000000000000" + # add length of script_pubkey + txouts = txouts + "fd0402" + # add script_pubkey + txouts = txouts + script_pubkey + return txouts + +def create_lots_of_big_transactions(node, txouts, utxos, fee): + addr = node.getnewaddress() + txids = [] + for i in xrange(len(utxos)): + t = utxos.pop() + inputs = [] + inputs.append({ "txid" : t["txid"], "vout" : t["vout"]}) + outputs = {} + send_value = t['amount'] - fee + outputs[addr] = satoshi_round(send_value) + rawtx = node.createrawtransaction(inputs, outputs) + newtx = rawtx[0:92] + newtx = newtx + txouts + newtx = newtx + rawtx[94:] + signresult = node.signrawtransaction(newtx, None, None, "NONE") + txid = node.sendrawtransaction(signresult["hex"], True) + txids.append(txid) + return txids diff --git a/qa/rpc-tests/txn_clone.py b/qa/rpc-tests/txn_clone.py index e8ced0e5bb..bad090bcb4 100755 --- a/qa/rpc-tests/txn_clone.py +++ b/qa/rpc-tests/txn_clone.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -136,7 +136,7 @@ class TxnMallTest(BitcoinTestFramework): tx2 = self.nodes[0].gettransaction(txid2) # Verify expected confirmations - assert_equal(tx1["confirmations"], -1) + assert_equal(tx1["confirmations"], -2) assert_equal(tx1_clone["confirmations"], 2) assert_equal(tx2["confirmations"], 1) diff --git a/qa/rpc-tests/txn_doublespend.py b/qa/rpc-tests/txn_doublespend.py index 36081127b4..05a3a34788 100755 --- a/qa/rpc-tests/txn_doublespend.py +++ b/qa/rpc-tests/txn_doublespend.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -99,7 +99,7 @@ class TxnMallTest(BitcoinTestFramework): # Now give doublespend and its parents to miner: self.nodes[2].sendrawtransaction(fund_foo_tx["hex"]) self.nodes[2].sendrawtransaction(fund_bar_tx["hex"]) - self.nodes[2].sendrawtransaction(doublespend["hex"]) + doublespend_txid = self.nodes[2].sendrawtransaction(doublespend["hex"]) # ... mine a block... self.nodes[2].generate(1) @@ -107,14 +107,15 @@ class TxnMallTest(BitcoinTestFramework): connect_nodes(self.nodes[1], 2) self.nodes[2].generate(1) # Mine another block to make sure we sync sync_blocks(self.nodes) + assert_equal(self.nodes[0].gettransaction(doublespend_txid)["confirmations"], 2) # Re-fetch transaction info: tx1 = self.nodes[0].gettransaction(txid1) tx2 = self.nodes[0].gettransaction(txid2) - + # Both transactions should be conflicted - assert_equal(tx1["confirmations"], -1) - assert_equal(tx2["confirmations"], -1) + assert_equal(tx1["confirmations"], -2) + assert_equal(tx2["confirmations"], -2) # Node0's total balance should be starting balance, plus 100BTC for # two more matured blocks, minus 1240 for the double-spend, plus fees (which are diff --git a/qa/rpc-tests/wallet.py b/qa/rpc-tests/wallet.py index f9ec6f429b..43ec621a40 100755 --- a/qa/rpc-tests/wallet.py +++ b/qa/rpc-tests/wallet.py @@ -1,29 +1,25 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -# -# Exercise the wallet. Ported from wallet.sh. -# Does the following: -# a) creates 3 nodes, with an empty chain (no blocks). -# b) node0 mines a block -# c) node1 mines 101 blocks, so now nodes 0 and 1 have 50btc, node2 has none. -# d) node0 sends 21 btc to node2, in two transactions (11 btc, then 10 btc). -# e) node0 mines a block, collects the fee on the second transaction -# f) node1 mines 100 blocks, to mature node0's just-mined block -# g) check that node0 has 100-21, node2 has 21 -# h) node0 should now have 2 unspent outputs; send these to node2 via raw tx broadcast by node1 -# i) have node1 mine a block -# j) check balances - node0 should have 0, node2 should have 100 -# k) test ResendWalletTransactions - create transactions, startup fourth node, make sure it syncs -# from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * class WalletTest (BitcoinTestFramework): + def check_fee_amount(self, curr_balance, balance_with_fee, fee_per_byte, tx_size): + """Return curr_balance after asserting the fee was in range""" + fee = balance_with_fee - curr_balance + target_fee = fee_per_byte * tx_size + if fee < target_fee: + raise AssertionError("Fee of %s BTC too low! (Should be %s BTC)"%(str(fee), str(target_fee))) + # allow the node's estimation to be at most 2 bytes off + if fee > fee_per_byte * (tx_size + 2): + raise AssertionError("Fee of %s BTC too high! (Should be %s BTC)"%(str(fee), str(target_fee))) + return curr_balance + def setup_chain(self): print("Initializing test directory "+self.options.tmpdir) initialize_chain_clean(self.options.tmpdir, 4) @@ -104,33 +100,37 @@ class WalletTest (BitcoinTestFramework): # Send 10 BTC normal address = self.nodes[0].getnewaddress("test") - self.nodes[2].settxfee(Decimal('0.001')) + fee_per_byte = Decimal('0.001') / 1000 + self.nodes[2].settxfee(fee_per_byte * 1000) txid = self.nodes[2].sendtoaddress(address, 10, "", "", False) self.nodes[2].generate(1) self.sync_all() - assert_equal(self.nodes[2].getbalance(), Decimal('89.99900000')) - assert_equal(self.nodes[0].getbalance(), Decimal('10.00000000')) + node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), Decimal('90'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid))) + assert_equal(self.nodes[0].getbalance(), Decimal('10')) # Send 10 BTC with subtract fee from amount txid = self.nodes[2].sendtoaddress(address, 10, "", "", True) self.nodes[2].generate(1) self.sync_all() - assert_equal(self.nodes[2].getbalance(), Decimal('79.99900000')) - assert_equal(self.nodes[0].getbalance(), Decimal('19.99900000')) + node_2_bal -= Decimal('10') + assert_equal(self.nodes[2].getbalance(), node_2_bal) + node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), Decimal('20'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid))) # Sendmany 10 BTC txid = self.nodes[2].sendmany('from1', {address: 10}, 0, "", []) self.nodes[2].generate(1) self.sync_all() - assert_equal(self.nodes[2].getbalance(), Decimal('69.99800000')) - assert_equal(self.nodes[0].getbalance(), Decimal('29.99900000')) + node_0_bal += Decimal('10') + node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), node_2_bal - Decimal('10'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid))) + assert_equal(self.nodes[0].getbalance(), node_0_bal) # Sendmany 10 BTC with subtract fee from amount txid = self.nodes[2].sendmany('from1', {address: 10}, 0, "", [address]) self.nodes[2].generate(1) self.sync_all() - assert_equal(self.nodes[2].getbalance(), Decimal('59.99800000')) - assert_equal(self.nodes[0].getbalance(), Decimal('39.99800000')) + node_2_bal -= Decimal('10') + assert_equal(self.nodes[2].getbalance(), node_2_bal) + node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), node_0_bal + Decimal('10'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid))) # Test ResendWalletTransactions: # Create a couple of transactions, then start up a fourth @@ -175,7 +175,7 @@ class WalletTest (BitcoinTestFramework): for uTx in unspentTxs: if uTx['txid'] == zeroValueTxid: found = True - assert_equal(uTx['amount'], Decimal('0.00000000')); + assert_equal(uTx['amount'], Decimal('0')) assert(found) #do some -walletbroadcast tests @@ -187,21 +187,22 @@ class WalletTest (BitcoinTestFramework): connect_nodes_bi(self.nodes,0,2) self.sync_all() - txIdNotBroadcasted = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2); + txIdNotBroadcasted = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2) txObjNotBroadcasted = self.nodes[0].gettransaction(txIdNotBroadcasted) self.nodes[1].generate(1) #mine a block, tx should not be in there self.sync_all() - assert_equal(self.nodes[2].getbalance(), Decimal('59.99800000')); #should not be changed because tx was not broadcasted + assert_equal(self.nodes[2].getbalance(), node_2_bal) #should not be changed because tx was not broadcasted #now broadcast from another node, mine a block, sync, and check the balance self.nodes[1].sendrawtransaction(txObjNotBroadcasted['hex']) self.nodes[1].generate(1) self.sync_all() + node_2_bal += 2 txObjNotBroadcasted = self.nodes[0].gettransaction(txIdNotBroadcasted) - assert_equal(self.nodes[2].getbalance(), Decimal('61.99800000')); #should not be + assert_equal(self.nodes[2].getbalance(), node_2_bal) #create another tx - txIdNotBroadcasted = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2); + txIdNotBroadcasted = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2) #restart the nodes with -walletbroadcast=1 stop_nodes(self.nodes) @@ -214,23 +215,24 @@ class WalletTest (BitcoinTestFramework): self.nodes[0].generate(1) sync_blocks(self.nodes) + node_2_bal += 2 #tx should be added to balance because after restarting the nodes tx should be broadcastet - assert_equal(self.nodes[2].getbalance(), Decimal('63.99800000')); #should not be + assert_equal(self.nodes[2].getbalance(), node_2_bal) #send a tx with value in a string (PR#6380 +) txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "2") txObj = self.nodes[0].gettransaction(txId) - assert_equal(txObj['amount'], Decimal('-2.00000000')) + assert_equal(txObj['amount'], Decimal('-2')) txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "0.0001") txObj = self.nodes[0].gettransaction(txId) - assert_equal(txObj['amount'], Decimal('-0.00010000')) + assert_equal(txObj['amount'], Decimal('-0.0001')) #check if JSON parser can handle scientific notation in strings txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "1e-4") txObj = self.nodes[0].gettransaction(txId) - assert_equal(txObj['amount'], Decimal('-0.00010000')) + assert_equal(txObj['amount'], Decimal('-0.0001')) #this should fail errorString = "" @@ -239,7 +241,7 @@ class WalletTest (BitcoinTestFramework): except JSONRPCException,e: errorString = e.error['message'] - assert_equal("Invalid amount" in errorString, True); + assert_equal("Invalid amount" in errorString, True) errorString = "" try: @@ -247,7 +249,30 @@ class WalletTest (BitcoinTestFramework): except JSONRPCException,e: errorString = e.error['message'] - assert_equal("not an integer" in errorString, True); + assert_equal("not an integer" in errorString, True) + + #check if wallet or blochchain maintenance changes the balance + self.sync_all() + self.nodes[0].generate(1) + self.sync_all() + balance_nodes = [self.nodes[i].getbalance() for i in range(3)] + + maintenance = [ + '-rescan', + '-reindex', + '-zapwallettxes=1', + '-zapwallettxes=2', + '-salvagewallet', + ] + for m in maintenance: + stop_nodes(self.nodes) + wait_bitcoinds() + self.nodes = start_nodes(3, self.options.tmpdir, [[m]] * 3) + connect_nodes_bi(self.nodes,0,1) + connect_nodes_bi(self.nodes,1,2) + connect_nodes_bi(self.nodes,0,2) + self.sync_all() + assert_equal(balance_nodes, [self.nodes[i].getbalance() for i in range(3)]) if __name__ == '__main__': diff --git a/qa/rpc-tests/walletbackup.py b/qa/rpc-tests/walletbackup.py index da100d7fc0..1221a09116 100755 --- a/qa/rpc-tests/walletbackup.py +++ b/qa/rpc-tests/walletbackup.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/qa/rpc-tests/zapwallettxes.py b/qa/rpc-tests/zapwallettxes.py index 0ec8ec5364..1ee0f79ac0 100755 --- a/qa/rpc-tests/zapwallettxes.py +++ b/qa/rpc-tests/zapwallettxes.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2 -# Copyright (c) 2014 The Bitcoin Core developers +# Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. |