Skip to content

feat: add rxrpl support to confluence harness#26

Open
RomThpt wants to merge 4 commits into
XRPL-Commons:mainfrom
RomThpt:feat/add-rxrpl-support
Open

feat: add rxrpl support to confluence harness#26
RomThpt wants to merge 4 commits into
XRPL-Commons:mainfrom
RomThpt:feat/add-rxrpl-support

Conversation

@RomThpt

@RomThpt RomThpt commented May 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds first-class support for rxrpl (Rust XRPL implementation) to the confluence interop testing harness, alongside the existing rippled and goXRPL integrations.

  • New src/rxrpl/rxrpl.star service module
  • topology.star: emits rxrpl TOML configs ([server], [peer], [validators], [validator_identity] with master_secret + ephemeral_seed)
  • main.star: rxrpl_count / rxrpl_image args (default 0 / rxrpl:latest); rxrpl nodes are launched and folded into all_nodes for the test suite
  • dashboard.star: generalized the second-arg name to alt_nodes and labels entries via node[\"type\"] so the dashboard shows both goXRPL and rxrpl
  • goxrpl.star & rxrpl.star: early-return on count == 0 to avoid the configs must be a non-empty dict kurtosis error when a topology requests zero nodes of a given impl
  • rxrpl.star: entrypoint = [\"rxrpl\"] overrides the image's wrapper script so we drive the binary directly; ws port has wait = None because rxrpl's TOML schema only opens an RPC listener

Validation

Ran the consensus suite locally on kurtosis 1.18.2 with {rippled_count: 1, goxrpl_count: 0, rxrpl_count: 1, test_suite: \"consensus\"}:

node state validated_ledger
rippled-0 proposing, peers=1 seq 4, hash 96EF2CA7…CA49B628
rxrpl-0 full, complete_ledgers 1-4 seq 4, hash 96EF2CA7…CA49B628

Both implementations form a private network, complete the P2P handshake, exchange `GetLedger` / `LedgerData` messages, and agree on identical validated-ledger hashes for ledgers #2#4. This is the first end-to-end demonstration of rxrpl ↔ rippled consensus interop on the confluence harness.

Known limitation

After consensus is reached, kurtosis aborts when probing rxrpl's `server_info` because rxrpl does not yet return a top-level `peers` count (extracted by `src/helpers/rpc.star` at line 37). The full consensus assertions (e.g. "all nodes agree on hash after K rounds") therefore don't run to completion in this PR. Fixing it is independent of this integration and can be addressed either by:

  1. adding a `peers` field to rxrpl's `server_info` response (rxrpl side), or
  2. tolerating its absence in `rpc.star` (confluence side).

Happy to follow up with a small companion PR for (2) if preferred — kept it out of scope here to keep the diff focused on adding the impl.

Test plan

  • `kurtosis run . '{"rippled_count":1,"goxrpl_count":0,"rxrpl_count":1,"test_suite":"consensus"}'` — rxrpl + rippled reach matching ledger hash at seq 4
  • Standalone Docker smoke test: `docker run --rm -v ./generated.toml:/etc/rxrpl/rxrpl-0.toml rxrpl:latest run --mode network --config /etc/rxrpl/rxrpl-0.toml` boots, loads validator identity, opens RPC port
  • Full multi-impl topology (e.g. 2 rippled + 1 goXRPL + 2 rxrpl) — left to reviewers as it depends on the `peers` field follow-up

RomThpt added 3 commits May 10, 2026 13:18
Adds rxrpl as a third XRPL node implementation alongside rippled and
goXRPL. rxrpl uses a different TOML schema where trusted validator
pubkeys live inline in [validators].trusted (no separate validators
file) and the signing seed is provided via
[validator_identity].master_secret.

- src/rxrpl/rxrpl.star: service launcher mirroring goxrpl.star.
- src/topology.star: rxrpl_count parameter, _render_rxrpl_config that
  emits the correct TOML schema with inline trusted set + identity.
- main.star: rxrpl_count / rxrpl_image args, launch + include in
  test_suite all_nodes list.
- src/dashboard/dashboard.star: generalize the second arg to alt_nodes
  with a `type` field on each descriptor so rxrpl shows up under its
  own label instead of being mislabeled as goxrpl.

Default rxrpl_count is 0, so existing rippled/goXRPL runs are unaffected.
rxrpl's [validator_identity] requires both master_secret and
ephemeral_seed when the master_secret form is used. Reuse the same
seed for both in the test harness — production setups would rotate
the ephemeral independently.

Also override the image ENTRYPOINT to invoke the rxrpl binary directly
with our generated TOML, bypassing the env-var bootstrap shipped with
the hive-style rxrpl image.
- goxrpl.launch/rxrpl.launch: early-return on count=0 to avoid the
  'configs argument should be a non empty dictionary' kurtosis error
  when the topology requests zero nodes of a given implementation.
- rxrpl.star: mark ws port with wait=None since rxrpl's TOML schema
  only opens an RPC listener; kurtosis was timing out probing 6006.

Validated by running the consensus suite with 1 rippled + 1 rxrpl:
rxrpl syncs ledger XRPL-Commons#2-XRPL-Commons#4 against rippled and reaches identical
ledger hash 96EF2CA73A2A895808CD78BD21FDC61127E741BAC098EF78AE03EF45CA49B628.
@RomThpt

RomThpt commented May 11, 2026

Copy link
Copy Markdown
Collaborator Author

cc @LeJamon — ajoute le support de rxrpl à confluence. Validation kurtosis : rippled + rxrpl atteignent le même hash de ledger validé (96EF2CA7…CA49B628 au seq 4). Détails dans la description.

Use jq's alternative operator (`// default`) on extractors that read
fields not yet emitted by all implementations:
- `server_info.result.info.peers` -> 0 when missing
- `ledger.result.ledger.account_hash` -> "" when missing
- `ledger.result.ledger.transaction_hash` -> "" when missing

Unblocks the consensus suite for rxrpl, whose RPC layer has not yet
implemented these fields. The hash agreement test now passes against
a 1 rippled + 1 rxrpl topology with matching ledger_hash at seq 5
(`9866B9EC…C1DFF924`).
@RomThpt

RomThpt commented May 11, 2026

Copy link
Copy Markdown
Collaborator Author

Update: ran the consensus suite end-to-end with {rippled_count: 1, rxrpl_count: 1, test_suite: "consensus"} and it now passes after the latest commit:

Starlark code successfully run. Output was:
{
  "consensus": {
    "ledger_hash_agreement": "completed",
    "mixed_consensus": "passed"
  }
}

Both nodes reach seq >= 10 and agree on ledger #5 hash 9866B9EC…C1DFF924 and validated_ledger #4 hash 7E7266B2…55136CFC. rxrpl's RPC layer is still missing server_info.peers, ledger.account_hash and ledger.transaction_hash — handled via jq's // default operator in src/helpers/rpc.star. Filing a companion issue on rxrpl to populate those upstream.

@RomThpt

RomThpt commented May 11, 2026

Copy link
Copy Markdown
Collaborator Author

Also tested with a larger mixed topology ({rippled_count: 2, rxrpl_count: 2, test_suite: "consensus"}) — same result, mixed_consensus PASS. All 4 nodes (2 rippled + 2 rxrpl) agree on ledger #5 hash 9CD2D533…79827F85D. Quorum holds across the heterogeneous validator set.

@RomThpt

RomThpt commented May 11, 2026

Copy link
Copy Markdown
Collaborator Author

Investigated whether rxrpl could be made an active proposer in mixed-validator topologies (i.e. emit ProposeSet, not just adopt-validate).

Empirical finding: cherry-picking the 6 reverted fixes (a9aaa37, a845ee2, 8d20d90, b0d238e, fc0a7be, 7b4322f) does make rxrpl call close_ledger and broadcast Validations, but causes a sawtooth pattern in the local ledger store on rxrpl nodes:

rxrpl-1:  #4 ✓   #5 ✗   #6 ✓   #7 ✓   #8 ✗   ...   (lgrNotFound on missing seqs)

Root cause traced in rxrpl logs:

  1. rxrpl catchup-adopts ledger #N from rippled ✓
  2. rxrpl closes #N+1 locally with empty tx_set and its own account_hash
  3. rippled validates #N+1 with a different hash (amendments / flag-ledger account_hash divergence)
  4. wrong prev_ledger detected: 2/2 trusted peers reference X, ours is Y. Triggering recovery (node.rs)
  5. Recovery jumps to #N+2 → #N+1 left as a gap in the store

So the architectural gap is bigger than the 6 fixes: rxrpl's local close path doesn't actually converge tx_sets with rippled (no ProposeSet processing, no amendment vote alignment). Making rxrpl an active proposer requires processing inbound ProposeSet, building a tx_set that converges with rippled, and identical amendment voting — multi-week work, not a single PR.

For this PR's scope, passive-validator mode is the right shipping state: rxrpl follows rippled's chain via catchup, signs a Validation for each adopted ledger, and rippled counts those validations toward quorum. The kurtosis consensus suite passes empirically with this mode. Active proposer is tracked as future work upstream on rxrpl, not on confluence.

@RomThpt

RomThpt commented May 11, 2026

Copy link
Copy Markdown
Collaborator Author

Verified empirically: rxrpl IS validating in the mixed-validator topology. My earlier comment was wrong on that point.

Proof, on a fresh 1-rippled + 1-rxrpl run with rippled raised to debug:

  • rxrpl logs: validator signing identity loaded, local manifest built sequence=1, sent local manifest to <rippled peer_id>, broadcast Validation
  • rippled-0 server_info: validation_quorum: 2, validated_ledger.seq: 20+ (advancing)
  • rippled-0 consensus_info: proposers: 0, previous_proposers: 0 (rippled alone proposing)

Math: with quorum=2 and rippled-0 the only proposer, validated_ledger only advances if a second trusted validation arrives every round. That second validation comes from rxrpl. So rxrpl's mtVALIDATION is accepted as trusted by rippled (manifest binding signing_key→master_key works) and counted toward quorum.

So the actual gap on rxrpl in mixed-validator setups is purely active proposing (no ProposeSet emitted), not validation. The PR description is accurate on this point — rxrpl operates as a passive validator: follows rippled's chain via catchup, signs Validations on adopted ledgers, contributes to rippled's quorum, but does not emit ProposeSet during consensus rounds.

Tracking active proposing as separate upstream work on rxrpl (tx_set convergence + amendment vote alignment + processing inbound ProposeSet).

@RomThpt

RomThpt commented May 11, 2026

Copy link
Copy Markdown
Collaborator Author

Opened RomThpt/rxrpl#76 to track active-proposer work on the rxrpl side. Includes the root cause analysis (close gate + tx_set/account_hash divergence), why the 6 previously-attempted fixes regressed (sawtooth ledger gaps from wrong_prev_ledger recovery), the minor decoder finding (ledger_seq: 0 hardcoded), and the multi-step plan required.

Shipping this confluence PR as-is, with passive-validator mode validated empirically. Active proposing remains future work upstream on rxrpl.

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.

1 participant