Commit Graph

14330 Commits

Author SHA1 Message Date
Nicholas Dudfield
9562b457cf chore: remove stale Peer-level RNG forwarders
All callsites now go through ce() consistently.
2026-03-23 09:57:53 +07:00
Nicholas Dudfield
724633ceb5 refactor(consensus): decouple CSF tests from xrpld.app via PeerTick.h
Move ConsensusExtensionsTick.h from xrpld/app/consensus/ to
xrpld/consensus/ — it's a pure template with no app-layer deps.
Extract Peer::Extensions::onTick() definition into test/csf/PeerTick.h
so Peer.h no longer includes from xrpld/app/.

Eliminates the test.csf > xrpld.app levelization edge.

Add --explain flag to levelization.py for tracing dependency edges.
2026-03-23 09:36:59 +07:00
Nicholas Dudfield
152d82e798 refactor(consensus): extract RNG/Export into ConsensusExtensions
Extract all Xahau consensus extension logic (RNG commit-reveal entropy
and Export validator multisign collection) from Consensus.h and
RCLCxAdaptor into a dedicated ConsensusExtensions class owned by
Application.

Implements all 10 lifecycle hooks from the design doc:
  onRoundStart, onTrustedPeerMessage, onTrustedPeerProposal,
  decoratePosition, decorateMessage, onTick, onPreBuild,
  onAcceptComplete, isSidecarSet, onAcquiredSidecarSet

Key design decisions:
- ConsensusTick<> template in ConsensusTypes.h keeps dependency
  direction clean (generic consensus defines contract, extensions
  implement it)
- extensionsTick<> shared template in ConsensusExtensionsTick.h
  ensures CSF test framework runs the same state machine as production
- ExportSigCollector ownership moved from global singleton to CE
- Sidecar acquisition routed through RCLConsensus mutex for thread
  safety (isExtensionSet + gotExtensionSet)
- RCLCxAdaptor reduced to thin ce() accessor + generic consensus
  interface methods

Files:
  new: ConsensusExtensions.h/.cpp, ConsensusExtensionsTick.h
  reduced: Consensus.h (-1060 lines), RCLConsensus.cpp (-1400 lines)
  updated: ConsensusTypes.h, Application.h/.cpp, NetworkOPs.cpp,
           PeerImp.cpp, Export.cpp, ExportSigCollector.h, Peer.h

7/7 testnet scenarios + 1463 consensus + 260 Export unit tests pass.
2026-03-19 20:23:19 +07:00
Nicholas Dudfield
0bb31ce7ce chore: add projected-source markers for consensus extension docs
Non-functional comment markers (//@@start, //@@end) for projected-source
documentation extraction.
2026-03-19 12:14:11 +07:00
Nicholas Dudfield
4cb3de0497 refactor(export): build xport wrapper via STObject then serialise to STTx
Replace the duplicated throwaway-STTx + real-STTx pattern with a
single STObject: set all fields including fee=0, serialise to compute
the fee, patch the fee, then serialise once into the final STTx.

20 lines shorter, no duplication.
2026-03-18 17:36:30 +07:00
Nicholas Dudfield
c6b315412d test(export): harden scenario tests with proper assertions
Replace warnings and logs with hard assertions across all export
scenario tests:

- export_helpers: add assert_hook_accepted(), assert_export_result(),
  assert_shadow_ticket() shared assertion helpers
- steady_state_export: assert hook ACCEPT + emitCount, ExportResult
  contents (signers, inner tx fields), shadow ticket exists
- retriable_export: assert ExportResult well-formed, shadow ticket
  created, payment not blocked
- export_degradation: assert export FAILS (not just log), no shadow
  ticket, payment still works
- export_unanimity: assert ExportResult + shadow ticket on success,
  absence on failure
2026-03-18 17:18:12 +07:00
Nicholas Dudfield
72395bec75 chore: clang-format 2026-03-18 17:12:41 +07:00
Nicholas Dudfield
8ed4d86f0f test(export): verify emitted ttEXPORT lifecycle end-to-end
testXportPayment now asserts the full emitted tx lifecycle:
- hook fires with ACCEPT, emitCount=1, returnCode=0
- sfHookEmissions present with 1 entry
- ltEMITTED_TXN in AffectedNodes
- emitted dir is not empty
- after close, emitted ttEXPORT appears in closed ledger

Also add FOCUSED_TEST env var gate for fast iteration during
development (set FOCUSED_TEST=1 to run only focused_test()).
2026-03-18 17:11:19 +07:00
Nicholas Dudfield
419fd16b9a chore: move export scenarios to export-suite.yml, add SuiteLogsWithOverrides
Move export scenario tests from suite.yml into their own
export-suite.yml file. The defaults already set CE+Export features
so individual test entries no longer need to repeat them.

Add SuiteLogsWithOverrides test utility: a Logs subclass that routes
specified journal partitions to stderr (always visible) while keeping
others on suite_.log (only on failure). Useful for debugging specific
subsystems during test development.
2026-03-18 17:09:47 +07:00
Nicholas Dudfield
a8097cd9a6 fix(export): compute emit fee before STTx construction
Mutating the fee via const_cast after STTx construction left a stale
cached getTransactionID(). When the emitted ttEXPORT was serialised
into the emitted directory and later deserialised, the round-tripped
txid differed from the original, causing tefNONDIR_EMIT in
Transactor::preclaim (the emitted dir entry was keyed with the stale
hash).

Build a throwaway STTx with fee=0 to calculate the fee size, then
construct the real STTx with the correct fee from the start.
2026-03-18 17:04:46 +07:00
Nicholas Dudfield
02a0552325 docs(export): clarify LLS semantics for retriable exports
Add comment explaining the three-state outcome table for exports
relative to LastLedgerSequence:
  ledger < LLS:  tesSUCCESS or terRETRY_EXPORT
  ledger == LLS: tesSUCCESS or tecEXPORT_EXPIRED
  ledger > LLS:  tefMAX_LEDGER (never reaches doApply)
2026-03-18 14:43:50 +07:00
Nicholas Dudfield
3698193b0a chore: clang-format 2026-03-18 14:21:00 +07:00
Nicholas Dudfield
de43ca2385 refactor(export): store multisigned tx as sfExportedTxn object in metadata
Use sfExportedTxn (OBJECT) instead of sfBlob (VL) for the multisigned
transaction in sfExportResult metadata. This renders as readable JSON
with all fields visible (Account, Signers, etc.) instead of an opaque
hex blob.

Also compute the tx hash directly via getHash(HashPrefix::transactionID)
on the STObject instead of serializing/deserializing through STTx.
2026-03-18 14:16:25 +07:00
Nicholas Dudfield
8c747a1916 feat(export): produce multisigned blob in export metadata
Export::doApply now builds the fully multisigned inner tx and stores
it as sfBlob in sfExportResult metadata. In standalone mode, the node
signs directly with its own validator keys (no consensus needed).

Key changes:
- ExportSigCollector stores actual multisign signatures, not just pubkeys
- RCLConsensus proposal attachment computes real multisign sigs over inner tx
- PeerImp harvests variable-length sig entries from proposals
- Export::doApply assembles Signers array, builds multisigned blob first,
  then uses its hash for the shadow ticket (getTransactionID includes all
  fields including Signers)
- Import skips OperationLimit and signing key checks for export callback
  path (sfTicketSequence present) — shadow ticket proves the relationship
- Full Export→XRPL→Import round-trip test: export on Xahau, submit
  multisigned blob to XRPL (with matching SignerList), build XPOP,
  import back, verify shadow ticket consumed
2026-03-18 13:54:29 +07:00
Nicholas Dudfield
cea110f29a feat: add XPOP test helper and XPOP_test suite
- src/test/jtx/xpop.h: test utilities for building XPOPs from Env ledgers
  (TestValidator, TestVLPublisher, TestXPOPContext, buildTestXPOP)
- src/test/app/XPOP_test.cpp: 4 tests (173 assertions)
  - LedgerProof construction from payment tx
  - XPOP v1 JSON structure verification
  - Merkle proof verification for multi-tx ledgers
  - Full Import round-trip: source Env payment → XPOP → dest Env Import → tesSUCCESS
2026-03-18 11:59:34 +07:00
Nicholas Dudfield
3ca056a94b feat: add XPOP test helper and XPOP_test suite
- src/test/jtx/xpop.h: test utilities for building XPOPs from Env ledgers
  (TestValidator, TestVLPublisher, buildTestXPOP)
- src/test/app/XPOP_test.cpp: 3 tests (133 assertions)
  - LedgerProof construction from payment tx
  - XPOP v1 JSON structure verification
  - Merkle proof verification for multi-tx ledgers
2026-03-18 11:41:16 +07:00
Nicholas Dudfield
705d8400db feat: add proof module for XPOP construction
New module at src/xrpld/app/proof/ with layered design:

- ProofBuilder: SHAMap merkle proof extraction (extractProofV1)
  Binary trie proof with 16-way branching, root hash verification.
- LedgerProof: proof-of-ledger (header fields + tx blob/meta + merkle proof)
  buildLedgerProof() extracts everything from a closed Ledger.
- XPOPv1: JSON format builder matching Import.cpp expectations
  buildXPOPv1() creates complete XPOP with validation signatures.

Designed for versioning: v1 JSON (current Import compat), future v2
binary proofs and account state proofs layer on the same core.
2026-03-18 11:29:29 +07:00
Nicholas Dudfield
655b751698 chore: regenerate hook/sfcodes.h for new sfields
Adds sfCancelTicketSequence (UINT32 101) and sfExportResult (OBJECT 98).
2026-03-18 11:06:04 +07:00
Nicholas Dudfield
f324081277 fix(import): verify XPOP tx hash matches shadow ticket
Shadow tickets store the exported transaction hash. Import now verifies
the XPOP's inner tx hash matches, preventing use of a different XPOP
with the same TicketSequence.
2026-03-18 10:52:19 +07:00
Nicholas Dudfield
24a284180a fix(export): address review findings from code audit
- Validate incoming export sig pubkeys against trusted UNL (PeerImp)
- Fix handleAcquiredRngSet misclassifying export sets when local map is null
- Add stale-entry cleanup (cleanupStale with 256-ledger timeout)
- Gate sig attachment on featureExport amendment (RCLConsensus)
- Fail with tefINTERNAL if ExportResult metadata can't be written
- Make export and cancel mutually exclusive (reject both in preflight)
- Remove dead ExportLimits.h include
2026-03-18 10:30:41 +07:00
Nicholas Dudfield
6f003cc983 feat(export): add SHAMap-based export sig convergence
Add deterministic export sig set convergence using CE infrastructure:
- exportSigSetHash in ExtendedPosition (flag 0x10)
- buildExportSigSet() builds SHAMap from collected sigs
- hasPendingExportSigs() gates convergence in phaseEstablish
- Parallel convergence gate alongside RNG sub-states
- handleAcquiredRngSet extended to merge export sig sets
- Tiered quorum: 80% with CE, 100% unanimity without CE

Scenario tests for all 3 feature combos:
- CE+Export, 1 node down: 4/5=80% → tesSUCCESS
- Export only, all up: 5/5=100% → tesSUCCESS
- Export only, 1 node down: 4/5≠100% → tecEXPORT_EXPIRED
2026-03-18 10:17:28 +07:00
Nicholas Dudfield
3a58020388 fix(export): tiered quorum threshold based on CE availability
Without CE: require unanimity (100% UNL) to avoid non-deterministic
quorum disagreement. With CE: use standard 80% quorum via
calculateQuorumThreshold (SHAMap convergence will ensure agreement).
Standalone/unit tests: require 1 sig.
2026-03-18 09:46:36 +07:00
Nicholas Dudfield
829441b52e fix(export): deduplicate export sigs across proposals within a round 2026-03-18 09:32:27 +07:00
Nicholas Dudfield
3a055663cc chore: add export-sig-attachment marker for projected-source 2026-03-18 09:26:33 +07:00
Nicholas Dudfield
985a194bdc feat(export): migrate to retriable ttEXPORT with proposal-based sigs
Replace the old ltEXPORTED_TXN + ttEXPORT_FINALIZE (validation-based
sigs, TxQ injection) approach with a retriable ttEXPORT that collects
validator signatures via TMProposeSet during consensus.

Added:
- terRETRY_EXPORT: keeps tx in retry set across ledger boundaries
- tecEXPORT_EXPIRED (200): LLS expiry frees sequence cleanly
- sfExportResult (OBJECT 98): signed export result in tx metadata
- ExportSigCollector: minimal thread-safe sig tracker
- Proposal sig attachment (RCLConsensus) + harvesting (PeerImp)
- exportSignatures field in TMProposeSet (ripple.proto)
- Metadata plumbing (TxMeta, ApplyViewImpl, ApplyStateTable)
- Hook xport() now emits ttEXPORT via normal emitted txn path

Removed:
- ttEXPORT_FINALIZE (type 90) pseudo-tx and Change::applyExportFinalize
- ltEXPORTED_TXN ledger entry and exportedDir/exportedTxn keylets
- ExportSignatureCollector (replaced by ExportSigCollector)
- TxQ export injection (quorum check + rawTxInsert)
- Validation-based export signing in RCLConsensus
- Application::getExportSignatureCollector

Verified on 5-node testnet: golden path (same-ledger finalization with
ExportResult in metadata), degraded path (tecEXPORT_EXPIRED on sub-quorum),
and hook xport() path (emitted ttEXPORT with shadow ticket creation).
2026-03-18 09:23:52 +07:00
Nicholas Dudfield
869f366d8a feat(export): add sfCancelTicketSequence for shadow ticket cancellation
Add sfCancelTicketSequence (UINT32 field 101) to ttEXPORT, allowing
users to cancel shadow tickets via a transaction. Both sfExportedTxn
and sfCancelTicketSequence are optional — at least one must be present.
This allows export, cancel, or both in a single transaction.

Test: create shadow ticket via export, cancel via sfCancelTicketSequence,
verify ticket is gone and owner reserve is freed.
2026-03-17 14:13:57 +07:00
Nicholas Dudfield
03936aa928 fix(export): require TicketSequence on exported transactions
Exported transactions must use TicketSequence (with Sequence=0)
because a bounced tx on the destination chain would jam sequential
sequence numbers. This is enforced in both the hook xport() API
and the Export transactor via ExportLedgerOps::validateTicketSequence().

Adds test: ttEXPORT rejects export without TicketSequence.
Updates existing test hooks to include TicketSequence in exported txns.
2026-03-17 12:30:55 +07:00
Nicholas Dudfield
6d180307ad feat(export): split Import into B2M and export callback paths
When the inner XPOP transaction has sfTicketSequence, Import now
takes the export callback path: consume the shadow ticket via
ExportLedgerOps::cancelShadowTicket() and return. No B2M balance
crediting, no account creation. Hooks fire normally and can inspect
the result via xpop_slot().

The B2M path is unchanged for non-ticket imports.

Also migrates the shadow ticket check in preclaim from the old
hookState namespace approach to keylet::shadowTicket().

Removes the unused shadowTicketNamespace constant.
2026-03-17 12:23:27 +07:00
Nicholas Dudfield
f2ca499c97 feat(export): add ltSHADOW_TICKET and xport_cancel hook API
Introduce shadow tickets for export replay protection:

- ltSHADOW_TICKET ledger entry: account-owned, keyed by
  account + ticket sequence. Fields: sfAccount, sfTicketSequence,
  sfTransactionHash, sfLedgerSequence, sfOwnerNode.

- ExportLedgerOps::createShadowTicket(): creates shadow ticket
  when exported tx has sfTicketSequence. Charges owner reserve.
  Called from both hook xport() path and Export transactor.

- ExportLedgerOps::cancelShadowTicket(): deletes shadow ticket,
  frees reserve. Used by xport_cancel hook API.

- xport_cancel(ticket_seq) hook API: allows hooks to cancel
  shadow tickets for exports that will never get a callback.

- InvariantCheck: add ltSHADOW_TICKET to valid entry types.

- Test: verify shadow ticket creation with correct fields and
  owner count bump via ttEXPORT with TicketSequence.
2026-03-17 12:13:41 +07:00
Nicholas Dudfield
bd68364f25 feat(export): add ttEXPORT user transaction and extract ExportLedgerOps
Rename the existing ttEXPORT pseudo-tx to ttEXPORT_FINALIZE (type 90)
to make room for a user-submittable ttEXPORT (type 91).

ttEXPORT allows non-hook users to submit export transactions directly,
creating the same ltEXPORTED_TXN entries that the hook xport() API
creates inline.

Extract shared logic into ExportLedgerOps.h:
- createExportedTxn(): creates ltEXPORTED_TXN, enforces directory cap
- validateNetworkID(): self-target and unconfigured guards
- validateExportAccount(): account ownership check

Both the hook API (HookAPI.cpp) and the Export transactor now call
into ExportLedgerOps, eliminating duplicated validation and ledger
mutation code.
2026-03-17 11:43:45 +07:00
Nicholas Dudfield
42a6407815 fix(export): reject exports when NETWORK_ID is unconfigured
If the node's NETWORK_ID is 0 (default/unconfigured) and the exported
transaction has no sfNetworkID field, we can't distinguish self-targeting
from legitimate cross-chain export. Reject to be safe.

Also adds exportTestConfig() helper and test for the unconfigured case.
2026-03-17 07:41:36 +07:00
Nicholas Dudfield
a387c853ab test(export): add NetworkID self-target guard test
Verify that xport() rejects exported transactions whose sfNetworkID
matches the local network. The hook builds a Payment with
NetworkID=21337 (matching the test env), and the guard correctly
returns EXPORT_FAILURE causing tecHOOK_REJECTED.

Also fix log level for the guard rejection to warn (not trace).
2026-03-17 07:31:35 +07:00
Nicholas Dudfield
9311e567d3 fix(export): reject exports targeting the local network
Explicitly forbid exported transactions whose sfNetworkID matches the
local network's ID. An exported txn re-executing on its origin chain
could cause exploits or logic issues.

The check is intentionally non-mandatory: XRPL mainnet (the primary
export target) doesn't use NetworkID, so absent = allowed.
2026-03-16 17:30:14 +07:00
Nicholas Dudfield
c26582bdf9 fix(export): move ExportLimits.h to xrpl/protocol
Both xrpld.overlay and xrpl.hook depend on xrpl.protocol, so placing
the header there avoids introducing a new xrpld.overlay > xrpl.hook
levelization dependency.
2026-03-16 15:58:58 +07:00
Nicholas Dudfield
417b999c7f chore(levelization): add xrpld.overlay > xrpl.hook dependency
New include of ExportLimits.h in PeerImp.cpp introduces this
module dependency (from feat(export) commit 89274b538).
2026-03-16 15:47:45 +07:00
Nicholas Dudfield
0205be4500 chore: add testnet scenario scripts
Entropy and export scenario scripts for local testnet validation.
2026-03-16 15:17:32 +07:00
Nicholas Dudfield
89274b5387 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.
2026-03-16 13:59:06 +07:00
Nicholas Dudfield
b65d9faf12 docs(consensus): add MERGE NOTE comments for upstream 86ef16dbeb resolution
Extends merge guidance to cover the empty-disputes bugfix (not yet in
sync-2.5.0): !disputes.empty() guard, stalled() j/clog params,
"should be rare" doc wording, debug→warn promotion, and auto-merged
testDisputes duplicate warning.
2026-03-11 10:45:04 +07:00
Nicholas Dudfield
aa1a7e5320 docs(consensus): add MERGE NOTE comments for sync-2.5.0 resolution
Inline comments at all 6 conflict points guiding the maintainer
through the expected merge conflicts when sync-2.5.0 lands:
ledgerMAX_CONSENSUS const, bootstrap params, calculateQuorumThreshold,
effectiveParms+stalled in haveConsensus, DisputedTx::stalled() j/clog
params, and testDisputes placement.
2026-03-11 10:09:02 +07:00
Nicholas Dudfield
6f0f17aad9 fix(consensus): cherry-pick upstream 86ef16dbeb empty-disputes stall fix
Cherry-pick of ripple/rippled@86ef16dbeb ("Fix: Don't flag consensus
as stalled prematurely (#5627)"). Not yet in any xahau sync branch.

Fixes false stall detection when there are no disputed transactions:
std::ranges::all_of on an empty set is vacuously true, so consensus
was incorrectly flagged as stalled. Adds !result_->disputes.empty()
guard.

Also adds diagnostic logging to DisputedTx::stalled() and the
stall detection path in haveConsensus(), and promotes the
"Need validated ledger" log from debug to warn.
2026-03-11 09:47:11 +07:00
Nicholas Dudfield
407bfa1467 feat(consensus): cherry-pick dd085e5d8 (upstream d22a5057b9) anti-stall mechanisms
Cherry-pick of ripple/rippled@d22a5057b9 / xahau dd085e5d8 ("Prevent
consensus from getting stuck in the establish phase (#5277)"), resolved
against our RNG pipeline and bootstrap fast-start changes.

Upstream adds three layered anti-stall mechanisms:
- Stateful per-dispute avalanche state machine (init→mid→late→stuck)
- Stall detection: declares consensus when all disputes individually settled
- Hard expiration: clamp(10× prev round, 15s, 120s) wall-clock safety net

Conflict resolution:
- ConsensusParms.h: kept both avalanche state machine (const members,
  avMIN_ROUNDS, avSTALLED_ROUNDS, getNeededWeight) and our bootstrap
  params (bootstrapRoundTimeSeed, bootstrapStableRoundsRequired).
  ledgerMAX_CONSENSUS left non-const for bootstrap override.
- Consensus.h: pass both stalled flag and effectiveParms to checkConsensus.
  Stall check uses original parms, bootstrap override only affects max
  consensus timeout.
- Consensus_test.cpp: kept all 12 RNG tests and new testDisputes test.
2026-03-11 09:36:38 +07:00
Nicholas Dudfield
f0dfcf6b81 fix(consensus): cap bootstrap ledgerMAX_CONSENSUS at 5s
Use an explicit 5s cap instead of dividing the default 15s.
5s is the sweet spot: long enough for peers to exchange proposals
and converge naturally, short enough to avoid wasted time.
Shorter values (e.g. 3.75s) cause nodes to hit reachedMax before
peers converge, cascading into slower subsequent rounds.
2026-03-10 14:30:20 +07:00
Nicholas Dudfield
503d2ebf98 feat(consensus): add XAHAUD_BOOTSTRAP_FAST_START for faster cold-start
Seed prevRoundTime_ to 3s instead of 15s on first round, override
idle interval to bypass closeTimeResolution (10-30s on early ledgers),
and halve ledgerMAX_CONSENSUS during bootstrap. Auto-disables after 3
consecutive rounds with UNL quorum participation.

Cuts 5-node testnet cold-start from ~28s to ~13s.

Also adds projected-source markers to TxQ, NetworkOPs, and Submit for
the transaction-submission documentation template.
2026-03-10 12:52:56 +07:00
Nicholas Dudfield
e52bc51384 refactor(consensus): extract shouldZeroEntropy() for quorum-gated entropy
Consolidate the repeated entropy fallback condition
(entropyFailed || no reveals || sub-quorum reveals) into a single
method. Fixes EntropyCount field reporting non-zero when the digest
was correctly zeroed due to sub-quorum reveals.
2026-03-10 08:42:10 +07:00
Nicholas Dudfield
91860db578 fix(consensus): require quorum-many reveals for non-zero entropy
Sub-quorum reveals (e.g. 3/4 threshold) were producing real entropy,
allowing a minority of validators to disproportionately influence the
output. Both injectEntropyPseudoTx and buildExplicitFinalProposalTxSet
now fall back to zero entropy when reveals < quorumThreshold().
2026-03-09 17:13:02 +07:00
Nicholas Dudfield
0b317a8e7a fix(consensus): skip rng pipeline during bootstrap convergence
When prevProposers < quorumThreshold, the network is still converging
and RNG can only produce zero entropy. Skip the commit/reveal pipeline
to avoid PIPELINE_TIMEOUT and conflict-wait delays that compound across
staggered startup rounds.
2026-03-09 16:27:36 +07:00
Nicholas Dudfield
dbd230b695 feat(rpc): add rng state to consensus_info response 2026-03-09 16:05:42 +07:00
Nicholas Dudfield
30cefcba85 chore: clang-format alignment fixes 2026-03-06 18:39:37 +07:00
Nicholas Dudfield
94edb5759d fix(export): gate pre-quorum on verified signature count
hasQuorum() and getExportsWithQuorum() were using raw signerMap.size()
which includes unverified signatures. TxQ could inject a ttEXPORT
pseudo-tx that then fails the stricter verified-signature check in
Change::applyExport(). Use verifiedSignatureCount() instead so TxQ
only injects when cryptographically verified quorum is actually met.

Also add cmake plumbing for enhanced logging: link date::date-tz when
available and enable BEAST_ENHANCED_LOGGING for Debug builds.
2026-03-06 18:38:54 +07:00
Nicholas Dudfield
ce57b6a3a0 fix(consensus): fix rng quorum to active UNL and demote rng log noise
Quorum fix:
- Rename expectedProposers_ → likelyParticipants_ to clarify role
- Fix commit quorum to 80% of active UNL snapshot (not shrinkable by
  recent proposer count, which was allowing 2/3 to pass as quorum)
- hasQuorumOfCommits() now uses simple threshold check only
- Add CSF test: persistent loss does not shrink quorum

Log level cleanup:
- Demote ~30 RNG/STALLDIAG per-peer/per-tick lines from info/debug to
  debug/trace across Consensus.h and RCLConsensus.cpp
- Principle: per-peer/per-tick → trace; state transitions → debug;
  milestones → info
- Reduces testnet log volume by ~93%
2026-03-06 18:36:43 +07:00