Compare commits

..

7 Commits

Author SHA1 Message Date
Ed Hennis
e27b19d4d6 Merge branch 'develop' into ximinez/after-is-never-null 2026-06-05 18:48:46 -04:00
Ed Hennis
b364d8cf5e Merge branch 'develop' into ximinez/after-is-never-null 2026-06-04 13:32:27 -04:00
Ed Hennis
3f957d56ed Merge branch 'develop' into ximinez/after-is-never-null 2026-06-04 10:09:23 -04:00
Ed Hennis
231dc888aa Merge branch 'develop' into ximinez/after-is-never-null 2026-06-02 11:58:38 -04:00
Ed Hennis
3f52b71c89 Merge branch 'develop' into ximinez/after-is-never-null 2026-06-01 14:24:34 -04:00
Ed Hennis
4659ec0a7b Merge branch 'develop' into ximinez/after-is-never-null 2026-05-28 23:37:51 -04:00
Ed Hennis
88b81ee66e fix: Improve Invariant documentation to emphasize that "after" is never null
- Expand the description in InvariantChecker_PROTOTYPE::visitEntry.
- Add an explicit assertion in "XRPLNotCreated::visitEntry".
2026-05-28 23:34:13 -04:00
4 changed files with 64 additions and 68 deletions

View File

@@ -65,9 +65,13 @@ public:
/**
* @brief called for each ledger entry in the current transaction.
*
* @param isDelete true if the SLE is being deleted
* @param before ledger entry before modification by the transaction
* @param after ledger entry after modification by the transaction
* @param isDelete true if the SLE is being deleted.
* @param before ledger entry before modification by the
* transaction.
* @param after ledger entry after modification by the transaction.
* `after` IS NEVER NULL. `isDelete` is the only correct way to check for deletions.
* Check for null defensively, but do not make any logic decisions based on whether `after` is
* set, because it will always be set.
*/
void
visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after);

View File

@@ -135,24 +135,28 @@ XRPNotCreated::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref a
}
}
if (after)
if (!after)
{
switch (after->getType())
{
case ltACCOUNT_ROOT:
drops_ += (*after)[sfBalance].xrp().drops();
break;
case ltPAYCHAN:
if (!isDelete)
drops_ += ((*after)[sfAmount] - (*after)[sfBalance]).xrp().drops();
break;
case ltESCROW:
if (!isDelete && isXRP((*after)[sfAmount]))
drops_ += (*after)[sfAmount].xrp().drops();
break;
default:
break;
}
// LCOV_EXCL_START
UNREACHABLE("xrpl::XRPNotCreated::visitEntry : after can't be null");
return;
// LCOV_EXCL_STOP
}
switch (after->getType())
{
case ltACCOUNT_ROOT:
drops_ += (*after)[sfBalance].xrp().drops();
break;
case ltPAYCHAN:
if (!isDelete)
drops_ += ((*after)[sfAmount] - (*after)[sfBalance]).xrp().drops();
break;
case ltESCROW:
if (!isDelete && isXRP((*after)[sfAmount]))
drops_ += (*after)[sfAmount].xrp().drops();
break;
default:
break;
}
}

View File

@@ -61,7 +61,6 @@
#include <xrpl/server/Handoff.h>
#include <xrpl/server/LoadFeeTrack.h>
#include <xrpl/server/NetworkOPs.h>
#include <xrpl/shamap/SHAMap.h>
#include <xrpl/shamap/SHAMapNodeID.h>
#include <xrpl/tx/apply.h>
@@ -1494,11 +1493,23 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMGetLedger> const& m)
}
}
// Verify ledger node IDs. Full parsing is deferred to the job.
if (itype != protocol::liBASE && m->nodeids_size() <= 0)
// Verify ledger node IDs
if (itype != protocol::liBASE)
{
badData("Invalid ledger node IDs");
return;
if (m->nodeids_size() <= 0)
{
badData("Invalid ledger node IDs");
return;
}
for (auto const& nodeId : m->nodeids())
{
if (deserializeSHAMapNodeID(nodeId) == std::nullopt)
{
badData("Invalid SHAMap node ID");
return;
}
}
}
// Verify query type
@@ -1518,32 +1529,11 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMGetLedger> const& m)
}
}
// Queue a job to process the request. Full parsing of the node IDs is
// performed inside the job so the I/O thread is not burdened with
// SHAMapNodeID deserialization for every TMGetLedger message.
// Queue a job to process the request
std::weak_ptr<PeerImp> const weak = shared_from_this();
app_.getJobQueue().addJob(JtLedgerReq, "RcvGetLedger", [weak, m, itype]() {
auto peer = weak.lock();
if (!peer)
return;
std::vector<SHAMapNodeID> nodeIDs;
if (itype != protocol::liBASE)
{
nodeIDs.reserve(m->nodeids_size());
for (auto const& nodeId : m->nodeids())
{
auto parsed = deserializeSHAMapNodeID(nodeId);
if (!parsed)
{
peer->charge(Resource::kFeeInvalidData, "get_ledger invalid node ID");
return;
}
nodeIDs.push_back(std::move(*parsed));
}
}
peer->processLedgerRequest(m, std::move(nodeIDs));
app_.getJobQueue().addJob(JtLedgerReq, "RcvGetLedger", [weak, m]() {
if (auto peer = weak.lock())
peer->processLedgerRequest(m);
});
}
@@ -3252,9 +3242,7 @@ PeerImp::getTxSet(std::shared_ptr<protocol::TMGetLedger> const& m) const
}
void
PeerImp::processLedgerRequest(
std::shared_ptr<protocol::TMGetLedger> const& m,
std::vector<SHAMapNodeID> nodeIDs)
PeerImp::processLedgerRequest(std::shared_ptr<protocol::TMGetLedger> const& m)
{
// Do not resource charge a peer responding to a relay
if (!m->has_requestcookie())
@@ -3339,23 +3327,26 @@ PeerImp::processLedgerRequest(
}
// Add requested node data to reply
if (!nodeIDs.empty())
if (m->nodeids_size() > 0)
{
std::uint32_t const defaultDepth = isHighLatency() ? 2 : 1;
auto const queryDepth{m->has_querydepth() ? m->querydepth() : defaultDepth};
std::vector<std::pair<SHAMapNodeID, Blob>> data;
data.reserve(Tuning::kSoftMaxReplyNodes);
for (auto const& nodeID : nodeIDs)
for (int i = 0;
i < m->nodeids_size() && ledgerData.nodes_size() < Tuning::kSoftMaxReplyNodes;
++i)
{
if (ledgerData.nodes_size() >= Tuning::kSoftMaxReplyNodes)
break;
auto const shaMapNodeId{deserializeSHAMapNodeID(m->nodeids(i))};
data.clear();
data.reserve(Tuning::kSoftMaxReplyNodes);
try
{
if (map->getNodeFat(nodeID, data, fatLeaves, queryDepth))
// NOLINTNEXTLINE(bugprone-unchecked-optional-access) nodeids checked in onGetLedger
if (map->getNodeFat(*shaMapNodeId, data, fatLeaves, queryDepth))
{
JLOG(pJournal_.trace())
<< "processLedgerRequest: getNodeFat got " << data.size() << " nodes";
@@ -3364,9 +3355,9 @@ PeerImp::processLedgerRequest(
{
if (ledgerData.nodes_size() >= Tuning::kHardMaxReplyNodes)
break;
protocol::TMLedgerNode* ledgerNode{ledgerData.add_nodes()};
ledgerNode->set_nodeid(d.first.getRawString());
ledgerNode->set_nodedata(d.second.data(), d.second.size());
protocol::TMLedgerNode* node{ledgerData.add_nodes()};
node->set_nodeid(d.first.getRawString());
node->set_nodedata(d.second.data(), d.second.size());
}
}
else
@@ -3405,12 +3396,12 @@ PeerImp::processLedgerRequest(
info += ", no hash specified";
JLOG(pJournal_.warn())
<< "processLedgerRequest: getNodeFat with nodeId " << nodeID
<< "processLedgerRequest: getNodeFat with nodeId " << *shaMapNodeId
<< " and ledger info type " << info << " throws exception: " << e.what();
}
}
JLOG(pJournal_.info()) << "processLedgerRequest: Got request for " << nodeIDs.size()
JLOG(pJournal_.info()) << "processLedgerRequest: Got request for " << m->nodeids_size()
<< " nodes at depth " << queryDepth << ", return "
<< ledgerData.nodes_size() << " nodes";
}

View File

@@ -14,7 +14,6 @@
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/STValidation.h>
#include <xrpl/resource/Fees.h>
#include <xrpl/shamap/SHAMapNodeID.h>
#include <boost/circular_buffer.hpp>
#include <boost/endian/conversion.hpp>
@@ -624,9 +623,7 @@ private:
getTxSet(std::shared_ptr<protocol::TMGetLedger> const& m) const;
void
processLedgerRequest(
std::shared_ptr<protocol::TMGetLedger> const& m,
std::vector<SHAMapNodeID> nodeIDs);
processLedgerRequest(std::shared_ptr<protocol::TMGetLedger> const& m);
};
//------------------------------------------------------------------------------