From 4ef014151d0c2d0f9c151a96b7ae8fb521f56282 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 20 Jun 2017 15:58:46 +1200 Subject: [PATCH] Additional testing of -mempooltxinputlimit --- qa/pull-tester/rpc-tests.sh | 1 + qa/rpc-tests/mempool_tx_input_limit.py | 138 +++++++++++++++++++++++++ src/gtest/test_mempool.cpp | 6 +- 3 files changed, 144 insertions(+), 1 deletion(-) create mode 100755 qa/rpc-tests/mempool_tx_input_limit.py diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index 08ff3fe7a..99706f178 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -26,6 +26,7 @@ testScripts=( 'rest.py' 'mempool_spendcoinbase.py' 'mempool_coinbase_spends.py' + 'mempool_tx_input_limit.py' 'httpbasics.py' 'zapwallettxes.py' 'proxy_test.py' diff --git a/qa/rpc-tests/mempool_tx_input_limit.py b/qa/rpc-tests/mempool_tx_input_limit.py new file mode 100755 index 000000000..ed0bf206e --- /dev/null +++ b/qa/rpc-tests/mempool_tx_input_limit.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python2 +# Copyright (c) 2017 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * +import os +import shutil +from time import sleep + +# Create one-input, one-output, no-fee transaction: +class MempoolTxInputLimitTest(BitcoinTestFramework): + + alert_filename = None # Set by setup_network + + def setup_network(self): + args = ["-checkmempool", "-debug=mempool", "-mempooltxinputlimit=2"] + self.nodes = [] + self.nodes.append(start_node(0, self.options.tmpdir, args)) + self.nodes.append(start_node(1, self.options.tmpdir, args)) + connect_nodes(self.nodes[1], 0) + self.is_network_split = False + self.sync_all + + def setup_chain(self): + print "Initializing test directory "+self.options.tmpdir + initialize_chain_clean(self.options.tmpdir, 2) + + def call_z_sendmany(self, from_addr, to_addr, amount): + recipients = [] + recipients.append({"address": to_addr, "amount": amount}) + myopid = self.nodes[0].z_sendmany(from_addr, recipients) + + opids = [] + opids.append(myopid) + + timeout = 120 + status = None + for x in xrange(1, timeout): + results = self.nodes[0].z_getoperationresult(opids) + if len(results)==0: + sleep(1) + else: + status = results[0]["status"] + assert_equal("success", status) + return results[0]["result"]["txid"] + + def run_test(self): + start_count = self.nodes[0].getblockcount() + + self.nodes[0].generate(100) + self.sync_all() + # Mine three blocks. After this, nodes[0] blocks + # 1, 2, and 3 are spend-able. + self.nodes[1].generate(3) + self.sync_all() + + # Check 1: z_sendmany is limited by -mempooltxinputlimit + + # Add zaddr to node 0 + node0_zaddr = self.nodes[0].z_getnewaddress() + + # Send three inputs from node 0 taddr to zaddr to get out of coinbase + node0_taddr = self.nodes[0].getnewaddress(); + recipients = [] + recipients.append({"address":node0_zaddr, "amount":Decimal('30.0')-Decimal('0.0001')}) # utxo amount less fee + myopid = self.nodes[0].z_sendmany(node0_taddr, recipients) + + opids = [] + opids.append(myopid) + + # Spend should fail due to -mempooltxinputlimit + timeout = 120 + status = None + for x in xrange(1, timeout): + results = self.nodes[0].z_getoperationresult(opids) + if len(results)==0: + sleep(1) + else: + status = results[0]["status"] + msg = results[0]["error"]["message"] + assert_equal("failed", status) + assert_equal("Too many transparent inputs 3 > limit 2", msg) + break + + # Mempool should be empty. + assert_equal(set(self.nodes[0].getrawmempool()), set()) + + # Reduce amount to only use two inputs + spend_zaddr_amount = Decimal('20.0') - Decimal('0.0001') + spend_zaddr_id = self.call_z_sendmany(node0_taddr, node0_zaddr, spend_zaddr_amount) # utxo amount less fee + self.sync_all() + + # Spend should be in the mempool + assert_equal(set(self.nodes[0].getrawmempool()), set([ spend_zaddr_id ])) + + self.nodes[0].generate(1) + self.sync_all() + + # mempool should be empty. + assert_equal(set(self.nodes[0].getrawmempool()), set()) + + # Check 2: sendfrom is limited by -mempooltxinputlimit + node1_taddr = self.nodes[1].getnewaddress(); + recipients = [] + spend_taddr_amount = spend_zaddr_amount - Decimal('0.0001') + spend_taddr_output = Decimal('8') + # Create three outputs + self.call_z_sendmany(node0_zaddr, node1_taddr, spend_taddr_output - Decimal('0.0001')) + self.nodes[1].generate(1) + self.sync_all() + self.call_z_sendmany(node0_zaddr, node1_taddr, spend_taddr_output - Decimal('0.0001')) + self.nodes[1].generate(1) + self.sync_all() + self.call_z_sendmany(node0_zaddr, node1_taddr, spend_taddr_amount - spend_taddr_output - spend_taddr_output - Decimal('0.0001')) # note amount less fees + self.nodes[1].generate(1) + self.sync_all() + + # Should use three UTXOs and fail + try: + self.nodes[1].sendtoaddress(node0_taddr, spend_taddr_amount - Decimal('1')) + assert(False) + except JSONRPCException,e: + msg = e.error['message'] + assert_equal("Too many transparent inputs 3 > limit 2", msg) + + # mempool should be empty. + assert_equal(set(self.nodes[1].getrawmempool()), set()) + + # Should use two UTXOs and succeed + spend_taddr_id2 = self.nodes[1].sendtoaddress(node0_taddr, spend_taddr_output + spend_taddr_output - Decimal('1')) + + # Spend should be in the mempool + assert_equal(set(self.nodes[1].getrawmempool()), set([ spend_taddr_id2 ])) + +if __name__ == '__main__': + MempoolTxInputLimitTest().main() diff --git a/src/gtest/test_mempool.cpp b/src/gtest/test_mempool.cpp index e84b2dead..14b4d83b1 100644 --- a/src/gtest/test_mempool.cpp +++ b/src/gtest/test_mempool.cpp @@ -96,6 +96,9 @@ TEST(Mempool, TxInputLimit) { bool missingInputs; // Create an obviously-invalid transaction + // We intentionally set tx.nVersion = 0 to reliably trigger an error, as + // it's the first check that occurs after the -mempooltxinputlimit check, + // and it means that we don't have to mock out a lot of global state. CMutableTransaction mtx; mtx.nVersion = 0; mtx.vin.resize(10); @@ -121,7 +124,8 @@ TEST(Mempool, TxInputLimit) { CValidationState state3; CTransaction tx3(mtx); EXPECT_FALSE(AcceptToMemoryPool(pool, state3, tx3, false, &missingInputs)); - EXPECT_NE(state3.GetRejectReason(), "bad-txns-version-too-low"); + // The -mempooltxinputlimit check doesn't set a reason + EXPECT_EQ(state3.GetRejectReason(), ""); // Clear the limit mapArgs.erase("-mempooltxinputlimit");