mirror of
https://github.com/Xahau/xahaud.git
synced 2026-03-17 18:12:23 +00:00
feat(export): wip export system limits
- max_export per hook: 4 → 2 - maxPendingExports: cap exported directory at 8 entries (tecDIR_FULL) - clamp inbound signature processing in PeerImp to directory cap The directory cap is the root DoS constraint: each pending export requires every validator to sign and broadcast every round. Inbound processing and signing throughput are transitively bounded by it.
This commit is contained in:
@@ -402,7 +402,7 @@ const uint16_t max_state_modifications = 256;
|
||||
const uint8_t max_slots = 255;
|
||||
const uint8_t max_nonce = 255;
|
||||
const uint8_t max_emit = 255;
|
||||
const uint8_t max_export = 4;
|
||||
const uint8_t max_export = 2;
|
||||
const uint8_t max_params = 16;
|
||||
const double fee_base_multiplier = 1.1f;
|
||||
|
||||
|
||||
32
include/xrpl/hook/ExportLimits.h
Normal file
32
include/xrpl/hook/ExportLimits.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef RIPPLE_HOOK_EXPORT_LIMITS_H_INCLUDED
|
||||
#define RIPPLE_HOOK_EXPORT_LIMITS_H_INCLUDED
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// Export system caps.
|
||||
//
|
||||
// These limits bound the DoS surface of the export signature system:
|
||||
// - Each pending export requires every validator to sign it every round
|
||||
// (sign-once, broadcast-many via TMValidation)
|
||||
// - Inbound signature processing involves crypto verification per sig
|
||||
// - The directory cap (maxPendingExports) is the root constraint;
|
||||
// signing throughput and inbound processing are transitively bounded by it
|
||||
struct ExportLimits
|
||||
{
|
||||
// Maximum exports a single hook execution may produce
|
||||
// (also enforced by hook_api::max_export in Enum.h)
|
||||
static constexpr std::uint8_t maxExportsPerHook = 2;
|
||||
|
||||
// Maximum pending exports in the exported directory at any time.
|
||||
// This transitively caps:
|
||||
// - signatures per TMValidation message (1 per pending export)
|
||||
// - inbound signature processing in PeerImp (clamped to this)
|
||||
// - validator signing work per round
|
||||
static constexpr std::uint8_t maxPendingExports = 8;
|
||||
};
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
@@ -7,8 +7,10 @@
|
||||
#include <xrpld/app/misc/TxQ.h>
|
||||
#include <xrpld/app/tx/detail/Import.h>
|
||||
#include <xrpld/app/tx/detail/NFTokenUtils.h>
|
||||
#include <xrpld/ledger/View.h>
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/hook/ExportLimits.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/protocol/st.h>
|
||||
@@ -1735,6 +1737,43 @@ hook::finalizeHookResult(
|
||||
|
||||
if (!sleExported)
|
||||
{
|
||||
// Enforce maxPendingExports on the exported directory.
|
||||
// Each pending export costs validator signing + broadcast
|
||||
// work every round, so this is the root DoS constraint.
|
||||
{
|
||||
Keylet const expDirKey{keylet::exportedDir()};
|
||||
std::size_t dirSize = 0;
|
||||
std::shared_ptr<SLE const> sleDirNode;
|
||||
unsigned int uDirEntry{0};
|
||||
uint256 dirEntry{beast::zero};
|
||||
if (cdirFirst(
|
||||
applyCtx.view(),
|
||||
expDirKey.key,
|
||||
sleDirNode,
|
||||
uDirEntry,
|
||||
dirEntry))
|
||||
{
|
||||
do
|
||||
{
|
||||
++dirSize;
|
||||
} while (cdirNext(
|
||||
applyCtx.view(),
|
||||
expDirKey.key,
|
||||
sleDirNode,
|
||||
uDirEntry,
|
||||
dirEntry));
|
||||
}
|
||||
|
||||
if (dirSize >= ExportLimits::maxPendingExports)
|
||||
{
|
||||
JLOG(j.warn()) << "HookError[" << HR_ACC() << "]: "
|
||||
<< "Export directory at cap ("
|
||||
<< ExportLimits::maxPendingExports
|
||||
<< "), rejecting export " << id;
|
||||
return tecDIR_FULL;
|
||||
}
|
||||
}
|
||||
|
||||
exported_txnid.emplace_back(id);
|
||||
|
||||
sleExported = std::make_shared<SLE>(exportedId);
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include <xrpl/basics/random.h>
|
||||
#include <xrpl/basics/safe_cast.h>
|
||||
#include <xrpl/beast/core/LexicalCast.h>
|
||||
#include <xrpl/hook/ExportLimits.h>
|
||||
#include <xrpl/protocol/digest.h>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
@@ -3065,7 +3066,19 @@ PeerImp::checkValidation(
|
||||
{
|
||||
auto const currentSeq = val->getFieldU32(sfLedgerSequence);
|
||||
|
||||
for (int i = 0; i < packet->exportsignatures_size(); ++i)
|
||||
// Clamp inbound export signatures to the directory cap.
|
||||
// A legitimate validator can only have maxPendingExports
|
||||
// pending, so anything beyond that is either a bug or abuse.
|
||||
auto const sigCount = std::min(
|
||||
packet->exportsignatures_size(),
|
||||
static_cast<int>(ExportLimits::maxPendingExports));
|
||||
if (sigCount < packet->exportsignatures_size())
|
||||
{
|
||||
JLOG(p_journal_.warn())
|
||||
<< "Export: clamping " << packet->exportsignatures_size()
|
||||
<< " signatures to cap " << sigCount;
|
||||
}
|
||||
for (int i = 0; i < sigCount; ++i)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user