mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-12 11:19:08 -05:00
[rpc] Return fee and vsize from testmempoolaccept
Return fee and vsize if tx would pass ATMP.
This commit is contained in:
parent
9366a73d69
commit
2233a93a10
6 changed files with 43 additions and 15 deletions
|
@ -102,6 +102,9 @@ will trigger BIP 125 (replace-by-fee) opt-in. (#11413)
|
||||||
option `-deprecatedrpc=banscore` is used. The `banscore` field will be fully
|
option `-deprecatedrpc=banscore` is used. The `banscore` field will be fully
|
||||||
removed in the next major release. (#19469)
|
removed in the next major release. (#19469)
|
||||||
|
|
||||||
|
- The `testmempoolaccept` RPC returns `vsize` and a `fee` object with the `base` fee
|
||||||
|
if the transaction passes validation. (#19940)
|
||||||
|
|
||||||
- The `walletcreatefundedpsbt` RPC call will now fail with
|
- The `walletcreatefundedpsbt` RPC call will now fail with
|
||||||
`Insufficient funds` when inputs are manually selected but are not enough to cover
|
`Insufficient funds` when inputs are manually selected but are not enough to cover
|
||||||
the outputs and fee. Additional inputs can automatically be added through the
|
the outputs and fee. Additional inputs can automatically be added through the
|
||||||
|
|
|
@ -878,6 +878,11 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
|
||||||
{
|
{
|
||||||
{RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
|
{RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
|
||||||
{RPCResult::Type::BOOL, "allowed", "If the mempool allows this tx to be inserted"},
|
{RPCResult::Type::BOOL, "allowed", "If the mempool allows this tx to be inserted"},
|
||||||
|
{RPCResult::Type::NUM, "vsize", "Virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted (only present when 'allowed' is true)"},
|
||||||
|
{RPCResult::Type::OBJ, "fees", "Transaction fees (only present if 'allowed' is true)",
|
||||||
|
{
|
||||||
|
{RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT},
|
||||||
|
}},
|
||||||
{RPCResult::Type::STR, "reject-reason", "Rejection string (only present when 'allowed' is false)"},
|
{RPCResult::Type::STR, "reject-reason", "Rejection string (only present when 'allowed' is false)"},
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
|
@ -924,13 +929,22 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
|
||||||
|
|
||||||
TxValidationState state;
|
TxValidationState state;
|
||||||
bool test_accept_res;
|
bool test_accept_res;
|
||||||
|
CAmount fee;
|
||||||
{
|
{
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
test_accept_res = AcceptToMemoryPool(mempool, state, std::move(tx),
|
test_accept_res = AcceptToMemoryPool(mempool, state, std::move(tx),
|
||||||
nullptr /* plTxnReplaced */, false /* bypass_limits */, max_raw_tx_fee, /* test_accept */ true);
|
nullptr /* plTxnReplaced */, false /* bypass_limits */, max_raw_tx_fee, /* test_accept */ true, &fee);
|
||||||
}
|
}
|
||||||
result_0.pushKV("allowed", test_accept_res);
|
result_0.pushKV("allowed", test_accept_res);
|
||||||
if (!test_accept_res) {
|
|
||||||
|
// Only return the fee and vsize if the transaction would pass ATMP.
|
||||||
|
// These can be used to calculate the feerate.
|
||||||
|
if (test_accept_res) {
|
||||||
|
result_0.pushKV("vsize", virtual_size);
|
||||||
|
UniValue fees(UniValue::VOBJ);
|
||||||
|
fees.pushKV("base", ValueFromAmount(fee));
|
||||||
|
result_0.pushKV("fees", fees);
|
||||||
|
} else {
|
||||||
if (state.IsInvalid()) {
|
if (state.IsInvalid()) {
|
||||||
if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
|
if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
|
||||||
result_0.pushKV("reject-reason", "missing-inputs");
|
result_0.pushKV("reject-reason", "missing-inputs");
|
||||||
|
|
|
@ -475,6 +475,7 @@ public:
|
||||||
*/
|
*/
|
||||||
std::vector<COutPoint>& m_coins_to_uncache;
|
std::vector<COutPoint>& m_coins_to_uncache;
|
||||||
const bool m_test_accept;
|
const bool m_test_accept;
|
||||||
|
CAmount* m_fee_out;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Single transaction acceptance
|
// Single transaction acceptance
|
||||||
|
@ -687,6 +688,11 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
||||||
return false; // state filled in by CheckTxInputs
|
return false; // state filled in by CheckTxInputs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If fee_out is passed, return the fee to the caller
|
||||||
|
if (args.m_fee_out) {
|
||||||
|
*args.m_fee_out = nFees;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for non-standard pay-to-script-hash in inputs
|
// Check for non-standard pay-to-script-hash in inputs
|
||||||
if (fRequireStandard && !AreInputsStandard(tx, m_view)) {
|
if (fRequireStandard && !AreInputsStandard(tx, m_view)) {
|
||||||
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "bad-txns-nonstandard-inputs");
|
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "bad-txns-nonstandard-inputs");
|
||||||
|
@ -1061,10 +1067,10 @@ bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs
|
||||||
/** (try to) add transaction to memory pool with a specified acceptance time **/
|
/** (try to) add transaction to memory pool with a specified acceptance time **/
|
||||||
static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
|
static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
|
||||||
int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
|
int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
|
||||||
bool bypass_limits, const CAmount nAbsurdFee, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
bool bypass_limits, const CAmount nAbsurdFee, bool test_accept, CAmount* fee_out=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
||||||
{
|
{
|
||||||
std::vector<COutPoint> coins_to_uncache;
|
std::vector<COutPoint> coins_to_uncache;
|
||||||
MemPoolAccept::ATMPArgs args { chainparams, state, nAcceptTime, plTxnReplaced, bypass_limits, nAbsurdFee, coins_to_uncache, test_accept };
|
MemPoolAccept::ATMPArgs args { chainparams, state, nAcceptTime, plTxnReplaced, bypass_limits, nAbsurdFee, coins_to_uncache, test_accept, fee_out };
|
||||||
bool res = MemPoolAccept(pool).AcceptSingleTransaction(tx, args);
|
bool res = MemPoolAccept(pool).AcceptSingleTransaction(tx, args);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
// Remove coins that were not present in the coins cache before calling ATMPW;
|
// Remove coins that were not present in the coins cache before calling ATMPW;
|
||||||
|
@ -1083,10 +1089,10 @@ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPo
|
||||||
|
|
||||||
bool AcceptToMemoryPool(CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
|
bool AcceptToMemoryPool(CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
|
||||||
std::list<CTransactionRef>* plTxnReplaced,
|
std::list<CTransactionRef>* plTxnReplaced,
|
||||||
bool bypass_limits, const CAmount nAbsurdFee, bool test_accept)
|
bool bypass_limits, const CAmount nAbsurdFee, bool test_accept, CAmount* fee_out)
|
||||||
{
|
{
|
||||||
const CChainParams& chainparams = Params();
|
const CChainParams& chainparams = Params();
|
||||||
return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, GetTime(), plTxnReplaced, bypass_limits, nAbsurdFee, test_accept);
|
return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, GetTime(), plTxnReplaced, bypass_limits, nAbsurdFee, test_accept, fee_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock)
|
CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock)
|
||||||
|
|
|
@ -199,10 +199,11 @@ void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune);
|
||||||
void PruneBlockFilesManual(int nManualPruneHeight);
|
void PruneBlockFilesManual(int nManualPruneHeight);
|
||||||
|
|
||||||
/** (try to) add transaction to memory pool
|
/** (try to) add transaction to memory pool
|
||||||
* plTxnReplaced will be appended to with all transactions replaced from mempool **/
|
* plTxnReplaced will be appended to with all transactions replaced from mempool
|
||||||
|
* @param[out] fee_out optional argument to return tx fee to the caller **/
|
||||||
bool AcceptToMemoryPool(CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
|
bool AcceptToMemoryPool(CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
|
||||||
std::list<CTransactionRef>* plTxnReplaced,
|
std::list<CTransactionRef>* plTxnReplaced,
|
||||||
bool bypass_limits, const CAmount nAbsurdFee, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
bool bypass_limits, const CAmount nAbsurdFee, bool test_accept=false, CAmount* fee_out=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||||
|
|
||||||
/** Get the BIP9 state for a given deployment at the current tip. */
|
/** Get the BIP9 state for a given deployment at the current tip. */
|
||||||
ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos);
|
ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos);
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
"""Test mempool acceptance of raw transactions."""
|
"""Test mempool acceptance of raw transactions."""
|
||||||
|
|
||||||
|
from decimal import Decimal
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
import math
|
import math
|
||||||
|
|
||||||
|
@ -91,20 +92,22 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
|
||||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0)))
|
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0)))
|
||||||
txid_0 = tx.rehash()
|
txid_0 = tx.rehash()
|
||||||
self.check_mempool_result(
|
self.check_mempool_result(
|
||||||
result_expected=[{'txid': txid_0, 'allowed': True}],
|
result_expected=[{'txid': txid_0, 'allowed': True, 'vsize': 110, 'fees': {'base': Decimal(str(fee))}}],
|
||||||
rawtxs=[raw_tx_0],
|
rawtxs=[raw_tx_0],
|
||||||
)
|
)
|
||||||
|
|
||||||
self.log.info('A final transaction not in the mempool')
|
self.log.info('A final transaction not in the mempool')
|
||||||
coin = coins.pop() # Pick a random coin(base) to spend
|
coin = coins.pop() # Pick a random coin(base) to spend
|
||||||
|
output_amount = 0.025
|
||||||
raw_tx_final = node.signrawtransactionwithwallet(node.createrawtransaction(
|
raw_tx_final = node.signrawtransactionwithwallet(node.createrawtransaction(
|
||||||
inputs=[{'txid': coin['txid'], 'vout': coin['vout'], "sequence": 0xffffffff}], # SEQUENCE_FINAL
|
inputs=[{'txid': coin['txid'], 'vout': coin['vout'], "sequence": 0xffffffff}], # SEQUENCE_FINAL
|
||||||
outputs=[{node.getnewaddress(): 0.025}],
|
outputs=[{node.getnewaddress(): output_amount}],
|
||||||
locktime=node.getblockcount() + 2000, # Can be anything
|
locktime=node.getblockcount() + 2000, # Can be anything
|
||||||
))['hex']
|
))['hex']
|
||||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_final)))
|
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_final)))
|
||||||
|
fee_expected = int(coin['amount']) - output_amount
|
||||||
self.check_mempool_result(
|
self.check_mempool_result(
|
||||||
result_expected=[{'txid': tx.rehash(), 'allowed': True}],
|
result_expected=[{'txid': tx.rehash(), 'allowed': True, 'vsize': 188, 'fees': {'base': Decimal(str(fee_expected))}}],
|
||||||
rawtxs=[tx.serialize().hex()],
|
rawtxs=[tx.serialize().hex()],
|
||||||
maxfeerate=0,
|
maxfeerate=0,
|
||||||
)
|
)
|
||||||
|
@ -127,7 +130,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
|
||||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0)))
|
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0)))
|
||||||
txid_0 = tx.rehash()
|
txid_0 = tx.rehash()
|
||||||
self.check_mempool_result(
|
self.check_mempool_result(
|
||||||
result_expected=[{'txid': txid_0, 'allowed': True}],
|
result_expected=[{'txid': txid_0, 'allowed': True, 'vsize': 110, 'fees': {'base': Decimal(str(2 * fee))}}],
|
||||||
rawtxs=[raw_tx_0],
|
rawtxs=[raw_tx_0],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -187,7 +190,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
|
||||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
|
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
|
||||||
# Reference tx should be valid on itself
|
# Reference tx should be valid on itself
|
||||||
self.check_mempool_result(
|
self.check_mempool_result(
|
||||||
result_expected=[{'txid': tx.rehash(), 'allowed': True}],
|
result_expected=[{'txid': tx.rehash(), 'allowed': True, 'vsize': 110, 'fees': { 'base': Decimal(str(0.1 - 0.05))}}],
|
||||||
rawtxs=[tx.serialize().hex()],
|
rawtxs=[tx.serialize().hex()],
|
||||||
maxfeerate=0,
|
maxfeerate=0,
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
"""Test segwit transactions and blocks on P2P network."""
|
"""Test segwit transactions and blocks on P2P network."""
|
||||||
|
from decimal import Decimal
|
||||||
import math
|
import math
|
||||||
import random
|
import random
|
||||||
import struct
|
import struct
|
||||||
|
@ -695,13 +696,13 @@ class SegWitTest(BitcoinTestFramework):
|
||||||
if not self.segwit_active:
|
if not self.segwit_active:
|
||||||
# Just check mempool acceptance, but don't add the transaction to the mempool, since witness is disallowed
|
# Just check mempool acceptance, but don't add the transaction to the mempool, since witness is disallowed
|
||||||
# in blocks and the tx is impossible to mine right now.
|
# in blocks and the tx is impossible to mine right now.
|
||||||
assert_equal(self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()]), [{'txid': tx3.hash, 'allowed': True}])
|
assert_equal(self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()]), [{'txid': tx3.hash, 'allowed': True, 'vsize': 93, 'fees': { 'base': Decimal('0.00001000')}}])
|
||||||
# Create the same output as tx3, but by replacing tx
|
# Create the same output as tx3, but by replacing tx
|
||||||
tx3_out = tx3.vout[0]
|
tx3_out = tx3.vout[0]
|
||||||
tx3 = tx
|
tx3 = tx
|
||||||
tx3.vout = [tx3_out]
|
tx3.vout = [tx3_out]
|
||||||
tx3.rehash()
|
tx3.rehash()
|
||||||
assert_equal(self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()]), [{'txid': tx3.hash, 'allowed': True}])
|
assert_equal(self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()]), [{'txid': tx3.hash, 'allowed': True, 'vsize': 93, 'fees': { 'base': Decimal('0.00011000')}}])
|
||||||
test_transaction_acceptance(self.nodes[0], self.test_node, tx3, with_witness=True, accepted=True)
|
test_transaction_acceptance(self.nodes[0], self.test_node, tx3, with_witness=True, accepted=True)
|
||||||
|
|
||||||
self.nodes[0].generate(1)
|
self.nodes[0].generate(1)
|
||||||
|
|
Loading…
Add table
Reference in a new issue