From 9ddb6ad028ec5c89a3b00eb2c291243b914162d5 Mon Sep 17 00:00:00 2001 From: Simon Date: Mon, 14 Nov 2016 11:48:55 -0800 Subject: [PATCH] Mempool will accept tx with joinsplits and the default z_sendmany fee. Issue #1851 shows that a zaddr->taddr can be rejected from mempools due to not meeting fee requirements given the size of the transaction. Fee calculation for joinsplit txs has not yet been agreed upon, so during this interim period, this patch ensures joinsplit txs using the default fee are not rejected due to an insufficient fee. --- qa/rpc-tests/wallet_protectcoinbase.py | 34 +++++++++++++++++++++++++- src/main.cpp | 18 +++++++++----- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/qa/rpc-tests/wallet_protectcoinbase.py b/qa/rpc-tests/wallet_protectcoinbase.py index e05ccb529..b71a1e6f0 100755 --- a/qa/rpc-tests/wallet_protectcoinbase.py +++ b/qa/rpc-tests/wallet_protectcoinbase.py @@ -150,6 +150,37 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): errorString = e.error['message'] assert_equal("Insufficient funds, coinbase funds can only be spent after they have been sent to a zaddr" in errorString, True) + # Verify that mempools accept tx with joinsplits which have at least the default z_sendmany fee. + # If this test passes, it confirms that issue #1851 has been resolved, where sending from + # a zaddr to 1385 taddr recipients fails because the default fee was considered too low + # given the tx size, resulting in mempool rejection. + errorString = '' + recipients = [] + num_t_recipients = 2500 + amount_per_recipient = Decimal('0.00000546') # dust threshold + # Note that regtest chainparams does not require standard tx, so setting the amount to be + # less than the dust threshold, e.g. 0.00000001 will not result in mempool rejection. + for i in xrange(0,num_t_recipients): + newtaddr = self.nodes[2].getnewaddress() + recipients.append({"address":newtaddr, "amount":amount_per_recipient}) + myopid = self.nodes[0].z_sendmany(myzaddr, recipients) + try: + self.wait_and_assert_operationid_status(myopid) + except JSONRPCException as e: + print("JSONRPC error: "+e.error['message']) + assert(False) + except Exception as e: + print("Unexpected exception caught during testing: "+str(sys.exc_info()[0])) + assert(False) + + self.sync_all() + self.nodes[1].generate(1) + self.sync_all() + + # check balance + node2balance = amount_per_recipient * num_t_recipients + assert_equal(self.nodes[2].getbalance(), node2balance) + # Send will succeed because the balance of non-coinbase utxos is 10.0 try: self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 9) @@ -161,7 +192,8 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework): self.sync_all() # check balance - assert_equal(self.nodes[2].getbalance(), 9) + node2balance = node2balance + 9 + assert_equal(self.nodes[2].getbalance(), node2balance) # Check that chained joinsplits in a single tx are created successfully. recipients = [] diff --git a/src/main.cpp b/src/main.cpp index 612f1ed83..9c3d6e678 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -26,6 +26,7 @@ #include "util.h" #include "utilmoneystr.h" #include "validationinterface.h" +#include "wallet/asyncrpcoperation_sendmany.h" #include @@ -1170,12 +1171,17 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), mempool.HasNoInputsOf(tx)); unsigned int nSize = entry.GetTxSize(); - // Don't accept it if it can't get into a block - CAmount txMinFee = GetMinRelayFee(tx, nSize, true); - if (fLimitFree && nFees < txMinFee) - return state.DoS(0, error("AcceptToMemoryPool: not enough fees %s, %d < %d", - hash.ToString(), nFees, txMinFee), - REJECT_INSUFFICIENTFEE, "insufficient fee"); + // Accept a tx if it contains joinsplits and has at least the default fee specified by z_sendmany. + if (tx.vjoinsplit.size() > 0 && nFees >= ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE) { + // In future we will we have more accurate and dynamic computation of fees for tx with joinsplits. + } else { + // Don't accept it if it can't get into a block + CAmount txMinFee = GetMinRelayFee(tx, nSize, true); + if (fLimitFree && nFees < txMinFee) + return state.DoS(0, error("AcceptToMemoryPool: not enough fees %s, %d < %d", + hash.ToString(), nFees, txMinFee), + REJECT_INSUFFICIENTFEE, "insufficient fee"); + } // Require that free transactions have sufficient priority to be mined in the next block. if (GetBoolArg("-relaypriority", false) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) {