From cac869fe7eb33060a035dd684c16d0ea64c1d51f Mon Sep 17 00:00:00 2001 From: Richard Holland Date: Tue, 15 Apr 2025 13:50:12 +1000 Subject: [PATCH] touch keys --- src/ripple/app/misc/HashRouter.cpp | 16 ++++++++++++++++ src/ripple/app/misc/HashRouter.h | 11 +++++++++++ src/ripple/app/misc/TxQ.h | 5 +++-- src/ripple/app/misc/impl/TxQ.cpp | 20 +++++++++++++++++++- src/ripple/ledger/OpenView.h | 6 ++++++ src/ripple/ledger/detail/RawStateTable.h | 16 ++++++++++++++++ src/ripple/ledger/impl/RawStateTable.cpp | 14 ++++++++++++++ 7 files changed, 85 insertions(+), 3 deletions(-) diff --git a/src/ripple/app/misc/HashRouter.cpp b/src/ripple/app/misc/HashRouter.cpp index 8085d6892..86a6ef65d 100644 --- a/src/ripple/app/misc/HashRouter.cpp +++ b/src/ripple/app/misc/HashRouter.cpp @@ -128,4 +128,20 @@ HashRouter::shouldRelay(uint256 const& key) return s.releasePeerSet(); } +void +HashRouter::setTouchedKeys(uint256 const& id, std::set&& k) +{ + std::unique_lock lock(touchedKeysMutex_); + touchedKeysMap_.insert_or_assign(id, std::move(k)); +} + +std::optional>> +HashRouter::getTouchedKeys(uint256 const& id) +{ + std::shared_lock lock(touchedKeysMutex_); + if (auto it = touchedKeysMap_.find(id); it != touchedKeysMap_.end()) + return std::cref(it->second); + return std::nullopt; +} + } // namespace ripple diff --git a/src/ripple/app/misc/HashRouter.h b/src/ripple/app/misc/HashRouter.h index 4bc9d3174..50e8a15b8 100644 --- a/src/ripple/app/misc/HashRouter.h +++ b/src/ripple/app/misc/HashRouter.h @@ -27,6 +27,8 @@ #include #include +#include +#include namespace ripple { @@ -195,6 +197,12 @@ public: int getFlags(uint256 const& key); + void + setTouchedKeys(uint256 const& id, std::set&& k); + + std::optional>> + getTouchedKeys(uint256 const& id); + /** Determines whether the hashed item should be relayed. Effects: @@ -217,6 +225,9 @@ private: std::mutex mutable mutex_; + mutable std::shared_mutex touchedKeysMutex_; + std::map> touchedKeysMap_; + // Stores all suppressed hashes and their expiration time beast::aged_unordered_map< uint256, diff --git a/src/ripple/app/misc/TxQ.h b/src/ripple/app/misc/TxQ.h index 65bcecde7..e178f50ef 100644 --- a/src/ripple/app/misc/TxQ.h +++ b/src/ripple/app/misc/TxQ.h @@ -31,6 +31,7 @@ #include #include #include +#include namespace ripple { @@ -105,13 +106,13 @@ public: FeeLevel64 minimumEscalationMultiplier = baseLevel * 500; /// Minimum number of transactions to allow into the ledger /// before escalation, regardless of the prior ledger's size. - std::uint32_t minimumTxnInLedger = 32; + std::uint32_t minimumTxnInLedger = 5000; /// Like @ref minimumTxnInLedger for standalone mode. /// Primarily so that tests don't need to worry about queuing. std::uint32_t minimumTxnInLedgerSA = 1000; /// Number of transactions per ledger that fee escalation "works /// towards". - std::uint32_t targetTxnInLedger = 1000; + std::uint32_t targetTxnInLedger = 10000; /** Optional maximum allowed value of transactions per ledger before fee escalation kicks in. By default, the maximum is an emergent property of network, validator, and consensus performance. This diff --git a/src/ripple/app/misc/impl/TxQ.cpp b/src/ripple/app/misc/impl/TxQ.cpp index 72898ee7e..83064a9f4 100644 --- a/src/ripple/app/misc/impl/TxQ.cpp +++ b/src/ripple/app/misc/impl/TxQ.cpp @@ -30,6 +30,7 @@ #include #include #include +#include namespace ripple { @@ -739,10 +740,17 @@ TxQ::apply( STAmountSO stAmountSO{view.rules().enabled(fixSTAmountCanonicalize)}; NumberSO stNumberSO{view.rules().enabled(fixUniversalNumber)}; + auto const transactionID = tx->getTransactionID(); + // See if the transaction paid a high enough fee that it can go straight // into the ledger. + view.getAndResetKeysTouched(); if (auto directApplied = tryDirectApply(app, view, tx, flags, j)) + { + app.getHashRouter().setTouchedKeys( + transactionID, view.getAndResetKeysTouched()); return *directApplied; + } // If we get past tryDirectApply() without returning then we expect // one of the following to occur: @@ -841,7 +849,6 @@ TxQ::apply( // is allowed in the TxQ: // 1. If the account's queue is empty or // 2. If the blocker replaces the only entry in the account's queue. - auto const transactionID = tx->getTransactionID(); if (pfresult.consequences.isBlocker()) { if (acctTxCount > 1) @@ -1207,6 +1214,8 @@ TxQ::apply( { OpenView sandbox(open_ledger, &view, view.rules()); + sandbox.getAndResetKeysTouched(); + auto result = tryClearAccountQueueUpThruTx( app, sandbox, @@ -1219,6 +1228,10 @@ TxQ::apply( flags, metricsSnapshot, j); + + app.getHashRouter().setTouchedKeys( + transactionID, sandbox.getAndResetKeysTouched()); + if (result.second) { sandbox.apply(view); @@ -1657,11 +1670,16 @@ TxQ::accept(Application& app, OpenView& view) JLOG(j_.trace()) << "Applying queued transaction " << candidateIter->txID << " to open ledger."; + view.getAndResetKeysTouched(); + auto const [txnResult, didApply] = candidateIter->apply(app, view, j_); if (didApply) { + app.getHashRouter().setTouchedKeys( + candidateIter->txID, view.getAndResetKeysTouched()); + // Remove the candidate from the queue JLOG(j_.debug()) << "Queued transaction " << candidateIter->txID diff --git a/src/ripple/ledger/OpenView.h b/src/ripple/ledger/OpenView.h index 98b783e3a..9f2ce0177 100644 --- a/src/ripple/ledger/OpenView.h +++ b/src/ripple/ledger/OpenView.h @@ -99,6 +99,12 @@ private: bool open_ = true; public: + std::set + getAndResetKeysTouched() + { + return items_.getAndResetKeysTouched(); + } + OpenView() = delete; OpenView& operator=(OpenView&&) = delete; diff --git a/src/ripple/ledger/detail/RawStateTable.h b/src/ripple/ledger/detail/RawStateTable.h index 2bb38dc49..4375a35e8 100644 --- a/src/ripple/ledger/detail/RawStateTable.h +++ b/src/ripple/ledger/detail/RawStateTable.h @@ -35,6 +35,9 @@ namespace detail { // Helper class that buffers raw modifications class RawStateTable { +private: + mutable std::set keysTouched_; + public: using key_type = ReadView::key_type; // Initial size for the monotonic_buffer_resource used for allocations @@ -98,6 +101,19 @@ public: std::unique_ptr slesUpperBound(ReadView const& base, uint256 const& key) const; + // each time a key is read or written it will be placed in the keysTouched_ + // set. + std::set + getAndResetKeysTouched() + { + std::set out; + out.swap(keysTouched_); + std::cout << "--------------\n"; + for (auto const& k : out) + std::cout << "getAndResetKeysTouched: " << to_string(k) << "\n"; + return out; + } + private: enum class Action { erase, diff --git a/src/ripple/ledger/impl/RawStateTable.cpp b/src/ripple/ledger/impl/RawStateTable.cpp index 019ef6156..1625be276 100644 --- a/src/ripple/ledger/impl/RawStateTable.cpp +++ b/src/ripple/ledger/impl/RawStateTable.cpp @@ -173,6 +173,8 @@ RawStateTable::apply(RawView& to) const to.rawReplace(item.sle); break; } + + keysTouched_.emplace(elem.first); } } @@ -180,6 +182,9 @@ bool RawStateTable::exists(ReadView const& base, Keylet const& k) const { assert(k.key.isNonZero()); + + keysTouched_.insert(k.key); + auto const iter = items_.find(k.key); if (iter == items_.end()) return base.exists(k); @@ -227,12 +232,18 @@ RawStateTable::succ( // what we got from the parent. if (last && next >= last) return std::nullopt; + + if (next.has_value()) + keysTouched_.insert(*next); + return next; } void RawStateTable::erase(std::shared_ptr const& sle) { + keysTouched_.insert(sle->key()); + // The base invariant is checked during apply auto const result = items_.emplace( std::piecewise_construct, @@ -259,6 +270,7 @@ RawStateTable::erase(std::shared_ptr const& sle) void RawStateTable::insert(std::shared_ptr const& sle) { + keysTouched_.insert(sle->key()); auto const result = items_.emplace( std::piecewise_construct, std::forward_as_tuple(sle->key()), @@ -284,6 +296,7 @@ RawStateTable::insert(std::shared_ptr const& sle) void RawStateTable::replace(std::shared_ptr const& sle) { + keysTouched_.insert(sle->key()); auto const result = items_.emplace( std::piecewise_construct, std::forward_as_tuple(sle->key()), @@ -306,6 +319,7 @@ RawStateTable::replace(std::shared_ptr const& sle) std::shared_ptr RawStateTable::read(ReadView const& base, Keylet const& k) const { + keysTouched_.insert(k.key); auto const iter = items_.find(k.key); if (iter == items_.end()) return base.read(k);