From 0c8a3cc4b0fca0f07d772a66bbbe5828c9db05fa Mon Sep 17 00:00:00 2001 From: Claude Agent Date: Thu, 2 Jul 2026 11:46:03 +0000 Subject: [PATCH] ripple: deployable mainnet + testnet rippled with health checks Fix mainnet rippled.cfg (ws,http RPC, port_peer 13331), strip eth-isms from x-upstreams, add testnet chain assets/compose, and wire --ripple sync-status via check-health.sh server_info probe. Co-authored-by: Cursor --- check-health.sh | 45 +++++++++- compose_registry.json | 16 ++++ ripple/mainnet/rippled.cfg | 15 ++-- .../ripple-mainnet-rippled-pruned-nudb.yml | 10 +-- .../ripple-testnet-rippled-pruned-nudb.yml | 89 +++++++++++++++++++ ripple/testnet/rippled.cfg | 88 ++++++++++++++++++ ripple/testnet/validators.txt | 5 ++ sync-status.sh | 12 ++- 8 files changed, 261 insertions(+), 19 deletions(-) create mode 100644 ripple/rippled/ripple-testnet-rippled-pruned-nudb.yml create mode 100644 ripple/testnet/rippled.cfg create mode 100644 ripple/testnet/validators.txt diff --git a/check-health.sh b/check-health.sh index 6402770a..cb639851 100755 --- a/check-health.sh +++ b/check-health.sh @@ -10,10 +10,11 @@ fi RPC_URL=$1 shift -# Check for --starknet / --aztec / --cosmos flag +# Check for --starknet / --aztec / --cosmos / --ripple flag is_starknet=false is_aztec=false is_cosmos=false +is_ripple=false if [ "$1" == "--starknet" ]; then is_starknet=true shift @@ -23,6 +24,9 @@ elif [ "$1" == "--aztec" ]; then elif [ "$1" == "--cosmos" ]; then is_cosmos=true shift +elif [ "$1" == "--ripple" ]; then + is_ripple=true + shift fi REF="" @@ -59,6 +63,45 @@ if $is_cosmos; then echo "online"; exit 0 fi +# XRPL rippled: server_info — server_state full/proposing + fresh validated ledger = online. +# Short-circuits here; the EVM/starknet/aztec block-comparison path below is not used. +if $is_ripple; then + status=$(curl -L --ipv4 -m $timeout -s -X POST -H "Content-Type: application/json" --data '{"method":"server_info"}' "$RPC_URL") + if [ $? -ne 0 ] || [ -z "$status" ]; then echo "error"; exit 1; fi + server_state=$(echo "$status" | jq -r '.result.info.server_state // .info.server_state' 2>/dev/null) + ledger_age=$(echo "$status" | jq -r '.result.info.validated_ledger.age // .info.validated_ledger.age' 2>/dev/null) + node_seq=$(echo "$status" | jq -r '.result.info.validated_ledger.seq // .info.validated_ledger.seq' 2>/dev/null) + if [ -z "$server_state" ] || [ "$server_state" = "null" ]; then echo "error"; exit 1; fi + case "$server_state" in + connected|syncing|tracking) + echo "syncing"; exit 1 ;; + full|proposing) + ;; + *) + echo "error"; exit 1 ;; + esac + if [ -z "$ledger_age" ] || [ "$ledger_age" = "null" ] || [ "$ledger_age" -gt 20 ] 2>/dev/null; then + echo "behind"; exit 1 + fi + if [ -n "$ref" ]; then + ref_status=$($BASEPATH/multicurl.sh -L --ipv4 -m $timeout -s -X POST -H "Content-Type: application/json" --data '{"method":"server_info"}' $ref) + ref_seq=$(echo "$ref_status" | jq -r '.result.info.validated_ledger.seq // .info.validated_ledger.seq' 2>/dev/null) + if [ -n "$ref_seq" ] && [ "$ref_seq" != "null" ] && [ "$ref_seq" -gt 0 ] 2>/dev/null && [ -n "$node_seq" ] && [ "$node_seq" != "null" ]; then + lag=$(( ref_seq - node_seq )) + lo=${LAGGING_LAG:-5}; hi=${SYNCING_LAG:-10} + if [ "$lo" -gt "$hi" ]; then tmp=$lo; lo=$hi; hi=$tmp; fi + if [ "$lag" -le "$lo" ]; then + echo "online"; exit 0 + elif [ "$lag" -le "$hi" ]; then + echo "lagging"; exit 0 + else + echo "syncing"; exit 1 + fi + fi + fi + echo "online"; exit 0 +fi + response_file=$(mktemp) # Use appropriate RPC method based on chain type diff --git a/compose_registry.json b/compose_registry.json index 70eeddad..1df46b38 100644 --- a/compose_registry.json +++ b/compose_registry.json @@ -7901,6 +7901,22 @@ "ripple-mainnet-rippled-pruned-nudb" ] }, + { + "chain": "testnet", + "client": "rippled", + "compose_file": "ripple/rippled/ripple-testnet-rippled-pruned-nudb", + "features": [ + "nudb" + ], + "network": "ripple", + "node": null, + "relay": null, + "stack": null, + "type": "pruned", + "volumes": [ + "ripple-testnet-rippled-pruned-nudb" + ] + }, { "chain": "mainnet", "client": "ronin", diff --git a/ripple/mainnet/rippled.cfg b/ripple/mainnet/rippled.cfg index b9317bb0..aaa333f0 100644 --- a/ripple/mainnet/rippled.cfg +++ b/ripple/mainnet/rippled.cfg @@ -1,8 +1,7 @@ [server] port_rpc_admin_local port_ws_public - -# port_peer +port_peer # port_ws_admin_local # ssl_key = /etc/ssl/private/server.key # ssl_cert = /etc/ssl/certs/server.crt @@ -16,12 +15,12 @@ protocol = http [port_ws_public] port = 8545 ip = 0.0.0.0 -protocol = ws +protocol = ws,http -# [port_peer] -# port = 51235 -# ip = 0.0.0.0 -# protocol = peer +[port_peer] +port = 13331 +ip = 0.0.0.0 +protocol = peer # [port_ws_admin_local] # port = 6006 @@ -83,4 +82,4 @@ validators.txt # fatal [ssl_verify] -1 \ No newline at end of file +1 diff --git a/ripple/rippled/ripple-mainnet-rippled-pruned-nudb.yml b/ripple/rippled/ripple-mainnet-rippled-pruned-nudb.yml index 849404c2..786c0ffb 100644 --- a/ripple/rippled/ripple-mainnet-rippled-pruned-nudb.yml +++ b/ripple/rippled/ripple-mainnet-rippled-pruned-nudb.yml @@ -30,7 +30,7 @@ x-logging-defaults: &logging-defaults services: ripple-mainnet: - image: ${RIPPLE_RIPPLED_IMAGE:-xrpllabsofficial/xrpld}:${RIPPLE_MAINNET_RIPPLED_VERSION:-3.1.3} + image: ${RIPPLE_RIPPLED_IMAGE:-xrpllabsofficial/xrpld}:${RIPPLE_MAINNET_RIPPLED_VERSION:-3.2.0} sysctls: # TCP Performance net.ipv4.tcp_slow_start_after_idle: 0 # Disable slow start after idle @@ -86,12 +86,4 @@ x-upstreams: msgSize: 50Mb url: $${WS_URL} chain: ripple - method-groups: - enabled: - - debug - - filter - methods: - disabled: - enabled: - - name: txpool_content # TODO: should be disabled for rollup nodes ... \ No newline at end of file diff --git a/ripple/rippled/ripple-testnet-rippled-pruned-nudb.yml b/ripple/rippled/ripple-testnet-rippled-pruned-nudb.yml new file mode 100644 index 00000000..3d860bae --- /dev/null +++ b/ripple/rippled/ripple-testnet-rippled-pruned-nudb.yml @@ -0,0 +1,89 @@ +--- +x-logging-defaults: &logging-defaults + driver: json-file + options: + max-size: "10m" + max-file: "3" + +# Usage: +# +# mkdir rpc && cd rpc +# +# git init +# git remote add origin https://github.com/StakeSquid/ethereum-rpc-docker.git +# git fetch origin vibe +# git checkout origin/vibe +# +# docker run --rm alpine sh -c "printf '0x'; head -c32 /dev/urandom | xxd -p -c 64" > .jwtsecret +# +# env +# ... +# IP=$(curl ipinfo.io/ip) +# DOMAIN=${IP}.traefik.me +# COMPOSE_FILE=base.yml:rpc.yml:ripple/rippled/ripple-testnet-rippled-pruned-nudb.yml +# +# docker compose up -d +# +# curl -X POST https://${IP}.traefik.me/ripple-testnet \ +# -H "Content-Type: application/json" \ +# --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' + +services: + ripple-testnet: + image: ${RIPPLE_RIPPLED_IMAGE:-xrpllabsofficial/xrpld}:${RIPPLE_TESTNET_RIPPLED_VERSION:-3.2.0} + sysctls: + # TCP Performance + net.ipv4.tcp_slow_start_after_idle: 0 # Disable slow start after idle + net.ipv4.tcp_no_metrics_save: 1 # Disable metrics cache + net.ipv4.tcp_rmem: 4096 87380 16777216 # Increase TCP read buffers + net.ipv4.tcp_wmem: 4096 87380 16777216 # Increase TCP write buffers + net.core.somaxconn: 32768 # Higher connection queue + # Memory/Connection Management + # net.core.netdev_max_backlog: 50000 # Increase network buffer + net.ipv4.tcp_max_syn_backlog: 30000 # More SYN requests + net.ipv4.tcp_max_tw_buckets: 2000000 # Allow more TIME_WAIT sockets + ulimits: + nofile: 1048576 # Max open files (for RPC/WS connections) + user: root + ports: + - 12986:12986 + - 12986:12986/udp + expose: + - 8545 + restart: unless-stopped + stop_grace_period: 5m + networks: + - chains + volumes: + - ${RIPPLE_TESTNET_RIPPLED_PRUNED_NUDB_DATA:-ripple-testnet-rippled-pruned-nudb}:/var/lib/rippled/db + - ./ripple/testnet:/config + - /slowdisk:/slowdisk + logging: *logging-defaults + labels: + - prometheus-scrape.enabled=false + - traefik.enable=true + - traefik.http.middlewares.ripple-testnet-rippled-pruned-nudb-stripprefix.stripprefix.prefixes=/ripple-testnet + - traefik.http.services.ripple-testnet-rippled-pruned-nudb.loadbalancer.server.port=8545 + - ${NO_SSL:-traefik.http.routers.ripple-testnet-rippled-pruned-nudb.entrypoints=websecure} + - ${NO_SSL:-traefik.http.routers.ripple-testnet-rippled-pruned-nudb.tls.certresolver=myresolver} + - ${NO_SSL:-traefik.http.routers.ripple-testnet-rippled-pruned-nudb.rule=Host(`$DOMAIN`) && (Path(`/ripple-testnet`) || Path(`/ripple-testnet/`))} + - ${NO_SSL:+traefik.http.routers.ripple-testnet-rippled-pruned-nudb.rule=Path(`/ripple-testnet`) || Path(`/ripple-testnet/`)} + - traefik.http.routers.ripple-testnet-rippled-pruned-nudb.middlewares=ripple-testnet-rippled-pruned-nudb-stripprefix, ipallowlist + +volumes: + ripple-testnet-rippled-pruned-nudb: + +x-upstreams: + - id: $${ID} + labels: + provider: $${PROVIDER} + connection: + generic: + rpc: + url: $${RPC_URL} + ws: + frameSize: 20Mb + msgSize: 50Mb + url: $${WS_URL} + chain: ripple-testnet +... \ No newline at end of file diff --git a/ripple/testnet/rippled.cfg b/ripple/testnet/rippled.cfg new file mode 100644 index 00000000..fa577fac --- /dev/null +++ b/ripple/testnet/rippled.cfg @@ -0,0 +1,88 @@ +[server] +port_rpc_admin_local +port_ws_public +port_peer +# port_ws_admin_local +# ssl_key = /etc/ssl/private/server.key +# ssl_cert = /etc/ssl/certs/server.crt + +[port_rpc_admin_local] +port = 5005 +ip = 127.0.0.1 +admin = 127.0.0.1 +protocol = http + +[port_ws_public] +port = 8545 +ip = 0.0.0.0 +protocol = ws,http + +[port_peer] +port = 12986 +ip = 0.0.0.0 +protocol = peer + +# [port_ws_admin_local] +# port = 6006 +# ip = 127.0.0.1 +# admin = 127.0.0.1 +# protocol = ws + +[node_size] +small + +# tiny +# small +# medium +# large +# huge + +[node_db] +type=NuDB +path=/var/lib/rippled/db/nudb +advisory_delete=0 + +# How many ledgers do we want to keep (history)? +# Integer value that defines the number of ledgers +# between online deletion events +online_delete=256 + +[ledger_history] +# How many ledgers do we want to keep (history)? +# Integer value (ledger count) +# or (if you have lots of TB SSD storage): 'full' +256 + +[database_path] +/var/lib/rippled/db + +[debug_logfile] +/var/lib/rippled/db/debug.log + +[sntp_servers] +time.windows.com +time.apple.com +time.nist.gov +pool.ntp.org + +[ips] +s.altnet.rippletest.net 51235 + +[network_id] +testnet + +[validators_file] +validators.txt + +[rpc_startup] +{ "command": "log_level", "severity": "info" } + +# severity (order: lots of information .. only errors) +# debug +# info +# warn +# error +# fatal + +[ssl_verify] +1 diff --git a/ripple/testnet/validators.txt b/ripple/testnet/validators.txt new file mode 100644 index 00000000..400ae977 --- /dev/null +++ b/ripple/testnet/validators.txt @@ -0,0 +1,5 @@ +[validator_list_sites] + https://vl.altnet.rippletest.net + +[validator_list_keys] + ED264807102805220DA0F312E71FC2C69E1552C9C5790F6C25E3729DEB573D5860 diff --git a/sync-status.sh b/sync-status.sh index 1f0058ec..70bf8b0d 100755 --- a/sync-status.sh +++ b/sync-status.sh @@ -54,7 +54,7 @@ for path in $pathlist; do fi case "$protocol" in - eth|starknet|aztec|cosmos) + eth|starknet|aztec|cosmos|ripple) ;; *) # Protocol family known from the registry but no probe support @@ -68,9 +68,11 @@ for path in $pathlist; do is_starknet=false is_aztec=false is_cosmos=false + is_ripple=false [ "$protocol" = "starknet" ] && is_starknet=true [ "$protocol" = "aztec" ] && is_aztec=true [ "$protocol" = "cosmos" ] && is_cosmos=true + [ "$protocol" = "ripple" ] && is_ripple=true ref='' if [ -n "$2" ]; then @@ -135,6 +137,12 @@ for path in $pathlist; do if [ -n "$chain_slug" ]; then ref=$($BASEPATH/reference-rpc-endpoint.sh --chain "$chain_slug" 2>/dev/null) || ref="" fi + elif $is_ripple; then + # XRPL: no chainid. Reference by drpc slug (optional — server_state is + # authoritative; ref only adds a validated-ledger seq gap check). + if [ -n "$chain_slug" ]; then + ref=$($BASEPATH/reference-rpc-endpoint.sh --chain "$chain_slug" 2>/dev/null) || ref="" + fi else # Ethereum chain ID detection chain_id_response=$(curl -L --ipv4 -m 1 -s -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' $RPC_URL) @@ -186,6 +194,8 @@ for path in $pathlist; do timeout "$HC_TIMEOUT" $BASEPATH/check-health.sh "$RPC_URL" --starknet $ref elif $is_cosmos; then timeout "$HC_TIMEOUT" $BASEPATH/check-health.sh "$RPC_URL" --cosmos $ref + elif $is_ripple; then + timeout "$HC_TIMEOUT" $BASEPATH/check-health.sh "$RPC_URL" --ripple $ref else timeout "$HC_TIMEOUT" $BASEPATH/check-health.sh "$RPC_URL" $ref fi