In null-nodestore mode the SHAMapStore rotation thread does no useful
work — there's no disk to amortize. The bursty rotation cadence also
causes mCompleteLedgers to over-report relative to mRetainedLedgers
(mCompleteLedgers prunes only on rotation; mRetainedLedgers caps
per-ledger via setFullLedger's pop_front loop). Peers consulting our
complete_ledgers advertisement get misled.
Replace the rotation thread with per-ledger retirement in null mode:
- Add memoryResidentMode() and retireLedger() to SHAMapStore interface.
- SHAMapStoreImp::memoryResidentMode_ is auto-derived from
isRWDBNullMode() (after type=none env-var propagation).
- start() skips spawning the rotation thread when memory-resident.
- working_ initialized false in memory-resident mode so rendezvous()
short-circuits without hanging.
- retireLedger synchronously prunes per-seq state for one ledger:
mCompleteLedgers (preserves pinning), LedgerHistory cache, and the
three relational tables (Transactions, AccountTransactions, Ledgers).
No batching, no backoff sleeps — RWDB-relational deletes are
microseconds.
- LedgerMaster::setFullLedger collects retired ledgers from the
pop_front loop and calls retireLedger on each (after releasing
m_mutex).
Disk-backed mode is unchanged: memoryResidentMode_ stays false, the
rotation thread runs as before, retireLedger short-circuits on the
flag check.
Prototype shape — minimum to validate the model on a live network.
Does not yet: skip state_db_ init in memory-resident mode, reject
explicit online_delete config, or remove the now-unused
healthWait/canDelete machinery for null mode.
Refs .ai-docs/null-nodestore-backend.md.j2 §"Rotation Is Vestigial in
Memory-Resident Mode" for the full reasoning.
NullFactory (type=none) already provides the exact null-backend
semantics: fetchNodeObject returns notFound, store is a no-op, no disk
I/O. Previously SHAMapStoreImp treated any non-"rwdb" type as
disk-backed and called dbPaths() unconditionally, crashing with
boost::filesystem::create_directories on an empty path.
- Recognise "none" alongside "rwdb" as a memory backend (skips
dbPaths() and takes the memory-backend rotation path).
- On type=none, set XAHAU_RWDB_NULL=1 (overwrite=0) so the existing
isRWDBNullMode() helpers in SHAMapSync, InboundLedger, Ledger etc.
detect null-mode semantics (FBC liveness+anchor, setFullyWired,
rotation-copy skip) without requiring the env var to be set
separately.
Makes type=none a first-class null-backend config declaration,
equivalent to type=rwdb + XAHAU_RWDB_NULL=1 but without the env-var
dance. Users can now write:
[node_db]
type = none
online_delete = 16
Re-enable FullBelowCache in null-nodestore mode. Previously disabled via
useFullBelowCache() returning false, forcing sync to walk every branch.
That was a workaround for the stale-claim problem where an FBC entry
could outlive the canonical node it vouches for, leading to
SHAMapMissingNode on later reads.
At the two FBC short-circuit sites (SHAMap::addKnownNode and
gmn_ProcessNodes), null mode now:
- validates the claim via TreeNodeCache::fetch (returns non-null iff the
canonical node is held alive anywhere in the system), and
- anchors the canonical into THIS SHAMap via canonicalizeChild, so
retention is structural and independent of whichever ledger originally
anchored the claim.
Disk-backed mode is byte-identical to before (gated on isRWDBNullMode()).
With the anchor rule in place, the post-sync wiring walks in
InboundLedger::init() and done() are redundant; drop both and call
setFullyWired() directly in null mode.
Adds projected-source markers at key points for the design doc at
.ai-docs/null-nodestore-backend.md.j2 (not tracked).
- Search both LedgerMaster and InboundLedgers for the closest fully wired base.
- Implement sameChainDistance helper to accurately calculate distance between ledgers on the same chain.
- Use findBestFullyWiredBase to minimize the 'prime walk' delta.
Introduces a 'NULL' node-store mode (via XAHAU_RWDB_NULL) that operates
entirely in-memory by leveraging a sliding window of retained Ledger objects.
Key changes:
- SHAMapSync: Bypass FullBelowCache in null mode to force full tree wiring.
- Ledger: Add 'fullyWired' state tracking and mandatory wiring before use.
- LedgerMaster: Implement 'mRetainedLedgers' sliding window to pin SHAMap graphs.
- PeerImp: Add fallbacks to TreeNodeCache and LedgerMaster for peer requests.
- contract: Add boost::stacktrace to LogThrow for easier debugging of misses.
- basics: Add ReaderPreferringSharedMutex to mitigate reader starvation.
Due to rounding, the LPTokenBalance of the last LP might not match the LP's trustline balance. This was fixed for `AMMWithdraw` in `fixAMMv1_1` by adjusting the LPTokenBalance to be the same as the trustline balance. Since `AMMClawback` is also performing a withdrawal, we need to adjust LPTokenBalance as well in `AMMClawback.`
This change includes:
1. Refactored `verifyAndAdjustLPTokenBalance` function in `AMMUtils`, which both`AMMWithdraw` and `AMMClawback` call to adjust LPTokenBalance.
2. Added the unit test `testLastHolderLPTokenBalance` to test the scenario.
3. Modify the existing unit tests for `fixAMMClawbackRounding`.
* Add AMM bid/create/deposit/swap/withdraw/vote invariants:
- Deposit, Withdrawal invariants: `sqrt(asset1Balance * asset2Balance) >= LPTokens`.
- Bid: `sqrt(asset1Balance * asset2Balance) > LPTokens` and the pool balances don't change.
- Create: `sqrt(asset1Balance * assetBalance2) == LPTokens`.
- Swap: `asset1BalanceAfter * asset2BalanceAfter >= asset1BalanceBefore * asset2BalanceBefore`
and `LPTokens` don't change.
- Vote: `LPTokens` and pool balances don't change.
- All AMM and swap transactions: amounts and tokens are greater than zero, except on withdrawal if all tokens
are withdrawn.
* Add AMM deposit and withdraw rounding to ensure AMM invariant:
- On deposit, tokens out are rounded downward and deposit amount is rounded upward.
- On withdrawal, tokens in are rounded upward and withdrawal amount is rounded downward.
* Add Order Book Offer invariant to verify consumed amounts. Consumed amounts are less than the offer.
* Fix Bid validation. `AuthAccount` can't have duplicate accounts or the submitter account.
Combine multiple related debug log data points into a single
message. Allows quick correlation of events that
previously were either not logged or, if logged, strewn
across multiple lines, making correlation difficult.
The Heartbeat Timer and consensus ledger accept processing
each have this capability.
Also guarantees that log entries will be written if the
node is a validator, regardless of log severity level.
Otherwise, the level of these messages is at INFO severity.