Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RPC: minetxlocally #21372

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/node/interfaces.cpp
Expand Up @@ -538,7 +538,7 @@ class ChainImpl : public Chain
bool relay,
std::string& err_string) override
{
const TransactionError err = BroadcastTransaction(m_node, tx, err_string, max_tx_fee, relay, /*wait_callback*/ false);
const TransactionError err = BroadcastTransaction(m_node, tx, err_string, max_tx_fee, relay, /*bypass_policy*/ false, /*wait_callback*/ false);
// Chain clients only care about failures to accept the tx to the mempool. Disregard non-mempool related failures.
// Note: this will need to be updated if BroadcastTransactions() is updated to return other non-mempool failures
// that Chain clients do not need to know about.
Expand Down
6 changes: 3 additions & 3 deletions src/node/transaction.cpp
Expand Up @@ -26,7 +26,7 @@ static TransactionError HandleATMPError(const TxValidationState& state, std::str
}
}

TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef tx, std::string& err_string, const CAmount& max_tx_fee, bool relay, bool wait_callback)
TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef tx, std::string& err_string, const CAmount& max_tx_fee, bool relay, bool bypass_policy, bool wait_callback)
{
// BroadcastTransaction can be called by either sendrawtransaction RPC or wallet RPCs.
// node.connman is assigned both before chain clients and before RPC server is accepting calls,
Expand Down Expand Up @@ -54,7 +54,7 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
// First, call ATMP with test_accept and check the fee. If ATMP
// fails here, return error immediately.
const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), *node.mempool, tx, false /* bypass_limits */,
true /* test_accept */);
bypass_policy, true /* test_accept */);
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
return HandleATMPError(result.m_state, err_string);
} else if (result.m_base_fees.value() > max_tx_fee) {
Expand All @@ -63,7 +63,7 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
}
// Try to submit the transaction to the mempool.
const MempoolAcceptResult result = AcceptToMemoryPool(::ChainstateActive(), *node.mempool, tx, false /* bypass_limits */,
false /* test_accept */);
bypass_policy, false /* test_accept */);
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
return HandleATMPError(result.m_state, err_string);
}
Expand Down
3 changes: 2 additions & 1 deletion src/node/transaction.h
Expand Up @@ -33,9 +33,10 @@ static const CFeeRate DEFAULT_MAX_RAW_TX_FEE_RATE{COIN / 10};
* @param[out] err_string reference to std::string to fill with error string if available
* @param[in] max_tx_fee reject txs with fees higher than this (if 0, accept any fee)
* @param[in] relay flag if both mempool insertion and p2p relay are requested
* @param[in] bypass_policy disable transaction standardness checks
* @param[in] wait_callback wait until callbacks have been processed to avoid stale result due to a sequentially RPC.
* return error
*/
[[nodiscard]] TransactionError BroadcastTransaction(NodeContext& node, CTransactionRef tx, std::string& err_string, const CAmount& max_tx_fee, bool relay, bool wait_callback);
[[nodiscard]] TransactionError BroadcastTransaction(NodeContext& node, CTransactionRef tx, std::string& err_string, const CAmount& max_tx_fee, bool relay, bool bypass_policy, bool wait_callback);

#endif // BITCOIN_NODE_TRANSACTION_H
2 changes: 1 addition & 1 deletion src/qt/psbtoperationsdialog.cpp
Expand Up @@ -102,7 +102,7 @@ void PSBTOperationsDialog::broadcastTransaction()
CTransactionRef tx = MakeTransactionRef(mtx);
std::string err_string;
TransactionError error = BroadcastTransaction(
*m_client_model->node().context(), tx, err_string, DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK(), /* relay */ true, /* await_callback */ false);
*m_client_model->node().context(), tx, err_string, DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK(), /* relay */ true, /* bypass_policy */ false, /* await_callback */ false);

if (error == TransactionError::OK) {
showStatus(tr("Transaction broadcast successfully! Transaction ID: %1")
Expand Down
1 change: 1 addition & 0 deletions src/rpc/client.cpp
Expand Up @@ -66,6 +66,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "listtransactions", 3, "include_watchonly" },
{ "walletpassphrase", 1, "timeout" },
{ "getblocktemplate", 0, "template_request" },
{ "minetxlocally", 1, "fee_delta" },
{ "listsinceblock", 1, "target_confirmations" },
{ "listsinceblock", 2, "include_watchonly" },
{ "listsinceblock", 3, "include_removed" },
Expand Down
51 changes: 51 additions & 0 deletions src/rpc/mining.cpp
Expand Up @@ -15,6 +15,7 @@
#include <net.h>
#include <node/context.h>
#include <policy/fees.h>
#include <policy/policy.h>
#include <pow.h>
#include <rpc/blockchain.h>
#include <rpc/mining.h>
Expand Down Expand Up @@ -473,6 +474,55 @@ static RPCHelpMan prioritisetransaction()
};
}

static RPCHelpMan minetxlocally()
{
return RPCHelpMan{"minetxlocally",
"\nInclude a raw transaction in the next block without relaying it.\n",
{
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
{"fee_delta", RPCArg::Type::NUM, RPCArg::Optional::NO, "The fee value (in satoshis) to add (or subtract, if negative).\n"
" Note, that this value is not a fee rate. It is a value to modify absolute fee of the TX.\n"
" The fee is not actually paid, only the algorithm for selecting transactions into a block\n"
" considers the transaction as it would have paid a higher (or lower) fee."},
},
RPCResult{
RPCResult::Type::STR_HEX, "", "The transaction hash in hex"
},
RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
RPCTypeCheck(request.params, {
UniValue::VSTR,
UniValue::VNUM,
});

CAmount delta = request.params[1].get_int64();

CMutableTransaction mtx;
if (!DecodeHexTx(mtx, request.params[0].get_str())) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input.");
}
CTransactionRef tx(MakeTransactionRef(std::move(mtx)));

const CFeeRate max_raw_tx_fee_rate = DEFAULT_MAX_RAW_TX_FEE_RATE;

int64_t virtual_size = GetVirtualTransactionSize(*tx);
CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size);

std::string err_string;
AssertLockNotHeld(cs_main);
NodeContext& node = EnsureNodeContext(request.context);
const TransactionError err = BroadcastTransaction(node, tx, err_string, max_raw_tx_fee, /*relay*/ false, /*bypass_policy*/ true, /*wait_callback*/ true);
if (TransactionError::OK != err) {
throw JSONRPCTransactionError(err, err_string);
}

EnsureMemPool(request.context).PrioritiseTransaction(tx->GetHash(), delta);

return tx->GetHash().GetHex();
},
};
}

// NOTE: Assumes a conclusive result; if result is inconclusive, it must be handled by caller
static UniValue BIP22ValidationResult(const BlockValidationState& state)
Expand Down Expand Up @@ -1238,6 +1288,7 @@ static const CRPCCommand commands[] =
{ "mining", &getmininginfo, },
{ "mining", &prioritisetransaction, },
{ "mining", &getblocktemplate, },
{ "mining", &minetxlocally, },
{ "mining", &submitblock, },
{ "mining", &submitheader, },

Expand Down
5 changes: 3 additions & 2 deletions src/rpc/rawtransaction.cpp
Expand Up @@ -863,7 +863,7 @@ static RPCHelpMan sendrawtransaction()
std::string err_string;
AssertLockNotHeld(cs_main);
NodeContext& node = EnsureNodeContext(request.context);
const TransactionError err = BroadcastTransaction(node, tx, err_string, max_raw_tx_fee, /*relay*/ true, /*wait_callback*/ true);
const TransactionError err = BroadcastTransaction(node, tx, err_string, max_raw_tx_fee, /*relay*/ true, /*bypass_policy*/ false, /*wait_callback*/ true);
if (TransactionError::OK != err) {
throw JSONRPCTransactionError(err, err_string);
}
Expand Down Expand Up @@ -947,7 +947,8 @@ static RPCHelpMan testmempoolaccept()
result_0.pushKV("wtxid", tx->GetWitnessHash().GetHex());

const MempoolAcceptResult accept_result = WITH_LOCK(cs_main, return AcceptToMemoryPool(::ChainstateActive(), mempool, std::move(tx),
false /* bypass_limits */, /* test_accept */ true));
false /* bypass_limits */, false /* bypass_policy*/,
true /* test_accept */));

// Only return the fee and vsize if the transaction would pass ATMP.
// These can be used to calculate the feerate.
Expand Down
16 changes: 10 additions & 6 deletions src/validation.cpp
Expand Up @@ -484,6 +484,7 @@ class MemPoolAccept
const CChainParams& m_chainparams;
const int64_t m_accept_time;
const bool m_bypass_limits;
const bool m_bypass_policy;
/*
* Return any outpoints which were not previously present in the coins
* cache, but were added as a result of validating the tx for mempool
Expand Down Expand Up @@ -1068,7 +1069,9 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
// checks pass, to mitigate CPU exhaustion denial-of-service attacks.
PrecomputedTransactionData txdata;

if (!PolicyScriptChecks(args, ws, txdata)) return MempoolAcceptResult(ws.m_state);
if (!args.m_bypass_policy) {
if (!PolicyScriptChecks(args, ws, txdata)) return MempoolAcceptResult(ws.m_state);
}

if (!ConsensusScriptChecks(args, ws, txdata)) return MempoolAcceptResult(ws.m_state);

Expand All @@ -1090,11 +1093,11 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
static MempoolAcceptResult AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool,
CChainState& active_chainstate,
const CTransactionRef &tx, int64_t nAcceptTime,
bool bypass_limits, bool test_accept)
bool bypass_limits, bool bypass_policy, bool test_accept)
EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
std::vector<COutPoint> coins_to_uncache;
MemPoolAccept::ATMPArgs args { chainparams, nAcceptTime, bypass_limits, coins_to_uncache, test_accept };
MemPoolAccept::ATMPArgs args { chainparams, nAcceptTime, bypass_limits, bypass_policy, coins_to_uncache, test_accept };

assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
const MempoolAcceptResult result = MemPoolAccept(pool, active_chainstate).AcceptSingleTransaction(tx, args);
Expand All @@ -1114,10 +1117,10 @@ static MempoolAcceptResult AcceptToMemoryPoolWithTime(const CChainParams& chainp
}

MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPool& pool, const CTransactionRef& tx,
bool bypass_limits, bool test_accept)
bool bypass_limits, bool bypass_policy, bool test_accept)
{
assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
return AcceptToMemoryPoolWithTime(Params(), pool, active_chainstate, tx, GetTime(), bypass_limits, test_accept);
return AcceptToMemoryPoolWithTime(Params(), pool, active_chainstate, tx, GetTime(), bypass_limits, bypass_policy, test_accept);
}

CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock)
Expand Down Expand Up @@ -5051,7 +5054,8 @@ bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate)
if (nTime > nNow - nExpiryTimeout) {
LOCK(cs_main);
assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate));
if (AcceptToMemoryPoolWithTime(chainparams, pool, active_chainstate, tx, nTime, false /* bypass_limits */,
if (AcceptToMemoryPoolWithTime(chainparams, pool, active_chainstate, tx, nTime,
false /* bypass_limits */, false /*bypass_policy*/,
false /* test_accept */).m_result_type == MempoolAcceptResult::ResultType::VALID) {
++count;
} else {
Expand Down
4 changes: 3 additions & 1 deletion src/validation.h
Expand Up @@ -221,10 +221,12 @@ struct MempoolAcceptResult {
/**
* (Try to) add a transaction to the memory pool.
* @param[in] bypass_limits When true, don't enforce mempool fee limits.
* @param[in] bypass_policy When true, disable standardness checks.
* @param[in] test_accept When true, run validation checks but don't submit to mempool.
*/
MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPool& pool, const CTransactionRef& tx,
bool bypass_limits, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool bypass_limits, bool bypass_policy=false, bool test_accept=false)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);


/** Apply the effects of this transaction on the UTXO set represented by view */
Expand Down