From bb0a62e0255ec24d19e68806e9b14abb684e695f Mon Sep 17 00:00:00 2001 From: Bart <11445373+bthomee@users.noreply.github.com> Date: Sat, 1 Aug 2026 21:34:05 -0400 Subject: [PATCH] refactor: Add `SHAMapNodeID::isPrefixOf` A node at depth d names the tree path spelled by the first d nibbles of its key, so any leaf beneath it must agree on that prefix. Three call sites open-coded this check against `depthMask`, and a fourth rebuilt a whole node ID with `createID` just to compare it back. No behavior change: each converted site tests the same condition it did before. --- include/xrpl/shamap/SHAMapNodeID.h | 14 ++++++++++++++ src/libxrpl/shamap/SHAMapNodeID.cpp | 11 ++++++++--- src/libxrpl/shamap/SHAMapSync.cpp | 7 +++---- src/xrpld/app/ledger/detail/LedgerNodeHelpers.cpp | 5 ++--- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/include/xrpl/shamap/SHAMapNodeID.h b/include/xrpl/shamap/SHAMapNodeID.h index 9fbfca90ceb..37337b73765 100644 --- a/include/xrpl/shamap/SHAMapNodeID.h +++ b/include/xrpl/shamap/SHAMapNodeID.h @@ -54,6 +54,20 @@ class SHAMapNodeID : public CountedObject [[nodiscard]] SHAMapNodeID getChildNodeID(unsigned int branch) const; + /** + * Test whether this node ID lies on the path to the given leaf key + * + * A node at depth d identifies the tree path spelled by the first d + * nibbles of its key, so any leaf beneath it must agree on that prefix. + * A node ID that fails this test names a different subtree than the one + * it was built for. + * + * @param key the key of a leaf below this node + * @return whether this node ID is a prefix of the leaf key + */ + [[nodiscard]] bool + isPrefixOf(uint256 const& key) const; + /** * Create a SHAMapNodeID of a node with the depth of the node and * the key of a leaf diff --git a/src/libxrpl/shamap/SHAMapNodeID.cpp b/src/libxrpl/shamap/SHAMapNodeID.cpp index ecde22a63d5..8fd7afe8fc1 100644 --- a/src/libxrpl/shamap/SHAMapNodeID.cpp +++ b/src/libxrpl/shamap/SHAMapNodeID.cpp @@ -46,8 +46,7 @@ SHAMapNodeID::SHAMapNodeID(unsigned int depth, uint256 const& hash) : id_(hash), XRPL_ASSERT( depth <= SHAMap::kLeafDepth, "xrpl::SHAMapNodeID::SHAMapNodeID : maximum depth input"); XRPL_ASSERT( - id_ == (id_ & depthMask(depth)), - "xrpl::SHAMapNodeID::SHAMapNodeID : hash and depth inputs do match"); + isPrefixOf(id_), "xrpl::SHAMapNodeID::SHAMapNodeID : hash and depth inputs do match"); } std::string @@ -79,7 +78,7 @@ SHAMapNodeID::getChildNodeID(unsigned int branch) const if (depth_ >= SHAMap::kLeafDepth) Throw("Request for child node ID of " + to_string(*this)); - if (id_ != (id_ & depthMask(depth_))) + if (!isPrefixOf(id_)) Throw("Incorrect mask for " + to_string(*this)); SHAMapNodeID node{depth_ + 1, id_}; @@ -87,6 +86,12 @@ SHAMapNodeID::getChildNodeID(unsigned int branch) const return node; } +bool +SHAMapNodeID::isPrefixOf(uint256 const& key) const +{ + return (key & depthMask(depth_)) == id_; +} + [[nodiscard]] std::optional deserializeSHAMapNodeID(void const* data, std::size_t size) { diff --git a/src/libxrpl/shamap/SHAMapSync.cpp b/src/libxrpl/shamap/SHAMapSync.cpp index e6948ec3ac2..a12e524a5f4 100644 --- a/src/libxrpl/shamap/SHAMapSync.cpp +++ b/src/libxrpl/shamap/SHAMapSync.cpp @@ -555,10 +555,9 @@ SHAMap::addKnownNode( { XRPL_ASSERT(!nodeID.isRoot(), "xrpl::SHAMap::addKnownNode : valid node"); XRPL_ASSERT(treeNode, "xrpl::SHAMap::addKnownNode : non-null tree node"); - XRPL_ASSERT( - !treeNode->isLeaf() || - SHAMapNodeID::createID(nodeID.getDepth(), leafKey(*treeNode)).getNodeID() == - nodeID.getNodeID(), + XRPL_ASSERT_IF( + treeNode->isLeaf(), + nodeID.isPrefixOf(leafKey(*treeNode)), "xrpl::SHAMap::addKnownNode : leaf position consistent with node ID"); if (!isSynching()) diff --git a/src/xrpld/app/ledger/detail/LedgerNodeHelpers.cpp b/src/xrpld/app/ledger/detail/LedgerNodeHelpers.cpp index abd669d4461..230c802022e 100644 --- a/src/xrpld/app/ledger/detail/LedgerNodeHelpers.cpp +++ b/src/xrpld/app/ledger/detail/LedgerNodeHelpers.cpp @@ -75,11 +75,10 @@ getSHAMapNodeID(protocol::TMLedgerNode const& ledgerNode, SHAMapTreeNode const& if (treeNode.isLeaf()) { auto const key = leafKey(treeNode); - auto const expectedID = SHAMapNodeID::createID(nodeID->getDepth(), key); SOMETIMES( - nodeID->getNodeID() != expectedID.getNodeID(), + !nodeID->isPrefixOf(key), "xrpl::getSHAMapNodeID : legacy leaf ID inconsistent with key"); - if (nodeID->getNodeID() != expectedID.getNodeID()) + if (!nodeID->isPrefixOf(key)) return std::nullopt; }