mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-07 02:36:47 +00:00
Optimize handling, support relaying to old node
This commit is contained in:
@@ -81,8 +81,8 @@ enum class SHAMapState {
|
||||
struct SHAMapNodeData
|
||||
{
|
||||
SHAMapNodeID nodeID;
|
||||
Blob data;
|
||||
bool isLeaf;
|
||||
Blob data; // Placed last, so `isLeaf` can fit into the alignment padding of `nodeID`.
|
||||
};
|
||||
|
||||
class SHAMap
|
||||
@@ -320,8 +320,9 @@ public:
|
||||
* @param filter Optional sync filter to track received nodes.
|
||||
* @return Status indicating whether the node was useful, duplicate, or invalid.
|
||||
*
|
||||
* @note This function expects that the caller has already validated that the nodeID is
|
||||
* consistent with the node's content.
|
||||
* @note This function expects the treeNode to be a valid, deserialized SHAMapTreeNode. The
|
||||
* caller is responsible for deserialization and basic validation before calling this
|
||||
* function. This also means that the nodeID must be consistent with the node's content.
|
||||
*/
|
||||
SHAMapAddNode
|
||||
addKnownNode(
|
||||
|
||||
@@ -130,8 +130,7 @@ SHAMapNodeID
|
||||
SHAMapNodeID::createID(int depth, uint256 const& key)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
depth >= 0 && depth <= SHAMap::kLeafDepth,
|
||||
"xrpl::SHAMapNodeID::createID : valid branch input");
|
||||
depth >= 0 && depth <= SHAMap::kLeafDepth, "xrpl::SHAMapNodeID::createID : valid depth");
|
||||
return SHAMapNodeID(depth, key & depthMask(depth));
|
||||
}
|
||||
|
||||
|
||||
@@ -449,17 +449,16 @@ SHAMap::getNodeFat(
|
||||
std::stack<std::tuple<SHAMapTreeNode*, SHAMapNodeID, int>> stack;
|
||||
stack.emplace(node, nodeID, depth);
|
||||
|
||||
Serializer s(8192);
|
||||
|
||||
while (!stack.empty())
|
||||
{
|
||||
std::tie(node, nodeID, depth) = stack.top();
|
||||
stack.pop();
|
||||
|
||||
// Add this node to the reply
|
||||
s.erase();
|
||||
// Use a fresh Serializer per node and move its buffer into `data` rather than copying it
|
||||
// via Serializer::getData(): the move is O(1) whereas the copy was O(node size).
|
||||
Serializer s(256);
|
||||
node->serializeForWire(s);
|
||||
data.emplace_back(nodeID, s.getData(), node->isLeaf());
|
||||
data.emplace_back(nodeID, node->isLeaf(), std::move(s.modData()));
|
||||
|
||||
if (node->isInner())
|
||||
{
|
||||
@@ -487,9 +486,10 @@ SHAMap::getNodeFat(
|
||||
else if (childNode->isInner() || fatLeaves)
|
||||
{
|
||||
// Just include this node
|
||||
s.erase();
|
||||
childNode->serializeForWire(s);
|
||||
data.emplace_back(childID, s.getData(), childNode->isLeaf());
|
||||
Serializer cs(256);
|
||||
childNode->serializeForWire(cs);
|
||||
data.emplace_back(
|
||||
childID, childNode->isLeaf(), std::move(cs.modData()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -518,8 +518,8 @@ SHAMap::addRootNode(
|
||||
// we already have a root_ node
|
||||
if (root_->getHash().isNonZero())
|
||||
{
|
||||
JLOG(journal_.trace()) << "got root node, already have one";
|
||||
XRPL_ASSERT(root_->getHash() == hash, "xrpl::SHAMap::addRootNode : valid hash input");
|
||||
JLOG(journal_.trace()) << "Got root node, already have one";
|
||||
XRPL_ASSERT(root_->getHash() == hash, "xrpl::SHAMap::addRootNode : valid hash");
|
||||
return SHAMapAddNode::duplicate();
|
||||
}
|
||||
|
||||
@@ -554,8 +554,15 @@ SHAMap::addKnownNode(
|
||||
SHAMapTreeNodePtr treeNode,
|
||||
SHAMapSyncFilter const* filter)
|
||||
{
|
||||
XRPL_ASSERT(!nodeID.isRoot(), "xrpl::SHAMap::addKnownNode : valid node input");
|
||||
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(),
|
||||
safeDowncast<SHAMapLeafNode const*>(treeNode.get())->peekItem()->key())
|
||||
.getNodeID() == nodeID.getNodeID(),
|
||||
"xrpl::SHAMap::addKnownNode : leaf position consistent with node ID");
|
||||
|
||||
if (!isSynching())
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <xrpld/app/ledger/detail/LedgerNodeHelpers.h>
|
||||
#include <xrpld/app/ledger/LedgerNodeHelpers.h>
|
||||
|
||||
#include <xrpl/basics/IntrusivePointer.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
@@ -41,131 +41,6 @@ class LedgerNodeHelpers_test : public beast::unit_test::Suite
|
||||
return std::string(std::bit_cast<char const*>(slice.data()), slice.size());
|
||||
}
|
||||
|
||||
void
|
||||
testValidateLedgerNode()
|
||||
{
|
||||
testcase("validateLedgerNode");
|
||||
|
||||
auto const validID = SHAMapNodeID::createID(3, uint256{}).getRawString();
|
||||
|
||||
// Invalid: missing all fields.
|
||||
{
|
||||
protocol::TMLedgerNode const node;
|
||||
BEAST_EXPECT(!validateLedgerNode(node));
|
||||
}
|
||||
|
||||
// Invalid: missing `nodedata` field.
|
||||
{
|
||||
protocol::TMLedgerNode node;
|
||||
node.set_nodeid(validID);
|
||||
BEAST_EXPECT(!validateLedgerNode(node));
|
||||
}
|
||||
|
||||
// Invalid: missing `nodedata` field.
|
||||
{
|
||||
protocol::TMLedgerNode node;
|
||||
node.set_id(validID);
|
||||
BEAST_EXPECT(!validateLedgerNode(node));
|
||||
}
|
||||
|
||||
// Invalid: missing `nodedata` field.
|
||||
{
|
||||
protocol::TMLedgerNode node;
|
||||
node.set_depth(1);
|
||||
BEAST_EXPECT(!validateLedgerNode(node));
|
||||
}
|
||||
|
||||
// Valid: legacy `nodeid` field.
|
||||
{
|
||||
protocol::TMLedgerNode node;
|
||||
node.set_nodedata("test_data");
|
||||
node.set_nodeid(validID);
|
||||
BEAST_EXPECT(validateLedgerNode(node));
|
||||
}
|
||||
|
||||
// Invalid: legacy `nodeid` field with garbage content.
|
||||
{
|
||||
protocol::TMLedgerNode node;
|
||||
node.set_nodedata("test_data");
|
||||
node.set_nodeid("garbage");
|
||||
BEAST_EXPECT(!validateLedgerNode(node));
|
||||
}
|
||||
|
||||
// Invalid: has both legacy `nodeid` and new `id` fields.
|
||||
{
|
||||
protocol::TMLedgerNode node;
|
||||
node.set_nodedata("test_data");
|
||||
node.set_nodeid(validID);
|
||||
node.set_id(validID);
|
||||
BEAST_EXPECT(!validateLedgerNode(node));
|
||||
}
|
||||
|
||||
// Invalid: has both legacy `nodeid` and new `depth` fields.
|
||||
{
|
||||
protocol::TMLedgerNode node;
|
||||
node.set_nodedata("test_data");
|
||||
node.set_nodeid(validID);
|
||||
node.set_depth(5);
|
||||
BEAST_EXPECT(!validateLedgerNode(node));
|
||||
}
|
||||
|
||||
// Valid: new `id` field.
|
||||
{
|
||||
protocol::TMLedgerNode node;
|
||||
node.set_nodedata("test_data");
|
||||
node.set_id(validID);
|
||||
BEAST_EXPECT(validateLedgerNode(node));
|
||||
}
|
||||
|
||||
// Invalid: new `id` field with garbage content.
|
||||
{
|
||||
protocol::TMLedgerNode node;
|
||||
node.set_nodedata("test_data");
|
||||
node.set_id("garbage");
|
||||
BEAST_EXPECT(!validateLedgerNode(node));
|
||||
}
|
||||
|
||||
// Valid: new `depth` field.
|
||||
{
|
||||
protocol::TMLedgerNode node;
|
||||
node.set_nodedata("test_data");
|
||||
node.set_depth(5);
|
||||
BEAST_EXPECT(validateLedgerNode(node));
|
||||
}
|
||||
|
||||
// Valid: `depth` at minimum depth.
|
||||
{
|
||||
protocol::TMLedgerNode node;
|
||||
node.set_nodedata("test_data");
|
||||
node.set_depth(0);
|
||||
BEAST_EXPECT(validateLedgerNode(node));
|
||||
}
|
||||
|
||||
// Valid: `depth` at arbitrary depth between minimum and maximum.
|
||||
{
|
||||
protocol::TMLedgerNode node;
|
||||
node.set_nodedata("test_data");
|
||||
node.set_depth(10);
|
||||
BEAST_EXPECT(validateLedgerNode(node));
|
||||
}
|
||||
|
||||
// Valid: `depth` at maximum depth.
|
||||
{
|
||||
protocol::TMLedgerNode node;
|
||||
node.set_nodedata("test_data");
|
||||
node.set_depth(SHAMap::kLeafDepth);
|
||||
BEAST_EXPECT(validateLedgerNode(node));
|
||||
}
|
||||
|
||||
// Invalid: `depth` is greater than maximum depth.
|
||||
{
|
||||
protocol::TMLedgerNode node;
|
||||
node.set_nodedata("test_data");
|
||||
node.set_depth(SHAMap::kLeafDepth + 1);
|
||||
BEAST_EXPECT(!validateLedgerNode(node));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testGetTreeNode()
|
||||
{
|
||||
@@ -232,10 +107,10 @@ class LedgerNodeHelpers_test : public beast::unit_test::Suite
|
||||
auto const innerDepth = 3;
|
||||
auto const innerID = SHAMapNodeID::createID(innerDepth, uint256{});
|
||||
|
||||
protocol::TMLedgerNode node;
|
||||
node.set_nodedata(innerData);
|
||||
node.set_nodeid(innerID.getRawString());
|
||||
auto const result = getSHAMapNodeID(node, *innerNode);
|
||||
protocol::TMLedgerNode ledgerNode;
|
||||
ledgerNode.set_nodedata(innerData);
|
||||
ledgerNode.set_nodeid(innerID.getRawString());
|
||||
auto const result = getSHAMapNodeID(ledgerNode, *innerNode);
|
||||
BEAST_EXPECT(result == innerID);
|
||||
}
|
||||
|
||||
@@ -244,19 +119,32 @@ class LedgerNodeHelpers_test : public beast::unit_test::Suite
|
||||
auto const innerDepth = 0;
|
||||
auto const innerID = SHAMapNodeID::createID(innerDepth, uint256{});
|
||||
|
||||
protocol::TMLedgerNode node;
|
||||
node.set_nodedata(innerData);
|
||||
node.set_id(innerID.getRawString());
|
||||
auto const result = getSHAMapNodeID(node, *innerNode);
|
||||
protocol::TMLedgerNode ledgerNode;
|
||||
ledgerNode.set_nodedata(innerData);
|
||||
ledgerNode.set_id(innerID.getRawString());
|
||||
auto const result = getSHAMapNodeID(ledgerNode, *innerNode);
|
||||
BEAST_EXPECT(result == innerID);
|
||||
}
|
||||
|
||||
// Invalid: new `depth` field should not be used for inner nodes.
|
||||
{
|
||||
protocol::TMLedgerNode node;
|
||||
node.set_nodedata(innerData);
|
||||
node.set_depth(10);
|
||||
auto const result = getSHAMapNodeID(node, *innerNode);
|
||||
protocol::TMLedgerNode ledgerNode;
|
||||
ledgerNode.set_nodedata(innerData);
|
||||
ledgerNode.set_depth(10);
|
||||
auto const result = getSHAMapNodeID(ledgerNode, *innerNode);
|
||||
BEAST_EXPECT(!result);
|
||||
}
|
||||
|
||||
// Invalid: both legacy `nodeid` and new `id` fields set for an inner node.
|
||||
{
|
||||
auto const innerDepth = 9;
|
||||
auto const innerID = SHAMapNodeID::createID(innerDepth, uint256{});
|
||||
|
||||
protocol::TMLedgerNode ledgerNode;
|
||||
ledgerNode.set_nodedata(innerData);
|
||||
ledgerNode.set_nodeid(innerID.getRawString());
|
||||
ledgerNode.set_id(innerID.getRawString());
|
||||
auto const result = getSHAMapNodeID(ledgerNode, *innerNode);
|
||||
BEAST_EXPECT(!result);
|
||||
}
|
||||
}
|
||||
@@ -297,10 +185,10 @@ class LedgerNodeHelpers_test : public beast::unit_test::Suite
|
||||
auto const kLeafDepth = 0;
|
||||
auto const leafID = SHAMapNodeID::createID(kLeafDepth, leafKey);
|
||||
|
||||
protocol::TMLedgerNode node;
|
||||
node.set_nodedata(leafData);
|
||||
node.set_depth(kLeafDepth);
|
||||
auto const result = getSHAMapNodeID(node, *leafNode);
|
||||
protocol::TMLedgerNode ledgerNode;
|
||||
ledgerNode.set_nodedata(leafData);
|
||||
ledgerNode.set_depth(kLeafDepth);
|
||||
auto const result = getSHAMapNodeID(ledgerNode, *leafNode);
|
||||
BEAST_EXPECT(result == leafID);
|
||||
}
|
||||
|
||||
@@ -324,10 +212,10 @@ class LedgerNodeHelpers_test : public beast::unit_test::Suite
|
||||
auto const kLeafDepth = SHAMap::kLeafDepth;
|
||||
auto const leafID = SHAMapNodeID::createID(kLeafDepth, leafKey);
|
||||
|
||||
protocol::TMLedgerNode node;
|
||||
node.set_nodedata(leafData);
|
||||
node.set_depth(kLeafDepth);
|
||||
auto const result = getSHAMapNodeID(node, *leafNode);
|
||||
protocol::TMLedgerNode ledgerNode;
|
||||
ledgerNode.set_nodedata(leafData);
|
||||
ledgerNode.set_depth(kLeafDepth);
|
||||
auto const result = getSHAMapNodeID(ledgerNode, *leafNode);
|
||||
BEAST_EXPECT(result == leafID);
|
||||
}
|
||||
|
||||
@@ -348,13 +236,21 @@ class LedgerNodeHelpers_test : public beast::unit_test::Suite
|
||||
BEAST_EXPECT(!result);
|
||||
}
|
||||
}
|
||||
|
||||
// Invalid: no field set.
|
||||
{
|
||||
auto const innerNode = intr_ptr::makeShared<SHAMapInnerNode>(1);
|
||||
protocol::TMLedgerNode ledgerNode;
|
||||
ledgerNode.set_nodedata("test_data");
|
||||
auto const result = getSHAMapNodeID(ledgerNode, *innerNode);
|
||||
BEAST_EXPECT(!result);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testValidateLedgerNode();
|
||||
testGetTreeNode();
|
||||
testGetSHAMapNodeID();
|
||||
}
|
||||
|
||||
@@ -124,6 +124,7 @@ public:
|
||||
auto node = SHAMapTreeNode::makeFromWire(makeSlice(a[0].data));
|
||||
if (!node)
|
||||
fail("", __FILE__, __LINE__);
|
||||
BEAST_EXPECT(a[0].isLeaf == node->isLeaf());
|
||||
BEAST_EXPECT(
|
||||
destination.addRootNode(source.getHash(), std::move(node), nullptr).isGood());
|
||||
}
|
||||
@@ -164,6 +165,8 @@ public:
|
||||
auto node = SHAMapTreeNode::makeFromWire(makeSlice(b[i].data));
|
||||
if (!node)
|
||||
fail("", __FILE__, __LINE__);
|
||||
if (b[i].isLeaf != node->isLeaf())
|
||||
fail("", __FILE__, __LINE__);
|
||||
if (!destination.addKnownNode(b[i].nodeID, std::move(node), nullptr).isUseful())
|
||||
fail("", __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
@@ -135,7 +135,10 @@ private:
|
||||
takeHeader(std::string_view data);
|
||||
|
||||
void
|
||||
receiveNode(protocol::TMLedgerData& packet, SHAMapAddNode& san);
|
||||
receiveNode(
|
||||
std::shared_ptr<Peer> const& peer,
|
||||
protocol::TMLedgerData& packet,
|
||||
SHAMapAddNode& san);
|
||||
|
||||
bool
|
||||
takeTxRootNode(std::string_view data, SHAMapAddNode& san);
|
||||
|
||||
@@ -13,24 +13,6 @@ class TMLedgerNode;
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* @brief Validates a ledger node proto message.
|
||||
*
|
||||
* This function checks whether a ledger node has the expected fields (for non-ledger base data):
|
||||
* - The node must have `nodedata`.
|
||||
* - If the legacy `nodeid` field is present then the new `id` and `depth` fields must not be
|
||||
* present, and `nodeid` must be a valid serialized SHAMapNodeID.
|
||||
* - If the new `id` or `depth` fields are present (it is a oneof field, so only one of the two can
|
||||
* be set) then the legacy `nodeid` must not be present.
|
||||
* - If the `id` field is present then it must be a valid serialized SHAMapNodeID.
|
||||
* - If the `depth` field is present then it must be between 0 and SHAMap::kLeafDepth (inclusive).
|
||||
*
|
||||
* @param ledgerNode The ledger node to validate.
|
||||
* @return true if the ledger node has the expected fields, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
validateLedgerNode(protocol::TMLedgerNode const& ledgerNode);
|
||||
|
||||
/**
|
||||
* @brief Deserializes a SHAMapTreeNode from wire format data.
|
||||
*
|
||||
@@ -64,8 +46,6 @@ getTreeNode(std::string_view data);
|
||||
* @param treeNode The deserialized tree node (inner or leaf node).
|
||||
* @return An optional containing the node ID if extraction/reconstruction succeeds, or std::nullopt
|
||||
* if the required fields are missing or validation fails.
|
||||
* @note This function expects that the caller has already validated the ledger node by calling the
|
||||
* `validateLedgerNode` function and obtained a valid tree node by calling `getTreeNode`.
|
||||
*/
|
||||
[[nodiscard]] std::optional<SHAMapNodeID>
|
||||
getSHAMapNodeID(protocol::TMLedgerNode const& ledgerNode, SHAMapTreeNode const& treeNode);
|
||||
@@ -3,8 +3,8 @@
|
||||
#include <xrpld/app/ledger/AccountStateSF.h>
|
||||
#include <xrpld/app/ledger/InboundLedgers.h>
|
||||
#include <xrpld/app/ledger/LedgerMaster.h>
|
||||
#include <xrpld/app/ledger/LedgerNodeHelpers.h>
|
||||
#include <xrpld/app/ledger/TransactionStateSF.h>
|
||||
#include <xrpld/app/ledger/detail/LedgerNodeHelpers.h>
|
||||
#include <xrpld/app/ledger/detail/TimeoutCounter.h>
|
||||
#include <xrpld/app/main/Application.h>
|
||||
#include <xrpld/overlay/Message.h>
|
||||
@@ -821,7 +821,10 @@ InboundLedger::takeHeader(std::string_view data)
|
||||
Call with a lock
|
||||
*/
|
||||
void
|
||||
InboundLedger::receiveNode(protocol::TMLedgerData& packet, SHAMapAddNode& san)
|
||||
InboundLedger::receiveNode(
|
||||
std::shared_ptr<Peer> const& peer,
|
||||
protocol::TMLedgerData& packet,
|
||||
SHAMapAddNode& san)
|
||||
{
|
||||
if (!haveHeader_)
|
||||
{
|
||||
@@ -870,6 +873,7 @@ InboundLedger::receiveNode(protocol::TMLedgerData& packet, SHAMapAddNode& san)
|
||||
if (!treeNode)
|
||||
{
|
||||
JLOG(journal_.warn()) << "Got invalid node data";
|
||||
peer->charge(Resource::kFeeInvalidData, "ledger_node.node_data invalid");
|
||||
san.incInvalid();
|
||||
return;
|
||||
}
|
||||
@@ -878,6 +882,7 @@ InboundLedger::receiveNode(protocol::TMLedgerData& packet, SHAMapAddNode& san)
|
||||
if (!nodeID)
|
||||
{
|
||||
JLOG(journal_.warn()) << "Got invalid node id";
|
||||
peer->charge(Resource::kFeeInvalidData, "ledger_node.node_id invalid");
|
||||
san.incInvalid();
|
||||
return;
|
||||
}
|
||||
@@ -893,14 +898,16 @@ InboundLedger::receiveNode(protocol::TMLedgerData& packet, SHAMapAddNode& san)
|
||||
|
||||
if (!san.isGood())
|
||||
{
|
||||
JLOG(journal_.warn()) << "Received bad node data";
|
||||
JLOG(journal_.warn()) << "Got invalid node";
|
||||
peer->charge(Resource::kFeeInvalidData, "ledger_node invalid");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
JLOG(journal_.error()) << "Received bad node data: " << e.what();
|
||||
// If we get here it is not necessarily because the node was bad, so don't charge the peer.
|
||||
JLOG(journal_.error()) << "Could not process node: " << e.what();
|
||||
san.incInvalid();
|
||||
return;
|
||||
}
|
||||
@@ -1132,24 +1139,18 @@ InboundLedger::processData(std::shared_ptr<Peer> peer, protocol::TMLedgerData& p
|
||||
|
||||
ScopedLockType const sl(mtx_);
|
||||
|
||||
// Verify nodes are complete
|
||||
for (auto const& ledgerNode : packet.nodes())
|
||||
{
|
||||
if (!validateLedgerNode(ledgerNode))
|
||||
{
|
||||
JLOG(journal_.warn()) << "Got malformed ledger node";
|
||||
peer->charge(Resource::kFeeMalformedRequest, "ledger_node");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
SHAMapAddNode san;
|
||||
receiveNode(packet, san);
|
||||
receiveNode(peer, packet, san);
|
||||
|
||||
JLOG(journal_.debug()) << "Ledger "
|
||||
<< ((packet.type() == protocol::liTX_NODE) ? "TX" : "AS")
|
||||
<< " node stats: " << san.get();
|
||||
|
||||
// Note: Peer charges for invalid/malformed data are issued from within receiveNode at the
|
||||
// exact failure site, so the peer is only charged for problems they are responsible for.
|
||||
if (san.isInvalid())
|
||||
return -1;
|
||||
|
||||
if (san.isUseful())
|
||||
progress_ = true;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <xrpld/app/ledger/InboundLedger.h>
|
||||
#include <xrpld/app/ledger/LedgerMaster.h>
|
||||
#include <xrpld/app/ledger/detail/LedgerNodeHelpers.h>
|
||||
#include <xrpld/app/ledger/LedgerNodeHelpers.h>
|
||||
#include <xrpld/app/main/Application.h>
|
||||
#include <xrpld/overlay/PeerSet.h>
|
||||
|
||||
@@ -250,9 +250,6 @@ public:
|
||||
{
|
||||
for (auto const& ledgerNode : packetPtr->nodes())
|
||||
{
|
||||
if (!validateLedgerNode(ledgerNode))
|
||||
return;
|
||||
|
||||
auto const treeNode = getTreeNode(ledgerNode.nodedata());
|
||||
if (!treeNode)
|
||||
return;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <xrpld/app/ledger/InboundTransactions.h>
|
||||
|
||||
#include <xrpld/app/ledger/detail/LedgerNodeHelpers.h>
|
||||
#include <xrpld/app/ledger/LedgerNodeHelpers.h>
|
||||
#include <xrpld/app/ledger/detail/TransactionAcquire.h>
|
||||
#include <xrpld/app/main/Application.h>
|
||||
#include <xrpld/overlay/PeerSet.h>
|
||||
@@ -137,7 +137,7 @@ public:
|
||||
|
||||
if (ta == nullptr)
|
||||
{
|
||||
peer->charge(Resource::kFeeUselessData, "ledger_data");
|
||||
peer->charge(Resource::kFeeUselessData, "ledger_data useless");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -146,18 +146,11 @@ public:
|
||||
|
||||
for (auto const& ledgerNode : packet.nodes())
|
||||
{
|
||||
if (!validateLedgerNode(ledgerNode))
|
||||
{
|
||||
JLOG(j_.warn()) << "Got malformed ledger node";
|
||||
peer->charge(Resource::kFeeMalformedRequest, "ledger_node");
|
||||
return;
|
||||
}
|
||||
|
||||
auto treeNode = getTreeNode(ledgerNode.nodedata());
|
||||
if (!treeNode)
|
||||
{
|
||||
JLOG(j_.warn()) << "Got invalid node data";
|
||||
peer->charge(Resource::kFeeInvalidData, "node_data");
|
||||
peer->charge(Resource::kFeeInvalidData, "ledger_node.node_data invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -165,15 +158,22 @@ public:
|
||||
if (!nodeID)
|
||||
{
|
||||
JLOG(j_.warn()) << "Got invalid node id";
|
||||
peer->charge(Resource::kFeeInvalidData, "node_id");
|
||||
peer->charge(Resource::kFeeInvalidData, "ledger_node.node_id invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
data.emplace_back(*nodeID, std::move(treeNode));
|
||||
}
|
||||
|
||||
if (!ta->takeNodes(std::move(data), peer).isUseful())
|
||||
peer->charge(Resource::kFeeUselessData, "ledger_data not useful");
|
||||
auto const san = ta->takeNodes(std::move(data), peer);
|
||||
if (san.isInvalid())
|
||||
{
|
||||
peer->charge(Resource::kFeeInvalidData, "ledger_data invalid");
|
||||
}
|
||||
else if (!san.isUseful())
|
||||
{
|
||||
peer->charge(Resource::kFeeUselessData, "ledger_data useless");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <xrpld/app/ledger/detail/LedgerNodeHelpers.h>
|
||||
#include <xrpld/app/ledger/LedgerNodeHelpers.h>
|
||||
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/basics/safe_cast.h>
|
||||
@@ -16,24 +16,6 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
bool
|
||||
validateLedgerNode(protocol::TMLedgerNode const& ledgerNode)
|
||||
{
|
||||
if (!ledgerNode.has_nodedata())
|
||||
return false;
|
||||
|
||||
if (ledgerNode.has_nodeid())
|
||||
{
|
||||
return ledgerNode.reference_case() == ledgerNode.REFERENCE_NOT_SET &&
|
||||
deserializeSHAMapNodeID(ledgerNode.nodeid()).has_value();
|
||||
}
|
||||
|
||||
if (ledgerNode.has_id())
|
||||
return deserializeSHAMapNodeID(ledgerNode.id()).has_value();
|
||||
|
||||
return ledgerNode.has_depth() && ledgerNode.depth() <= SHAMap::kLeafDepth;
|
||||
}
|
||||
|
||||
SHAMapTreeNodePtr
|
||||
getTreeNode(std::string_view data)
|
||||
{
|
||||
@@ -53,6 +35,10 @@ getSHAMapNodeID(protocol::TMLedgerNode const& ledgerNode, SHAMapTreeNode const&
|
||||
{
|
||||
if (ledgerNode.has_id() || ledgerNode.has_depth())
|
||||
{
|
||||
// Reject ambiguous messages that mix the legacy and new reference fields.
|
||||
if (ledgerNode.has_nodeid())
|
||||
return std::nullopt;
|
||||
|
||||
if (treeNode.isInner())
|
||||
{
|
||||
if (!ledgerNode.has_id())
|
||||
@@ -63,7 +49,7 @@ getSHAMapNodeID(protocol::TMLedgerNode const& ledgerNode, SHAMapTreeNode const&
|
||||
|
||||
if (treeNode.isLeaf())
|
||||
{
|
||||
if (!ledgerNode.has_depth())
|
||||
if (!ledgerNode.has_depth() || ledgerNode.depth() > SHAMap::kLeafDepth)
|
||||
return std::nullopt;
|
||||
|
||||
auto const key = safeDowncast<SHAMapLeafNode const*>(&treeNode)->peekItem()->key();
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <xrpld/app/ledger/InboundLedgers.h>
|
||||
#include <xrpld/app/ledger/InboundTransactions.h>
|
||||
#include <xrpld/app/ledger/LedgerMaster.h>
|
||||
#include <xrpld/app/ledger/LedgerNodeHelpers.h>
|
||||
#include <xrpld/app/ledger/TransactionMaster.h>
|
||||
#include <xrpld/app/misc/Transaction.h>
|
||||
#include <xrpld/app/misc/ValidatorList.h>
|
||||
@@ -1496,27 +1497,12 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMGetLedger> const& m)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify and parse ledger node IDs
|
||||
std::vector<SHAMapNodeID> nodeIDs;
|
||||
if (itype != protocol::liBASE)
|
||||
// Cheap structural checks on node IDs; full parsing is deferred to the job
|
||||
// so the IO thread is not burdened with SHAMapNodeID deserialization.
|
||||
if (itype != protocol::liBASE && m->nodeids_size() <= 0)
|
||||
{
|
||||
if (m->nodeids_size() <= 0)
|
||||
{
|
||||
badData("Invalid ledger node IDs");
|
||||
return;
|
||||
}
|
||||
|
||||
nodeIDs.reserve(m->nodeids_size());
|
||||
for (auto const& nodeId : m->nodeids())
|
||||
{
|
||||
auto parsed = deserializeSHAMapNodeID(nodeId);
|
||||
if (!parsed)
|
||||
{
|
||||
badData("Invalid SHAMap node ID");
|
||||
return;
|
||||
}
|
||||
nodeIDs.push_back(std::move(*parsed));
|
||||
}
|
||||
badData("Invalid ledger node IDs");
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify query type
|
||||
@@ -1536,13 +1522,33 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMGetLedger> const& m)
|
||||
}
|
||||
}
|
||||
|
||||
// Queue a job to process the request
|
||||
// Queue a job to process the request. Full parsing of the node IDs is
|
||||
// performed inside the job so the IO thread is not burdened with
|
||||
// SHAMapNodeID deserialization for every TMGetLedger.
|
||||
std::weak_ptr<PeerImp> const weak = shared_from_this();
|
||||
app_.getJobQueue().addJob(
|
||||
JtLedgerReq, "RcvGetLedger", [weak, m, nodeIDs = std::move(nodeIDs)]() mutable {
|
||||
if (auto peer = weak.lock())
|
||||
peer->processLedgerRequest(m, std::move(nodeIDs));
|
||||
});
|
||||
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));
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1706,12 +1712,44 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMLedgerData> const& m)
|
||||
return;
|
||||
}
|
||||
|
||||
// If there is a request cookie, attempt to relay the message
|
||||
// If there is a request cookie, attempt to relay the message.
|
||||
if (m->has_requestcookie())
|
||||
{
|
||||
if (auto peer = overlay_.findPeerByShortID(m->requestcookie()))
|
||||
{
|
||||
m->clear_requestcookie();
|
||||
|
||||
// If the original requester doesn't support the new depth-based format, rewrite any
|
||||
// nodes that use it back to the legacy nodeid format before relaying. Once all nodes
|
||||
// have upgraded, the old protocol version and this code can be removed.
|
||||
if (!peer->supportsFeature(ProtocolFeature::LedgerNodeDepth))
|
||||
{
|
||||
for (int i = 0; i < m->nodes_size(); ++i)
|
||||
{
|
||||
auto* ledgerNode = m->mutable_nodes(i);
|
||||
if (ledgerNode->reference_case() != ledgerNode->REFERENCE_NOT_SET)
|
||||
{
|
||||
auto treeNode = getTreeNode(ledgerNode->nodedata());
|
||||
if (!treeNode)
|
||||
{
|
||||
JLOG(pJournal_.warn()) << "Unable to get tree node";
|
||||
return;
|
||||
}
|
||||
|
||||
auto const nodeID = getSHAMapNodeID(*ledgerNode, *treeNode);
|
||||
if (!nodeID)
|
||||
{
|
||||
JLOG(pJournal_.warn()) << "Unable to get node ID";
|
||||
return;
|
||||
}
|
||||
|
||||
ledgerNode->set_nodeid(nodeID->getRawString());
|
||||
ledgerNode->clear_id();
|
||||
ledgerNode->clear_depth();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
peer->send(std::make_shared<Message>(*m, protocol::mtLEDGER_DATA));
|
||||
}
|
||||
else
|
||||
@@ -3343,6 +3381,7 @@ PeerImp::processLedgerRequest(
|
||||
auto const queryDepth{m->has_querydepth() ? m->querydepth() : defaultDepth};
|
||||
|
||||
std::vector<SHAMapNodeData> data;
|
||||
data.reserve(Tuning::kSoftMaxReplyNodes);
|
||||
auto const useLedgerNodeDepth = supportsFeature(ProtocolFeature::LedgerNodeDepth);
|
||||
|
||||
for (auto const& nodeID : nodeIDs)
|
||||
@@ -3351,7 +3390,6 @@ PeerImp::processLedgerRequest(
|
||||
break;
|
||||
|
||||
data.clear();
|
||||
data.reserve(Tuning::kSoftMaxReplyNodes);
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user