Skip to content

fix(sync): trust only initial validators + disable peer_private#10

Merged
LeJamon merged 3 commits into
XRPL-Commons:mainfrom
RomThpt:fix/sync-late-joiner-not-trusted
Jun 4, 2026
Merged

fix(sync): trust only initial validators + disable peer_private#10
LeJamon merged 3 commits into
XRPL-Commons:mainfrom
RomThpt:fix/sync-late-joiner-not-trusted

Conversation

@RomThpt

@RomThpt RomThpt commented May 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Two follow-on fixes after #9 to make the cross-impl late-join sync actually run end-to-end.

1. Trusted UNL = initial validators only

The topology was being built with numInitial + 1 keys so the late joiner had a slot, but WithValidatorConfig uploads the full key set as the trusted UNL on every node. rippled then applies its BFT rule (quorum = ceil(0.8 * trusted_count)), which on a 3-key list demands quorum=3 — and the 2-node initial network never validates.

rxrpl honored the configured XRPL_VALIDATION_QUORUM directly, so the rxrpl-only test passed and masked this. Drop the late joiner from the trusted set; it's a pure observer that catches up via received validations and doesn't need to be trusted itself.

2. peer_private=0 for sync tests

xrplsim/topology.go:EnvForNode hardcodes XRPL_PEER_PRIVATE=1. With that, rippled servers respond to non-fixed inbound connections with a 503 redirect (processResponse: failed to connect to peer: redirected). The late joiner's IP is not in the initial nodes' [ips_fixed] at boot, so it can never establish a peer link.

Override to 0 in the sync suite — rxrpl ignores the flag entirely (which is why the rxrpl-only path was unaffected by either issue).

Result on `--client rxrpl,rippled` (4 sub-tests):

  • ✅ `initial=rxrpl late=rxrpl` — PASS
  • ❌ `initial=rxrpl late=rippled` — fail (rippled catch-up from rxrpl-only UNL — separate issue, observable in rippled logs as STATE->disconnected despite peer link up)
  • ✅ `initial=rippled late=rxrpl` — PASS (the cross-impl signal: rxrpl follows a rippled-validated chain end-to-end, account balance verified)
  • ❌ `initial=rippled late=rippled` — fail (test-side: payment isn't yet in the first 10 ledgers under rippled's pacing — needs a longer tx-confirmation wait)

Why the partial fail is still progress

Before this PR the suite reported 0/1 passing in mixed mode and never actually exercised cross-impl sync. After this PR the canonical mainnet-shaped scenario "a fresh rxrpl observes a rippled-validated network and catches up" passes empirically, account state included.

Test plan

  • `go build ./simulators/sync/` clean
  • `--client rxrpl` (single impl) still passes the single test
  • `--client rxrpl,rippled` reports 4 sub-tests with 2 passing including `initial=rippled late=rxrpl`
  • follow-up: investigate rippled→rxrpl-validated catch-up path (the failing test 2)
  • follow-up: extend WaitForLedger or move the tx earlier so rippled→rippled sees the payment

RomThpt added 2 commits May 6, 2026 13:43
Two follow-on fixes after XRPL-Commons#9 to make the cross-impl late-join sync
actually run end-to-end:

1. Trusted UNL = initial validators only.
   The topology was being built with `numInitial + 1` keys so the
   late joiner had a slot, but `WithValidatorConfig` uploads the
   full key set as the trusted UNL on every node. rippled then
   applies its BFT rule (quorum = ceil(0.8 * trusted_count)), which
   on a 3-key list demands quorum=3 — and the 2-node initial network
   never validates. rxrpl honored the configured quorum directly so
   the rxrpl-only test masked this. Drop the late joiner from the
   trusted set; it's a pure observer that catches up via received
   validations and doesn't need to be trusted itself.

2. peer_private=0 for sync tests.
   `topology.go:EnvForNode` hardcodes XRPL_PEER_PRIVATE=1. With
   that, rippled servers respond to non-fixed inbound connections
   with a 503 redirect ("processResponse: failed to connect to
   peer: redirected"). The late joiner's IP is not in the initial
   nodes' [ips_fixed] at boot, so it can never establish a peer
   link. Override to 0 in the sync suite — rxrpl ignores the flag
   entirely (which is why the rxrpl-only path was unaffected).

Result on `--client rxrpl,rippled` (4 sub-tests):
  initial=rxrpl   late=rxrpl     PASS
  initial=rxrpl   late=rippled   fail (rippled catch-up from rxrpl)
  initial=rippled late=rxrpl     PASS  <-- the cross-impl signal
  initial=rippled late=rippled   fail (test-side: payment not yet
                                       in the first 10 ledgers
                                       under rippled's pacing)

The two remaining failures are independent issues: the second is
a rippled bug surface for catch-up from a non-rippled UNL, the
fourth is a test-pacing artifact. Both can be tackled in follow-ups
without further changes to this scaffolding.
After the previous fix the rippled→rippled sub-test still failed with
"account not found": WaitForLedger(10) returned as soon as the late
joiner saw a validated ledger ≥10, but the payment is in ledger ~11+
under rippled's idle pacing. Late joiner could be at exactly ledger 10
when AccountInfo runs.

Use the same target ledger (15) for both the initial-network
post-payment wait and the late-joiner sync wait. Bump the late-joiner
timeout to 240s to leave headroom for catch-up under rippled's
slower close cadence.
@LeJamon LeJamon merged commit d501123 into XRPL-Commons:main Jun 4, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants