From 52b057a1032bfba04547032d34892e45a7f84d2f Mon Sep 17 00:00:00 2001 From: RomThpt Date: Wed, 6 May 2026 16:47:29 +0200 Subject: [PATCH] fix(sync): switch rippled to xrpllabsofficial + retry account_info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rippleci/rippled:develop image was a CI dev build that loads the mainnet validator UNL (vl.ripple.com / vl.xrplf.org) by default. In a private test network this surfaced as: - hundreds of `is_trusted: 0` validation messages from real mainnet validators flooding the consensus log - PeerFinder bootcache trying real mainnet IPs (3.77.5.186 etc.) - tx submitted with tesSUCCESS getting stranded in open ledger because the local consensus circle never closed cleanly Three changes: 1. Base image -> `xrpllabsofficial/xrpld:latest` (3.1.2 stable). Debian based, runs as root, ships rippled release binaries. Smaller, faster to pull, and behaves predictably in a closed test net. 2. Empty `[validator_list_sites]` and `[validator_list_keys]` sections in rippled.cfg. Without these, rippled fetches the mainnet UNL on boot. With them empty, rippled trusts only what's in validators.txt — i.e. the keys hive uploaded. 3. Retry `account_info` on the late joiner. rippled returns `noNetwork` while `server_state == connected` (validations seen but state not yet replayed). The previous test failed `account_info` immediately after `WaitForLedger` returned, racing rippled's catchup. Retry up to 120s; rxrpl answers on first call so the loop is a no-op for it. Result on `--client rxrpl,rippled` (4 sub-tests): initial=rxrpl late=rxrpl PASS initial=rxrpl late=rippled FAIL (rippled catch-up from rxrpl UNL — separate issue) initial=rippled late=rxrpl PASS (cross-impl signal) initial=rippled late=rippled PASS (canonical homogeneous case now green) 3/4 sub-tests pass. The remaining failure is rippled-side catchup from a non-rippled-validated UNL and needs follow-up investigation. --- clients/rippled/Dockerfile | 12 +++++------- clients/rippled/xrpl_start.sh | 10 ++++++++++ simulators/sync/main.go | 20 ++++++++++++++++---- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/clients/rippled/Dockerfile b/clients/rippled/Dockerfile index a97e032..2f7adae 100644 --- a/clients/rippled/Dockerfile +++ b/clients/rippled/Dockerfile @@ -1,13 +1,11 @@ -ARG baseimage=rippleci/rippled -ARG tag=develop +ARG baseimage=xrpllabsofficial/xrpld +ARG tag=latest FROM --platform=linux/amd64 $baseimage:$tag -# Recent rippled images run as non-root user 'xrpld'; switch back to root for setup. -USER root - -# Install python3 for validators.json conversion (RHEL-based image). -RUN dnf install -y python3 && dnf clean all || true +# xrpllabsofficial image is Debian-based, runs as root, ships rippled +# release binaries with proper amd64/arm64 support and no rippleci dev +# noise. xrpl_start.sh uses pure shell, so we don't even need python3. # Write version. RUN rippled --version > /version.txt 2>&1 || echo "rippled-unknown" > /version.txt diff --git a/clients/rippled/xrpl_start.sh b/clients/rippled/xrpl_start.sh index 26308b8..27c3349 100644 --- a/clients/rippled/xrpl_start.sh +++ b/clients/rippled/xrpl_start.sh @@ -57,6 +57,16 @@ $VALIDATORS [ledger_history] ${XRPL_LEDGER_HISTORY:-256} + +# Empty publisher sections so rippled does NOT fetch the mainnet UNL +# (vl.ripple.com / vl.xrplf.org). Without these, rippled in a private +# test network ends up tracking mainnet validators in its untrusted +# set — which floods the consensus log with `is_trusted: 0` +# validations and slows down close cadence on a small test network. +[validator_list_sites] + +[validator_list_keys] + CFGEOF # Validator seed. diff --git a/simulators/sync/main.go b/simulators/sync/main.go index 11fbd3f..1d45cf7 100644 --- a/simulators/sync/main.go +++ b/simulators/sync/main.go @@ -166,10 +166,22 @@ func makeLateJoinTest(initialClient, lateClient string) func(t *xrplsim.T) { t.Fatalf("late-join %s node did not sync to ledger %d: %v", lateClient, targetLedger, err) } - // Verify the late joiner has the account we created. - acct, err := lateRPC.AccountInfo("rPMh7Pi9ct699iZUTWz6CFkakUy5Ju9f9v") - if err != nil { - t.Fatal("late-join node doesn't have the account:", err) + // rippled returns `noNetwork` from account_info while server_state + // is "connected" (validations seen but state not yet replayed). + // Retry until rippled finishes catchup or we time out. rxrpl + // answers immediately so the loop exits on first try. + var acct *xrplsim.AccountInfoResult + var lastErr error + acctDeadline := time.Now().Add(120 * time.Second) + for time.Now().Before(acctDeadline) { + acct, lastErr = lateRPC.AccountInfo("rPMh7Pi9ct699iZUTWz6CFkakUy5Ju9f9v") + if lastErr == nil { + break + } + time.Sleep(2 * time.Second) + } + if lastErr != nil { + t.Fatalf("late-join %s node didn't replay state for account in time: %v", lateClient, lastErr) } t.Logf("late-join %s synced from %s network — account balance: %s", lateClient, initialClient, acct.Balance) }