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

backport: Merge bitcoin/bitcoin#23418, 25144,25480,25492,24836 #6519

Draft
wants to merge 5 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/llmq/dkgsessionhandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ bool CDKGPendingMessages::HasSeen(const uint256& hash) const
void CDKGPendingMessages::Misbehaving(const NodeId from, const int score, PeerManager& peerman)
{
if (from == -1) return;
peerman.Misbehaving(from, score);
peerman.UnitTestMisbehaving(from, score);
}

void CDKGPendingMessages::Clear()
Expand Down
2 changes: 1 addition & 1 deletion src/llmq/instantsend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -938,7 +938,7 @@ std::unordered_set<uint256, StaticSaltedHasher> CInstantSendManager::ProcessPend
for (const auto& nodeId : batchVerifier.badSources) {
// Let's not be too harsh, as the peer might simply be unlucky and might have sent us an old lock which
// does not validate anymore due to changed quorums
peerman.Misbehaving(nodeId, 20);
peerman.UnitTestMisbehaving(nodeId, 20);
}
}
for (const auto& p : pend) {
Expand Down
2 changes: 1 addition & 1 deletion src/llmq/signing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ bool CSigningManager::ProcessPendingRecoveredSigs(PeerManager& peerman)

if (batchVerifier.badSources.count(nodeId)) {
LogPrint(BCLog::LLMQ, "CSigningManager::%s -- invalid recSig from other node, banning peer=%d\n", __func__, nodeId);
peerman.Misbehaving(nodeId, 100);
peerman.UnitTestMisbehaving(nodeId, 100);
continue;
}

Expand Down
2 changes: 1 addition & 1 deletion src/llmq/signing_shares.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1439,7 +1439,7 @@ void CSigSharesManager::BanNode(NodeId nodeId, PeerManager& peerman)
return;
}

peerman.Misbehaving(nodeId, 100);
peerman.UnitTestMisbehaving(nodeId, 100);

LOCK(cs);
auto it = nodeStates.find(nodeId);
Expand Down
106 changes: 61 additions & 45 deletions src/net_processing.cpp

Large diffs are not rendered by default.

6 changes: 2 additions & 4 deletions src/net_processing.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,8 @@ class PeerManager : public CValidationInterface, public NetEventsInterface
/** Set the best height */
virtual void SetBestHeight(int height) = 0;

/**
* Increment peer's misbehavior score. If the new value surpasses DISCOURAGEMENT_THRESHOLD (specified on startup or by default), mark node to be discouraged, meaning the peer might be disconnected & added to the discouragement filter.
*/
virtual void Misbehaving(const NodeId pnode, const int howmuch, const std::string& message = "") = 0;
/* Public for unit testing. */
virtual void UnitTestMisbehaving(NodeId peer_id, int howmuch) = 0;

/**
* Evict extra outbound peers. If we think our tip may be stale, connect to an extra outbound.
Expand Down
1 change: 1 addition & 0 deletions src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "sendrawtransaction", 3, "bypasslimits" },
{ "testmempoolaccept", 0, "rawtxs" },
{ "testmempoolaccept", 1, "maxfeerate" },
{ "submitpackage", 0, "package" },
{ "combinerawtransaction", 0, "txs" },
{ "fundrawtransaction", 1, "options" },
{ "walletcreatefundedpsbt", 0, "inputs" },
Expand Down
146 changes: 146 additions & 0 deletions src/rpc/mempool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <rpc/blockchain.h>

#include <chainparams.h>
#include <core_io.h>
#include <fs.h>
#include <policy/settings.h>
Expand Down Expand Up @@ -465,6 +466,150 @@ RPCHelpMan savemempool()
};
}

static RPCHelpMan submitpackage()
{
return RPCHelpMan{"submitpackage",
"Submit a package of raw transactions (serialized, hex-encoded) to local node (-regtest only).\n"
"The package will be validated according to consensus and mempool policy rules. If all transactions pass, they will be accepted to mempool.\n"
"This RPC is experimental and the interface may be unstable. Refer to doc/policy/packages.md for documentation on package policies.\n"
"Warning: until package relay is in use, successful submission does not mean the transaction will propagate to other nodes on the network.\n"
"Currently, each transaction is broadcasted individually after submission, which means they must meet other nodes' feerate requirements alone.\n"
,
{
{"package", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of raw transactions.",
{
{"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
},
},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::OBJ_DYN, "tx-results", "transaction results keyed by wtxid",
{
{RPCResult::Type::OBJ, "wtxid", "transaction wtxid", {
{RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
{RPCResult::Type::STR_HEX, "other-wtxid", /*optional=*/true, "The wtxid of a different transaction with the same txid but different witness found in the mempool. This means the submitted transaction was ignored."},
{RPCResult::Type::NUM, "vsize", "Virtual transaction size as defined in BIP 141."},
{RPCResult::Type::OBJ, "fees", "Transaction fees", {
{RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT},
}},
}}
}},
{RPCResult::Type::STR_AMOUNT, "package-feerate", /*optional=*/true, "package feerate used for feerate checks in " + CURRENCY_UNIT + " per KvB. Excludes transactions which were deduplicated or accepted individually."},
{RPCResult::Type::ARR, "replaced-transactions", /*optional=*/true, "List of txids of replaced transactions",
{
{RPCResult::Type::STR_HEX, "", "The transaction id"},
}},
},
},
RPCExamples{
HelpExampleCli("testmempoolaccept", "[rawtx1, rawtx2]") +
HelpExampleCli("submitpackage", "[rawtx1, rawtx2]")
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
if (!Params().IsMockableChain()) {
throw std::runtime_error("submitpackage is for regression testing (-regtest mode) only");
}
RPCTypeCheck(request.params, {
UniValue::VARR,
});
const UniValue raw_transactions = request.params[0].get_array();
if (raw_transactions.size() < 1 || raw_transactions.size() > MAX_PACKAGE_COUNT) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Array must contain between 1 and " + ToString(MAX_PACKAGE_COUNT) + " transactions.");
}

std::vector<CTransactionRef> txns;
txns.reserve(raw_transactions.size());
for (const auto& rawtx : raw_transactions.getValues()) {
CMutableTransaction mtx;
if (!DecodeHexTx(mtx, rawtx.get_str())) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
"TX decode failed: " + rawtx.get_str() + " Make sure the tx has at least one input.");
}
txns.emplace_back(MakeTransactionRef(std::move(mtx)));
}

NodeContext& node = EnsureAnyNodeContext(request.context);
CTxMemPool& mempool = EnsureMemPool(node);
CChainState& chainstate = EnsureChainman(node).ActiveChainstate();
const auto package_result = WITH_LOCK(::cs_main, return ProcessNewPackage(chainstate, mempool, txns, /*test_accept=*/ false));

// First catch any errors.
switch(package_result.m_state.GetResult()) {
case PackageValidationResult::PCKG_RESULT_UNSET: break;
case PackageValidationResult::PCKG_POLICY:
{
throw JSONRPCTransactionError(TransactionError::INVALID_PACKAGE,
package_result.m_state.GetRejectReason());
}
case PackageValidationResult::PCKG_MEMPOOL_ERROR:
{
throw JSONRPCTransactionError(TransactionError::MEMPOOL_ERROR,
package_result.m_state.GetRejectReason());
}
case PackageValidationResult::PCKG_TX:
{
for (const auto& tx : txns) {
auto it = package_result.m_tx_results.find(tx->GetWitnessHash());
if (it != package_result.m_tx_results.end() && it->second.m_state.IsInvalid()) {
throw JSONRPCTransactionError(TransactionError::MEMPOOL_REJECTED,
strprintf("%s failed: %s", tx->GetHash().ToString(), it->second.m_state.GetRejectReason()));
}
}
// If a PCKG_TX error was returned, there must have been an invalid transaction.
NONFATAL_UNREACHABLE();
}
}
for (const auto& tx : txns) {
size_t num_submitted{0};
std::string err_string;
const auto err = BroadcastTransaction(node, tx, err_string, 0, true, true);
if (err != TransactionError::OK) {
throw JSONRPCTransactionError(err,
strprintf("transaction broadcast failed: %s (all transactions were submitted, %d transactions were broadcast successfully)",
err_string, num_submitted));
}
}
UniValue rpc_result{UniValue::VOBJ};
UniValue tx_result_map{UniValue::VOBJ};
std::set<uint256> replaced_txids;
for (const auto& tx : txns) {
auto it = package_result.m_tx_results.find(tx->GetWitnessHash());
CHECK_NONFATAL(it != package_result.m_tx_results.end());
UniValue result_inner{UniValue::VOBJ};
result_inner.pushKV("txid", tx->GetHash().GetHex());
if (it->second.m_result_type == MempoolAcceptResult::ResultType::DIFFERENT_WITNESS) {
result_inner.pushKV("other-wtxid", it->second.m_other_wtxid.value().GetHex());
}
if (it->second.m_result_type == MempoolAcceptResult::ResultType::VALID ||
it->second.m_result_type == MempoolAcceptResult::ResultType::MEMPOOL_ENTRY) {
result_inner.pushKV("vsize", int64_t{it->second.m_vsize.value()});
UniValue fees(UniValue::VOBJ);
fees.pushKV("base", ValueFromAmount(it->second.m_base_fees.value()));
result_inner.pushKV("fees", fees);
if (it->second.m_replaced_transactions.has_value()) {
for (const auto& ptx : it->second.m_replaced_transactions.value()) {
replaced_txids.insert(ptx->GetHash());
}
}
}
tx_result_map.pushKV(tx->GetWitnessHash().GetHex(), result_inner);
}
rpc_result.pushKV("tx-results", tx_result_map);
if (package_result.m_package_feerate.has_value()) {
rpc_result.pushKV("package-feerate", ValueFromAmount(package_result.m_package_feerate.value().GetFeePerK()));
}
UniValue replaced_list(UniValue::VARR);
for (const uint256& hash : replaced_txids) replaced_list.push_back(hash.ToString());
rpc_result.pushKV("replaced-transactions", replaced_list);
return rpc_result;
},
};
}

void RegisterMempoolRPCCommands(CRPCTable& t)
{
static const CRPCCommand commands[]{
Expand All @@ -476,6 +621,7 @@ void RegisterMempoolRPCCommands(CRPCTable& t)
{"blockchain", &getmempoolinfo},
{"blockchain", &getrawmempool},
{"blockchain", &savemempool},
{"hidden", &submitpackage},
};
for (const auto& c : commands) {
t.appendCommand(c.name, &c);
Expand Down
6 changes: 3 additions & 3 deletions src/rpc/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,13 +222,13 @@ static RPCHelpMan getpeerinfo()
obj.pushKV("conntime", count_seconds(stats.m_connected));
obj.pushKV("timeoffset", stats.nTimeOffset);
if (stats.m_last_ping_time > 0us) {
obj.pushKV("pingtime", CountSecondsDouble(stats.m_last_ping_time));
obj.pushKV("pingtime", Ticks<SecondsDouble>(stats.m_last_ping_time));
}
if (stats.m_min_ping_time < std::chrono::microseconds::max()) {
obj.pushKV("minping", CountSecondsDouble(stats.m_min_ping_time));
obj.pushKV("minping", Ticks<SecondsDouble>(stats.m_min_ping_time));
}
if (fStateStats && statestats.m_ping_wait > 0s) {
obj.pushKV("pingwait", CountSecondsDouble(statestats.m_ping_wait));
obj.pushKV("pingwait", Ticks<SecondsDouble>(statestats.m_ping_wait));
}
obj.pushKV("version", stats.nVersion);
// Use the sanitized form of subver here, to avoid tricksy remote peers from
Expand Down
10 changes: 5 additions & 5 deletions src/test/denialofservice_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
peerLogic->InitializeNode(*nodes[0], NODE_NETWORK);
nodes[0]->fSuccessfullyConnected = true;
connman->AddTestNode(*nodes[0]);
peerLogic->Misbehaving(nodes[0]->GetId(), DISCOURAGEMENT_THRESHOLD); // Should be discouraged
peerLogic->UnitTestMisbehaving(nodes[0]->GetId(), DISCOURAGEMENT_THRESHOLD); // Should be discouraged
BOOST_CHECK(peerLogic->SendMessages(nodes[0]));
BOOST_CHECK(banman->IsDiscouraged(addr[0]));
BOOST_CHECK(nodes[0]->fDisconnect);
Expand All @@ -370,15 +370,15 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
peerLogic->InitializeNode(*nodes[1], NODE_NETWORK);
nodes[1]->fSuccessfullyConnected = true;
connman->AddTestNode(*nodes[1]);
peerLogic->Misbehaving(nodes[1]->GetId(), DISCOURAGEMENT_THRESHOLD - 1);
peerLogic->UnitTestMisbehaving(nodes[1]->GetId(), DISCOURAGEMENT_THRESHOLD - 1);
BOOST_CHECK(peerLogic->SendMessages(nodes[1]));
// [0] is still discouraged/disconnected.
BOOST_CHECK(banman->IsDiscouraged(addr[0]));
BOOST_CHECK(nodes[0]->fDisconnect);
// [1] is not discouraged/disconnected yet.
BOOST_CHECK(!banman->IsDiscouraged(addr[1]));
BOOST_CHECK(!nodes[1]->fDisconnect);
peerLogic->Misbehaving(nodes[1]->GetId(), 1); // [1] reaches discouragement threshold
peerLogic->UnitTestMisbehaving(nodes[1]->GetId(), 1); // [1] reaches discouragement threshold
BOOST_CHECK(peerLogic->SendMessages(nodes[1]));
// Expect both [0] and [1] to be discouraged/disconnected now.
BOOST_CHECK(banman->IsDiscouraged(addr[0]));
Expand All @@ -401,7 +401,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
peerLogic->InitializeNode(*nodes[2], NODE_NETWORK);
nodes[2]->fSuccessfullyConnected = true;
connman->AddTestNode(*nodes[2]);
peerLogic->Misbehaving(nodes[2]->GetId(), DISCOURAGEMENT_THRESHOLD, /* message */ "");
peerLogic->UnitTestMisbehaving(nodes[2]->GetId(), DISCOURAGEMENT_THRESHOLD);
BOOST_CHECK(peerLogic->SendMessages(nodes[2]));
BOOST_CHECK(banman->IsDiscouraged(addr[0]));
BOOST_CHECK(banman->IsDiscouraged(addr[1]));
Expand Down Expand Up @@ -447,7 +447,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
peerLogic->InitializeNode(dummyNode, NODE_NETWORK);
dummyNode.fSuccessfullyConnected = true;

peerLogic->Misbehaving(dummyNode.GetId(), DISCOURAGEMENT_THRESHOLD);
peerLogic->UnitTestMisbehaving(dummyNode.GetId(), DISCOURAGEMENT_THRESHOLD);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode));
BOOST_CHECK(banman->IsDiscouraged(addr));
banman->ClearDiscouraged();
Expand Down
1 change: 1 addition & 0 deletions src/test/fuzz/rpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
"signrawtransactionwithkey",
"submitblock",
"submitheader",
"submitpackage",
"syncwithvalidationinterfacequeue",
"testmempoolaccept",
"uptime",
Expand Down
22 changes: 13 additions & 9 deletions src/txmempool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <reverse_iterator.h>
#include <util/check.h>
#include <util/moneystr.h>
#include <util/overflow.h>
#include <util/system.h>
#include <util/time.h>
#include <validationinterface.h>
Expand Down Expand Up @@ -66,7 +67,7 @@ struct update_fee_delta
{
explicit update_fee_delta(int64_t _feeDelta) : feeDelta(_feeDelta) { }

void operator() (CTxMemPoolEntry &e) { e.UpdateFeeDelta(feeDelta); }
void operator() (CTxMemPoolEntry &e) { e.UpdateModifiedFee(feeDelta); }

private:
int64_t feeDelta;
Expand Down Expand Up @@ -100,18 +101,19 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& tx, CAmount fee,
entryHeight{entry_height},
spendsCoinbase{spends_coinbase},
sigOpCount{sigops_count},
m_modified_fee{nFee},
lockPoints{lp},
nSizeWithDescendants{GetTxSize()},
nModFeesWithDescendants{nFee},
nSizeWithAncestors{GetTxSize()},
nModFeesWithAncestors{nFee},
nSigOpCountWithAncestors{sigOpCount} {}

void CTxMemPoolEntry::UpdateFeeDelta(int64_t newFeeDelta)
void CTxMemPoolEntry::UpdateModifiedFee(int64_t fee_diff)
{
nModFeesWithDescendants += newFeeDelta - feeDelta;
nModFeesWithAncestors += newFeeDelta - feeDelta;
feeDelta = newFeeDelta;
nModFeesWithDescendants = SaturatingAdd(nModFeesWithDescendants, fee_diff);
nModFeesWithAncestors = SaturatingAdd(nModFeesWithAncestors, fee_diff);
m_modified_fee = SaturatingAdd(m_modified_fee, fee_diff);
}

void CTxMemPoolEntry::UpdateLockPoints(const LockPoints& lp)
Expand Down Expand Up @@ -443,7 +445,7 @@ void CTxMemPoolEntry::UpdateDescendantState(int64_t modifySize, CAmount modifyFe
{
nSizeWithDescendants += modifySize;
assert(int64_t(nSizeWithDescendants) > 0);
nModFeesWithDescendants += modifyFee;
nModFeesWithDescendants = SaturatingAdd(nModFeesWithDescendants, modifyFee);
nCountWithDescendants += modifyCount;
assert(int64_t(nCountWithDescendants) > 0);
}
Expand All @@ -452,7 +454,7 @@ void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, CAmount modifyFee,
{
nSizeWithAncestors += modifySize;
assert(int64_t(nSizeWithAncestors) > 0);
nModFeesWithAncestors += modifyFee;
nModFeesWithAncestors = SaturatingAdd(nModFeesWithAncestors, modifyFee);
nCountWithAncestors += modifyCount;
assert(int64_t(nCountWithAncestors) > 0);
nSigOpCountWithAncestors += modifySigOps;
Expand Down Expand Up @@ -500,6 +502,8 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces
// Update transaction for any feeDelta created by PrioritiseTransaction
CAmount delta{0};
ApplyDelta(entry.GetTx().GetHash(), delta);
// The following call to UpdateModifiedFee assumes no previous fee modifications
Assume(entry.GetFee() == entry.GetModifiedFee());
if (delta) {
mapTx.modify(newit, update_fee_delta(delta));
}
Expand Down Expand Up @@ -1448,10 +1452,10 @@ void CTxMemPool::PrioritiseTransaction(const uint256& hash, const CAmount& nFeeD
{
LOCK(cs);
CAmount &delta = mapDeltas[hash];
delta += nFeeDelta;
delta = SaturatingAdd(delta, nFeeDelta);
txiter it = mapTx.find(hash);
if (it != mapTx.end()) {
mapTx.modify(it, update_fee_delta(delta));
mapTx.modify(it, update_fee_delta(nFeeDelta));
// Now update all ancestors' modified fees with descendants
setEntries setAncestors;
uint64_t nNoLimit = std::numeric_limits<uint64_t>::max();
Expand Down
Loading
Loading