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))) {