mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
Simplify SHAMapNodeID:
The existing SHAMapNodeID object has both a valid and an invalid state and requirs callers to verify the state of an instance prior to using it. A simple set of changes removes that restriction and ensures that all instances are valid, making the code more robust. This change also: 1. Introduces a new function to construct a SHAMapNodeID from a serialized blob; and 2. Reduces the amount of constructors the class exposes.
This commit is contained in:
@@ -947,14 +947,20 @@ InboundLedger::receiveNode(protocol::TMLedgerData& packet, SHAMapAddNode& san)
|
||||
{
|
||||
for (auto const& node : packet.nodes())
|
||||
{
|
||||
SHAMapNodeID const nodeID(
|
||||
node.nodeid().data(), node.nodeid().size());
|
||||
if (nodeID.isRoot())
|
||||
auto const nodeID = deserializeSHAMapNodeID(node.nodeid());
|
||||
|
||||
if (!nodeID)
|
||||
{
|
||||
san.incInvalid();
|
||||
return;
|
||||
}
|
||||
|
||||
if (nodeID->isRoot())
|
||||
san += map.addRootNode(
|
||||
rootHash, makeSlice(node.nodedata()), filter.get());
|
||||
else
|
||||
san += map.addKnownNode(
|
||||
nodeID, makeSlice(node.nodedata()), filter.get());
|
||||
*nodeID, makeSlice(node.nodedata()), filter.get());
|
||||
|
||||
if (!san.isGood())
|
||||
{
|
||||
|
||||
@@ -159,15 +159,21 @@ public:
|
||||
std::list<Blob> nodeData;
|
||||
for (auto const& node : packet.nodes())
|
||||
{
|
||||
if (!node.has_nodeid() || !node.has_nodedata() ||
|
||||
(node.nodeid().size() != 33))
|
||||
if (!node.has_nodeid() || !node.has_nodedata())
|
||||
{
|
||||
peer->charge(Resource::feeInvalidRequest);
|
||||
return;
|
||||
}
|
||||
|
||||
nodeIDs.emplace_back(
|
||||
node.nodeid().data(), static_cast<int>(node.nodeid().size()));
|
||||
auto const id = deserializeSHAMapNodeID(node.nodeid());
|
||||
|
||||
if (!id)
|
||||
{
|
||||
peer->charge(Resource::feeBadData);
|
||||
return;
|
||||
}
|
||||
|
||||
nodeIDs.emplace_back(*id);
|
||||
nodeData.emplace_back(
|
||||
node.nodedata().begin(), node.nodedata().end());
|
||||
}
|
||||
|
||||
@@ -2794,12 +2794,12 @@ PeerImp::getLedger(std::shared_ptr<protocol::TMGetLedger> const& m)
|
||||
(reply.nodes().size() < Tuning::maxReplyNodes));
|
||||
++i)
|
||||
{
|
||||
SHAMapNodeID mn(packet.nodeids(i).data(), packet.nodeids(i).size());
|
||||
auto const mn = deserializeSHAMapNodeID(packet.nodeids(i));
|
||||
|
||||
if (!mn.isValid())
|
||||
if (!mn)
|
||||
{
|
||||
JLOG(p_journal_.warn()) << "GetLedger: Invalid node " << logMe;
|
||||
charge(Resource::feeInvalidRequest);
|
||||
charge(Resource::feeBadData);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2808,7 +2808,7 @@ PeerImp::getLedger(std::shared_ptr<protocol::TMGetLedger> const& m)
|
||||
|
||||
try
|
||||
{
|
||||
if (map->getNodeFat(mn, nodeIDs, rawNodes, fatLeaves, depth))
|
||||
if (map->getNodeFat(*mn, nodeIDs, rawNodes, fatLeaves, depth))
|
||||
{
|
||||
assert(nodeIDs.size() == rawNodes.size());
|
||||
JLOG(p_journal_.trace()) << "GetLedger: getNodeFat got "
|
||||
@@ -2821,10 +2821,8 @@ PeerImp::getLedger(std::shared_ptr<protocol::TMGetLedger> const& m)
|
||||
nodeIDIterator != nodeIDs.end();
|
||||
++nodeIDIterator, ++rawNodeIterator)
|
||||
{
|
||||
Serializer nID(33);
|
||||
nodeIDIterator->addIDRaw(nID);
|
||||
protocol::TMLedgerNode* node = reply.add_nodes();
|
||||
node->set_nodeid(nID.getDataPtr(), nID.getLength());
|
||||
node->set_nodeid(nodeIDIterator->getRawString());
|
||||
node->set_nodedata(
|
||||
&rawNodeIterator->front(), rawNodeIterator->size());
|
||||
}
|
||||
@@ -2852,7 +2850,7 @@ PeerImp::getLedger(std::shared_ptr<protocol::TMGetLedger> const& m)
|
||||
info += ", no hash specified";
|
||||
|
||||
JLOG(p_journal_.warn())
|
||||
<< "getNodeFat( " << mn << ") throws exception: " << info;
|
||||
<< "getNodeFat( " << *mn << ") throws exception: " << info;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -184,9 +184,9 @@ by 4 bits, and then packed in sequence into a `uint256` (such that the longest
|
||||
path possible has 256 / 4 = 64 steps). The high 4 bits of the first byte
|
||||
identify which child of the root is chosen, the lower 4 bits of the first byte
|
||||
identify the child of that node, and so on. The `SHAMapNodeID` identifying the
|
||||
root node has an ID of 0 and a depth of 0. See `SHAMapNodeID::selectBranch` for
|
||||
details of how a `SHAMapNodeID` selects a "branch" (child) by indexing into its
|
||||
path with its depth.
|
||||
root node has an ID of 0 and a depth of 0. See `selectBranch` for details of
|
||||
how we use a `SHAMapNodeID` to select a "branch" (child) by indexing into a
|
||||
path at a given depth.
|
||||
|
||||
While the current node is an inner node, traversing down the trie from the root
|
||||
continues, unless the path indicates a child that does not exist. And in this
|
||||
|
||||
@@ -29,8 +29,6 @@
|
||||
#include <ripple/shamap/SHAMapAddNode.h>
|
||||
#include <ripple/shamap/SHAMapItem.h>
|
||||
#include <ripple/shamap/SHAMapMissingNode.h>
|
||||
#include <ripple/shamap/SHAMapNodeID.h>
|
||||
#include <ripple/shamap/SHAMapSyncFilter.h>
|
||||
#include <ripple/shamap/SHAMapTreeNode.h>
|
||||
#include <ripple/shamap/TreeNodeCache.h>
|
||||
#include <cassert>
|
||||
@@ -39,18 +37,35 @@
|
||||
|
||||
namespace ripple {
|
||||
|
||||
enum class SHAMapState {
|
||||
Modifying = 0, // Objects can be added and removed (like an open ledger)
|
||||
Immutable = 1, // Map cannot be changed (like a closed ledger)
|
||||
Synching = 2, // Map's hash is locked in, valid nodes can be added (like a
|
||||
// peer's closing ledger)
|
||||
Floating = 3, // Map is free to change hash (like a synching open ledger)
|
||||
Invalid =
|
||||
4, // Map is known not to be valid (usually synching a corrupt ledger)
|
||||
};
|
||||
class SHAMapNodeID;
|
||||
class SHAMapSyncFilter;
|
||||
|
||||
/** Function object which handles missing nodes. */
|
||||
using MissingNodeHandler = std::function<void(std::uint32_t refNum)>;
|
||||
/** Describes the current state of a given SHAMap */
|
||||
enum class SHAMapState {
|
||||
/** The map is in flux and objects can be added and removed.
|
||||
|
||||
Example: map underlying the open ledger.
|
||||
*/
|
||||
Modifying = 0,
|
||||
|
||||
/** The map is set in stone and cannot be changed.
|
||||
|
||||
Example: a map underlying a given closed ledger.
|
||||
*/
|
||||
Immutable = 1,
|
||||
|
||||
/** The map's hash is fixed but valid nodes may be missing and can be added.
|
||||
|
||||
Example: a map that's syncing a given peer's closing ledger.
|
||||
*/
|
||||
Synching = 2,
|
||||
|
||||
/** The map is known to not be valid.
|
||||
|
||||
Example: usually synching a corrupt ledger.
|
||||
*/
|
||||
Invalid = 3,
|
||||
};
|
||||
|
||||
/** A SHAMap is both a radix tree with a fan-out of 16 and a Merkle tree.
|
||||
|
||||
@@ -61,11 +76,11 @@ using MissingNodeHandler = std::function<void(std::uint32_t refNum)>;
|
||||
2. A node with only one child is merged with that child
|
||||
(the "merge property")
|
||||
|
||||
These properties in a significantly smaller memory footprint for a radix
|
||||
tree.
|
||||
These properties result in a significantly smaller memory footprint for
|
||||
a radix tree.
|
||||
|
||||
And a fan-out of 16 means that each node in the tree has at most 16
|
||||
children. See https://en.wikipedia.org/wiki/Radix_tree
|
||||
A fan-out of 16 means that each node in the tree has at most 16
|
||||
children. See https://en.wikipedia.org/wiki/Radix_tree
|
||||
|
||||
A Merkle tree is a tree where each non-leaf node is labelled with the hash
|
||||
of the combined labels of its children nodes.
|
||||
@@ -89,6 +104,12 @@ private:
|
||||
bool full_ = false; // Map is believed complete in database
|
||||
|
||||
public:
|
||||
/** Each non-leaf node has 16 children (the 'radix tree' part of the map) */
|
||||
static inline constexpr unsigned int branchFactor = 16;
|
||||
|
||||
/** The depth of the hash map: data is only present in the leaves */
|
||||
static inline constexpr unsigned int leafDepth = 64;
|
||||
|
||||
using DeltaItem = std::pair<
|
||||
std::shared_ptr<SHAMapItem const>,
|
||||
std::shared_ptr<SHAMapItem const>>;
|
||||
@@ -499,8 +520,7 @@ SHAMap::setImmutable()
|
||||
inline bool
|
||||
SHAMap::isSynching() const
|
||||
{
|
||||
return (state_ == SHAMapState::Floating) ||
|
||||
(state_ == SHAMapState::Synching);
|
||||
return state_ == SHAMapState::Synching;
|
||||
}
|
||||
|
||||
inline void
|
||||
|
||||
@@ -21,160 +21,130 @@
|
||||
#define RIPPLE_SHAMAP_SHAMAPNODEID_H_INCLUDED
|
||||
|
||||
#include <ripple/basics/base_uint.h>
|
||||
#include <ripple/beast/utility/Journal.h>
|
||||
#include <ripple/protocol/Serializer.h>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// Identifies a node in a half-SHA512 (256 bit) hash map
|
||||
/** Identifies a node inside a SHAMap */
|
||||
class SHAMapNodeID
|
||||
{
|
||||
private:
|
||||
uint256 mNodeID;
|
||||
int mDepth;
|
||||
uint256 id_;
|
||||
unsigned int depth_ = 0;
|
||||
|
||||
public:
|
||||
SHAMapNodeID();
|
||||
SHAMapNodeID(int depth, uint256 const& hash);
|
||||
SHAMapNodeID(void const* ptr, int len);
|
||||
SHAMapNodeID() = default;
|
||||
SHAMapNodeID(SHAMapNodeID const& other) = default;
|
||||
SHAMapNodeID(unsigned int depth, uint256 const& hash);
|
||||
|
||||
SHAMapNodeID& operator=(SHAMapNodeID const& other) = default;
|
||||
|
||||
bool
|
||||
isValid() const;
|
||||
bool
|
||||
isRoot() const;
|
||||
isRoot() const
|
||||
{
|
||||
return depth_ == 0;
|
||||
}
|
||||
|
||||
// Convert to/from wire format (256-bit nodeID, 1-byte depth)
|
||||
void
|
||||
addIDRaw(Serializer& s) const;
|
||||
// Get the wire format (256-bit nodeID, 1-byte depth)
|
||||
std::string
|
||||
getRawString() const;
|
||||
|
||||
bool
|
||||
operator==(const SHAMapNodeID& n) const;
|
||||
bool
|
||||
operator!=(const SHAMapNodeID& n) const;
|
||||
|
||||
bool
|
||||
operator<(const SHAMapNodeID& n) const;
|
||||
bool
|
||||
operator>(const SHAMapNodeID& n) const;
|
||||
bool
|
||||
operator<=(const SHAMapNodeID& n) const;
|
||||
bool
|
||||
operator>=(const SHAMapNodeID& n) const;
|
||||
|
||||
std::string
|
||||
getString() const;
|
||||
void
|
||||
dump(beast::Journal journal) const;
|
||||
|
||||
// Only used by SHAMap and SHAMapTreeNode
|
||||
unsigned int
|
||||
getDepth() const
|
||||
{
|
||||
return depth_;
|
||||
}
|
||||
|
||||
uint256 const&
|
||||
getNodeID() const;
|
||||
getNodeID() const
|
||||
{
|
||||
return id_;
|
||||
}
|
||||
|
||||
SHAMapNodeID
|
||||
getChildNodeID(int m) const;
|
||||
int
|
||||
selectBranch(uint256 const& hash) const;
|
||||
int
|
||||
getDepth() const;
|
||||
getChildNodeID(unsigned int m) const;
|
||||
|
||||
// FIXME-C++20: use spaceship and operator synthesis
|
||||
/** Comparison operators */
|
||||
bool
|
||||
has_common_prefix(SHAMapNodeID const& other) const;
|
||||
operator<(SHAMapNodeID const& n) const
|
||||
{
|
||||
return std::tie(depth_, id_) < std::tie(n.depth_, n.id_);
|
||||
}
|
||||
|
||||
private:
|
||||
static uint256 const&
|
||||
Masks(int depth);
|
||||
bool
|
||||
operator>(SHAMapNodeID const& n) const
|
||||
{
|
||||
return n < *this;
|
||||
}
|
||||
|
||||
friend std::ostream&
|
||||
operator<<(std::ostream& out, SHAMapNodeID const& node);
|
||||
bool
|
||||
operator<=(SHAMapNodeID const& n) const
|
||||
{
|
||||
return !(n < *this);
|
||||
}
|
||||
|
||||
private: // Currently unused
|
||||
SHAMapNodeID
|
||||
getParentNodeID() const;
|
||||
bool
|
||||
operator>=(SHAMapNodeID const& n) const
|
||||
{
|
||||
return !(*this < n);
|
||||
}
|
||||
|
||||
bool
|
||||
operator==(SHAMapNodeID const& n) const
|
||||
{
|
||||
return (depth_ == n.depth_) && (id_ == n.id_);
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(SHAMapNodeID const& n) const
|
||||
{
|
||||
return !(*this == n);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
inline SHAMapNodeID::SHAMapNodeID() : mDepth(0)
|
||||
inline std::string
|
||||
to_string(SHAMapNodeID const& node)
|
||||
{
|
||||
}
|
||||
if (node.isRoot())
|
||||
return "NodeID(root)";
|
||||
|
||||
inline int
|
||||
SHAMapNodeID::getDepth() const
|
||||
{
|
||||
return mDepth;
|
||||
}
|
||||
|
||||
inline uint256 const&
|
||||
SHAMapNodeID::getNodeID() const
|
||||
{
|
||||
return mNodeID;
|
||||
}
|
||||
|
||||
inline bool
|
||||
SHAMapNodeID::isValid() const
|
||||
{
|
||||
return (mDepth >= 0) && (mDepth <= 64);
|
||||
}
|
||||
|
||||
inline bool
|
||||
SHAMapNodeID::isRoot() const
|
||||
{
|
||||
return mDepth == 0;
|
||||
}
|
||||
|
||||
inline SHAMapNodeID
|
||||
SHAMapNodeID::getParentNodeID() const
|
||||
{
|
||||
assert(mDepth);
|
||||
return SHAMapNodeID(mDepth - 1, mNodeID & Masks(mDepth - 1));
|
||||
}
|
||||
|
||||
inline bool
|
||||
SHAMapNodeID::operator<(const SHAMapNodeID& n) const
|
||||
{
|
||||
return std::tie(mDepth, mNodeID) < std::tie(n.mDepth, n.mNodeID);
|
||||
}
|
||||
|
||||
inline bool
|
||||
SHAMapNodeID::operator>(const SHAMapNodeID& n) const
|
||||
{
|
||||
return n < *this;
|
||||
}
|
||||
|
||||
inline bool
|
||||
SHAMapNodeID::operator<=(const SHAMapNodeID& n) const
|
||||
{
|
||||
return !(n < *this);
|
||||
}
|
||||
|
||||
inline bool
|
||||
SHAMapNodeID::operator>=(const SHAMapNodeID& n) const
|
||||
{
|
||||
return !(*this < n);
|
||||
}
|
||||
|
||||
inline bool
|
||||
SHAMapNodeID::operator==(const SHAMapNodeID& n) const
|
||||
{
|
||||
return (mDepth == n.mDepth) && (mNodeID == n.mNodeID);
|
||||
}
|
||||
|
||||
inline bool
|
||||
SHAMapNodeID::operator!=(const SHAMapNodeID& n) const
|
||||
{
|
||||
return !(*this == n);
|
||||
return "NodeID(" + std::to_string(node.getDepth()) + "," +
|
||||
to_string(node.getNodeID()) + ")";
|
||||
}
|
||||
|
||||
inline std::ostream&
|
||||
operator<<(std::ostream& out, SHAMapNodeID const& node)
|
||||
{
|
||||
return out << node.getString();
|
||||
return out << to_string(node);
|
||||
}
|
||||
|
||||
/** Return an object representing a serialized SHAMap Node ID
|
||||
*
|
||||
* @param s A string of bytes
|
||||
* @param data a non-null pointer to a buffer of @param size bytes.
|
||||
* @param size the size, in bytes, of the buffer pointed to by @param data.
|
||||
* @return A seated optional if the buffer contained a serialized SHAMap
|
||||
* node ID and an unseated optional otherwise.
|
||||
*/
|
||||
/** @{ */
|
||||
[[nodiscard]] std::optional<SHAMapNodeID>
|
||||
deserializeSHAMapNodeID(void const* data, std::size_t size);
|
||||
|
||||
[[nodiscard]] inline std::optional<SHAMapNodeID>
|
||||
deserializeSHAMapNodeID(std::string const& s)
|
||||
{
|
||||
return deserializeSHAMapNodeID(s.data(), s.size());
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** Returns the branch that would contain the given hash */
|
||||
[[nodiscard]] unsigned int
|
||||
selectBranch(SHAMapNodeID const& id, uint256 const& hash);
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
#include <ripple/basics/contract.h>
|
||||
#include <ripple/shamap/SHAMap.h>
|
||||
#include <ripple/shamap/SHAMapNodeID.h>
|
||||
#include <ripple/shamap/SHAMapSyncFilter.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -99,7 +101,7 @@ SHAMap::dirtyUp(
|
||||
stack.pop();
|
||||
assert(node != nullptr);
|
||||
|
||||
int branch = nodeID.selectBranch(target);
|
||||
int branch = selectBranch(nodeID, target);
|
||||
assert(branch >= 0);
|
||||
|
||||
node = unshareNode(std::move(node), nodeID);
|
||||
@@ -122,7 +124,7 @@ SHAMap::walkTowardsKey(uint256 const& id, SharedPtrNodeStack* stack) const
|
||||
stack->push({inNode, nodeID});
|
||||
|
||||
auto const inner = std::static_pointer_cast<SHAMapInnerNode>(inNode);
|
||||
auto const branch = nodeID.selectBranch(id);
|
||||
auto const branch = selectBranch(nodeID, id);
|
||||
if (inner->isEmptyBranch(branch))
|
||||
return nullptr;
|
||||
|
||||
@@ -333,7 +335,7 @@ SHAMap::descend(
|
||||
SHAMapSyncFilter* filter) const
|
||||
{
|
||||
assert(parent->isInner());
|
||||
assert((branch >= 0) && (branch < 16));
|
||||
assert((branch >= 0) && (branch < branchFactor));
|
||||
assert(!parent->isEmptyBranch(branch));
|
||||
|
||||
SHAMapAbstractNode* child = parent->getChildPointer(branch);
|
||||
@@ -428,7 +430,7 @@ SHAMap::firstBelow(
|
||||
if (node->isLeaf())
|
||||
{
|
||||
auto n = std::static_pointer_cast<SHAMapTreeNode>(node);
|
||||
stack.push({node, {64, n->peekItem()->key()}});
|
||||
stack.push({node, {leafDepth, n->peekItem()->key()}});
|
||||
return n.get();
|
||||
}
|
||||
auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
|
||||
@@ -436,7 +438,7 @@ SHAMap::firstBelow(
|
||||
stack.push({inner, SHAMapNodeID{}});
|
||||
else
|
||||
stack.push({inner, stack.top().second.getChildNodeID(branch)});
|
||||
for (int i = 0; i < 16;)
|
||||
for (int i = 0; i < branchFactor;)
|
||||
{
|
||||
if (!inner->isEmptyBranch(i))
|
||||
{
|
||||
@@ -445,7 +447,7 @@ SHAMap::firstBelow(
|
||||
if (node->isLeaf())
|
||||
{
|
||||
auto n = std::static_pointer_cast<SHAMapTreeNode>(node);
|
||||
stack.push({n, {64, n->peekItem()->key()}});
|
||||
stack.push({n, {leafDepth, n->peekItem()->key()}});
|
||||
return n.get();
|
||||
}
|
||||
inner = std::static_pointer_cast<SHAMapInnerNode>(node);
|
||||
@@ -469,7 +471,7 @@ SHAMap::onlyBelow(SHAMapAbstractNode* node) const
|
||||
{
|
||||
SHAMapAbstractNode* nextNode = nullptr;
|
||||
auto inner = static_cast<SHAMapInnerNode*>(node);
|
||||
for (int i = 0; i < 16; ++i)
|
||||
for (int i = 0; i < branchFactor; ++i)
|
||||
{
|
||||
if (!inner->isEmptyBranch(i))
|
||||
{
|
||||
@@ -522,7 +524,7 @@ SHAMap::peekNextItem(uint256 const& id, SharedPtrNodeStack& stack) const
|
||||
auto [node, nodeID] = stack.top();
|
||||
assert(!node->isLeaf());
|
||||
auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
|
||||
for (auto i = nodeID.selectBranch(id) + 1; i < 16; ++i)
|
||||
for (auto i = selectBranch(nodeID, id) + 1; i < branchFactor; ++i)
|
||||
{
|
||||
if (!inner->isEmptyBranch(i))
|
||||
{
|
||||
@@ -595,7 +597,8 @@ SHAMap::upper_bound(uint256 const& id) const
|
||||
else
|
||||
{
|
||||
auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
|
||||
for (auto branch = nodeID.selectBranch(id) + 1; branch < 16;
|
||||
for (auto branch = selectBranch(nodeID, id) + 1;
|
||||
branch < branchFactor;
|
||||
++branch)
|
||||
{
|
||||
if (!inner->isEmptyBranch(branch))
|
||||
@@ -654,7 +657,7 @@ SHAMap::delItem(uint256 const& id)
|
||||
stack.pop();
|
||||
|
||||
node = unshareNode(std::move(node), nodeID);
|
||||
node->setChild(nodeID.selectBranch(id), prevNode);
|
||||
node->setChild(selectBranch(nodeID, id), prevNode);
|
||||
|
||||
if (!nodeID.isRoot())
|
||||
{
|
||||
@@ -673,7 +676,7 @@ SHAMap::delItem(uint256 const& id)
|
||||
|
||||
if (item)
|
||||
{
|
||||
for (int i = 0; i < 16; ++i)
|
||||
for (int i = 0; i < branchFactor; ++i)
|
||||
{
|
||||
if (!node->isEmptyBranch(i))
|
||||
{
|
||||
@@ -735,7 +738,7 @@ SHAMap::addGiveItem(
|
||||
{
|
||||
// easy case, we end on an inner node
|
||||
auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
|
||||
int branch = nodeID.selectBranch(tag);
|
||||
int branch = selectBranch(nodeID, tag);
|
||||
assert(inner->isEmptyBranch(branch));
|
||||
auto newNode =
|
||||
std::make_shared<SHAMapTreeNode>(std::move(item), type, seq_);
|
||||
@@ -753,8 +756,8 @@ SHAMap::addGiveItem(
|
||||
|
||||
int b1, b2;
|
||||
|
||||
while ((b1 = nodeID.selectBranch(tag)) ==
|
||||
(b2 = nodeID.selectBranch(otherItem->key())))
|
||||
while ((b1 = selectBranch(nodeID, tag)) ==
|
||||
(b2 = selectBranch(nodeID, otherItem->key())))
|
||||
{
|
||||
stack.push({node, nodeID});
|
||||
|
||||
@@ -988,7 +991,7 @@ SHAMap::walkSubTree(bool doWrite, NodeObjectType t, std::uint32_t seq)
|
||||
// We can't flush an inner node until we flush its children
|
||||
while (1)
|
||||
{
|
||||
while (pos < 16)
|
||||
while (pos < branchFactor)
|
||||
{
|
||||
if (node->isEmptyBranch(pos))
|
||||
{
|
||||
@@ -1095,7 +1098,7 @@ SHAMap::dump(bool hash) const
|
||||
if (node->isInner())
|
||||
{
|
||||
auto inner = static_cast<SHAMapInnerNode*>(node);
|
||||
for (int i = 0; i < 16; ++i)
|
||||
for (int i = 0; i < branchFactor; ++i)
|
||||
{
|
||||
if (!inner->isEmptyBranch(i))
|
||||
{
|
||||
|
||||
@@ -17,18 +17,17 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/beast/core/LexicalCast.h>
|
||||
#include <ripple/crypto/csprng.h>
|
||||
#include <ripple/protocol/Serializer.h>
|
||||
#include <ripple/shamap/SHAMap.h>
|
||||
#include <ripple/shamap/SHAMapNodeID.h>
|
||||
#include <boost/format.hpp>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
uint256 const&
|
||||
SHAMapNodeID::Masks(int depth)
|
||||
static uint256 const&
|
||||
depthMask(unsigned int depth)
|
||||
{
|
||||
enum { mask_size = 65 };
|
||||
|
||||
@@ -49,106 +48,88 @@ SHAMapNodeID::Masks(int depth)
|
||||
entry[mask_size - 1] = selector;
|
||||
}
|
||||
};
|
||||
|
||||
static masks_t const masks;
|
||||
return masks.entry[depth];
|
||||
}
|
||||
|
||||
// canonicalize the hash to a node ID for this depth
|
||||
SHAMapNodeID::SHAMapNodeID(int depth, uint256 const& hash)
|
||||
: mNodeID(hash), mDepth(depth)
|
||||
SHAMapNodeID::SHAMapNodeID(unsigned int depth, uint256 const& hash)
|
||||
: id_(hash), depth_(depth)
|
||||
{
|
||||
assert((depth >= 0) && (depth < 65));
|
||||
assert(mNodeID == (mNodeID & Masks(depth)));
|
||||
}
|
||||
|
||||
SHAMapNodeID::SHAMapNodeID(void const* ptr, int len)
|
||||
{
|
||||
if (len < 33)
|
||||
mDepth = -1;
|
||||
else
|
||||
{
|
||||
std::memcpy(mNodeID.begin(), ptr, 32);
|
||||
mDepth = *(static_cast<unsigned char const*>(ptr) + 32);
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
SHAMapNodeID::getString() const
|
||||
{
|
||||
if ((mDepth == 0) && (mNodeID.isZero()))
|
||||
return "NodeID(root)";
|
||||
|
||||
return "NodeID(" + std::to_string(mDepth) + "," + to_string(mNodeID) + ")";
|
||||
}
|
||||
|
||||
void
|
||||
SHAMapNodeID::addIDRaw(Serializer& s) const
|
||||
{
|
||||
s.addBitString(mNodeID);
|
||||
s.add8(mDepth);
|
||||
assert(depth <= SHAMap::leafDepth);
|
||||
assert(id_ == (id_ & depthMask(depth)));
|
||||
}
|
||||
|
||||
std::string
|
||||
SHAMapNodeID::getRawString() const
|
||||
{
|
||||
Serializer s(33);
|
||||
addIDRaw(s);
|
||||
s.addBitString(id_);
|
||||
s.add8(depth_);
|
||||
return s.getString();
|
||||
}
|
||||
|
||||
// This can be optimized to avoid the << if needed
|
||||
SHAMapNodeID
|
||||
SHAMapNodeID::getChildNodeID(int m) const
|
||||
SHAMapNodeID::getChildNodeID(unsigned int m) const
|
||||
{
|
||||
assert((m >= 0) && (m < 16));
|
||||
assert(mDepth < 64);
|
||||
assert(m < SHAMap::branchFactor);
|
||||
|
||||
uint256 child(mNodeID);
|
||||
child.begin()[mDepth / 2] |= (mDepth & 1) ? m : (m << 4);
|
||||
// A SHAMap has exactly 65 levels, so nodes must not exceed that
|
||||
// depth; if they do, this breaks the invariant of never allowing
|
||||
// the construction of a SHAMapNodeID at an invalid depth. We assert
|
||||
// to catch this in debug builds.
|
||||
//
|
||||
// We throw (but never assert) if the node is at level 64, since
|
||||
// entries at that depth are leaf nodes and have no children and even
|
||||
// constructing a child node from them would break the above invariant.
|
||||
assert(depth_ <= SHAMap::leafDepth);
|
||||
|
||||
return SHAMapNodeID(mDepth + 1, child);
|
||||
if (depth_ >= SHAMap::leafDepth)
|
||||
Throw<std::logic_error>(
|
||||
"Request for child node ID of " + to_string(*this));
|
||||
|
||||
if (id_ != (id_ & depthMask(depth_)))
|
||||
Throw<std::logic_error>("Incorrect mask for " + to_string(*this));
|
||||
|
||||
SHAMapNodeID node{depth_ + 1, id_};
|
||||
node.id_.begin()[depth_ / 2] |= (depth_ & 1) ? m : (m << 4);
|
||||
return node;
|
||||
}
|
||||
|
||||
// Which branch would contain the specified hash
|
||||
int
|
||||
SHAMapNodeID::selectBranch(uint256 const& hash) const
|
||||
[[nodiscard]] std::optional<SHAMapNodeID>
|
||||
deserializeSHAMapNodeID(void const* data, std::size_t size)
|
||||
{
|
||||
int branch = *(hash.begin() + (mDepth / 2));
|
||||
std::optional<SHAMapNodeID> ret;
|
||||
|
||||
if (mDepth & 1)
|
||||
if (size == 33)
|
||||
{
|
||||
unsigned int depth = *(static_cast<unsigned char const*>(data) + 32);
|
||||
if (depth <= SHAMap::leafDepth)
|
||||
{
|
||||
auto const id = uint256::fromVoid(data);
|
||||
|
||||
if (id == (id & depthMask(depth)))
|
||||
ret.emplace(depth, id);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
[[nodiscard]] unsigned int
|
||||
selectBranch(SHAMapNodeID const& id, uint256 const& hash)
|
||||
{
|
||||
auto const depth = id.getDepth();
|
||||
auto branch = static_cast<unsigned int>(*(hash.begin() + (depth / 2)));
|
||||
|
||||
if (depth & 1)
|
||||
branch &= 0xf;
|
||||
else
|
||||
branch >>= 4;
|
||||
|
||||
assert((branch >= 0) && (branch < 16));
|
||||
|
||||
assert(branch < SHAMap::branchFactor);
|
||||
return branch;
|
||||
}
|
||||
|
||||
bool
|
||||
SHAMapNodeID::has_common_prefix(SHAMapNodeID const& other) const
|
||||
{
|
||||
assert(mDepth <= other.mDepth);
|
||||
auto x = mNodeID.begin();
|
||||
auto y = other.mNodeID.begin();
|
||||
for (unsigned i = 0; i < mDepth / 2; ++i, ++x, ++y)
|
||||
{
|
||||
if (*x != *y)
|
||||
return false;
|
||||
}
|
||||
if (mDepth & 1)
|
||||
{
|
||||
auto i = mDepth / 2;
|
||||
return (*(mNodeID.begin() + i) & 0xF0) ==
|
||||
(*(other.mNodeID.begin() + i) & 0xF0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
SHAMapNodeID::dump(beast::Journal journal) const
|
||||
{
|
||||
JLOG(journal.debug()) << getString();
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/basics/random.h>
|
||||
#include <ripple/nodestore/Database.h>
|
||||
#include <ripple/shamap/SHAMap.h>
|
||||
#include <ripple/shamap/SHAMapSyncFilter.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -459,7 +459,7 @@ SHAMap::getNodeFat(
|
||||
|
||||
while (node && node->isInner() && (nodeID.getDepth() < wanted.getDepth()))
|
||||
{
|
||||
int branch = nodeID.selectBranch(wanted.getNodeID());
|
||||
int branch = selectBranch(nodeID, wanted.getNodeID());
|
||||
auto inner = static_cast<SHAMapInnerNode*>(node);
|
||||
if (inner->isEmptyBranch(branch))
|
||||
return false;
|
||||
@@ -595,7 +595,6 @@ SHAMap::addKnownNode(
|
||||
Slice const& rawNode,
|
||||
SHAMapSyncFilter* filter)
|
||||
{
|
||||
// return value: true=okay, false=error
|
||||
assert(!node.isRoot());
|
||||
|
||||
if (!isSynching())
|
||||
@@ -613,7 +612,7 @@ SHAMap::addKnownNode(
|
||||
!static_cast<SHAMapInnerNode*>(iNode)->isFullBelow(generation) &&
|
||||
(iNodeID.getDepth() < node.getDepth()))
|
||||
{
|
||||
int branch = iNodeID.selectBranch(node.getNodeID());
|
||||
int branch = selectBranch(iNodeID, node.getNodeID());
|
||||
assert(branch >= 0);
|
||||
auto inner = static_cast<SHAMapInnerNode*>(iNode);
|
||||
if (inner->isEmptyBranch(branch))
|
||||
@@ -765,7 +764,7 @@ SHAMap::hasInnerNode(
|
||||
|
||||
while (node->isInner() && (nodeID.getDepth() < targetNodeID.getDepth()))
|
||||
{
|
||||
int branch = nodeID.selectBranch(targetNodeID.getNodeID());
|
||||
int branch = selectBranch(nodeID, targetNodeID.getNodeID());
|
||||
auto inner = static_cast<SHAMapInnerNode*>(node);
|
||||
if (inner->isEmptyBranch(branch))
|
||||
return false;
|
||||
@@ -790,7 +789,7 @@ SHAMap::hasLeafNode(uint256 const& tag, SHAMapHash const& targetNodeHash) const
|
||||
|
||||
do
|
||||
{
|
||||
int branch = nodeID.selectBranch(tag);
|
||||
int branch = selectBranch(nodeID, tag);
|
||||
auto inner = static_cast<SHAMapInnerNode*>(node);
|
||||
if (inner->isEmptyBranch(branch))
|
||||
return false; // Dead end, node must not be here
|
||||
|
||||
@@ -491,12 +491,7 @@ SHAMapInnerNode::getBranchCount() const
|
||||
std::string
|
||||
SHAMapAbstractNode::getString(const SHAMapNodeID& id) const
|
||||
{
|
||||
std::string ret = "NodeID(";
|
||||
ret += beast::lexicalCastThrow<std::string>(id.getDepth());
|
||||
ret += ",";
|
||||
ret += to_string(id.getNodeID());
|
||||
ret += ")";
|
||||
return ret;
|
||||
return to_string(id);
|
||||
}
|
||||
|
||||
std::string
|
||||
@@ -507,8 +502,8 @@ SHAMapInnerNode::getString(const SHAMapNodeID& id) const
|
||||
{
|
||||
if (!isEmptyBranch(i))
|
||||
{
|
||||
ret += "\nb";
|
||||
ret += beast::lexicalCastThrow<std::string>(i);
|
||||
ret += "\n";
|
||||
ret += std::to_string(i);
|
||||
ret += " = ";
|
||||
ret += to_string(mHashes[i]);
|
||||
}
|
||||
|
||||
@@ -249,7 +249,7 @@ public:
|
||||
uint256 const hash(ripple::sha512Half(123456789));
|
||||
getLedger->set_ledgerhash(hash.begin(), hash.size());
|
||||
getLedger->set_ledgerseq(123456789);
|
||||
ripple::SHAMapNodeID sha(hash.data(), hash.size());
|
||||
ripple::SHAMapNodeID sha(17, hash);
|
||||
getLedger->add_nodeids(sha.getRawString());
|
||||
getLedger->set_requestcookie(123456789);
|
||||
getLedger->set_querytype(protocol::qtINDIRECT);
|
||||
@@ -309,7 +309,7 @@ public:
|
||||
uint256 hash(ripple::sha512Half(i));
|
||||
auto object = getObject->add_objects();
|
||||
object->set_hash(hash.data(), hash.size());
|
||||
ripple::SHAMapNodeID sha(hash.data(), hash.size());
|
||||
ripple::SHAMapNodeID sha(i % 55, hash);
|
||||
object->set_nodeid(sha.getRawString());
|
||||
object->set_index("");
|
||||
object->set_data("");
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <ripple/beast/xor_shift_engine.h>
|
||||
#include <ripple/protocol/digest.h>
|
||||
#include <ripple/shamap/SHAMap.h>
|
||||
#include <ripple/shamap/SHAMapSyncFilter.h>
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
#include <test/shamap/common.h>
|
||||
|
||||
Reference in New Issue
Block a user