Skip to content

Instantly share code, notes, and snippets.

@mankins
Created November 11, 2025 11:52
Show Gist options
  • Select an option

  • Save mankins/814842c42d2f15982dc3be3fa9678db4 to your computer and use it in GitHub Desktop.

Select an option

Save mankins/814842c42d2f15982dc3be3fa9678db4 to your computer and use it in GitHub Desktop.
compare xrpl unl lists

compare xrpl unl lists

You need to have the xrpl_vl_tool in the same directory at the moment.

usage

./xrpdiff.sh https://vl.ripple.com https://unl.xrplf.org/unl/next.json
#!/usr/bin/env bash
set -euo pipefail
# Usage: xrpdiff.sh URL1 URL2
# Optionally set XRPL_VL_TOOL (defaults to ./xrpl_vl_tool)
# XRPL_VL_TOOL=/path/to/xrpl_vl_tool ./xrpdiff.sh https://vl.ripple.com https://unl.xrplf.org
XRPL_VL_TOOL="${XRPL_VL_TOOL:-./xrpl_vl_tool}"
URL1="${1:?usage: $0 URL1 URL2}"
URL2="${2:?usage: $0 URL1 URL2}"
load() { "$XRPL_VL_TOOL" load "$1"; }
summarize() {
load "$1" | awk '
BEGIN { cnt=""; seqno=""; expires_str=""; ver=""; master="" }
/^There are/ {
cnt=$3
tmp=$0
sub(/^.*Sequence is:[[:space:]]*/, "", tmp)
seqno=tmp; sub(/[^0-9].*$/, "", seqno)
tmp=$0
sub(/^.*Expires:[[:space:]]*/, "", tmp)
expires_str=tmp; sub(/[[:space:]]*\|.*$/, "", expires_str)
tmp=$0
sub(/^.*Version:[[:space:]]*/, "", tmp)
ver=tmp; sub(/[^0-9].*$/, "", ver)
}
/^Publisher Master Public Key:/ {
sub(/^Publisher Master Public Key:[[:space:]]*/, "", $0)
master=$0
}
END {
printf "count=%s sequence=%s expires=\"%s\" version=%s master=%s\n", cnt, seqno, expires_str, ver, master
}
'
}
# HEX-only (for set math)
keys() {
load "$1" | awk '/^Validator:/ { print $2 }' | LC_ALL=C sort -u
}
# HEX|NODE|DOMAIN (NODE is base58 in parens; DOMAIN may be empty)
# HEX|NODE|DOMAIN (NODE is base58 in parens; DOMAIN may be empty)
key_node_domain() {
load "$1" | awk -F'|' '
/^Validator:/ {
left = $1
sub(/^Validator:[[:space:]]*/, "", left)
# HEX is the first token before any space
hex = left
sub(/[[:space:]].*$/, "", hex)
# NODE: extract text between first "(" and next ")"
node = ""
lp = index(left, "(")
if (lp > 0) {
rest = substr(left, lp + 1)
rp = index(rest, ")")
if (rp > 0) {
node = substr(rest, 1, rp - 1)
}
}
# DOMAIN is field 3 (after the second "|"), if present
dom = ($3 == "" ? "" : $3)
sub(/^[[:space:]]*/, "", dom)
print hex "|" node "|" dom
}
' | LC_ALL=C sort -u
}
# Temp files
k1=$(mktemp); k2=$(mktemp)
f1=$(mktemp); f2=$(mktemp)
trap 'rm -f "$k1" "$k2" "$f1" "$f2"' EXIT
keys "$URL1" > "$k1"
keys "$URL2" > "$k2"
key_node_domain "$URL1" > "$f1"
key_node_domain "$URL2" > "$f2"
echo "== Summary =="
echo "URL1: $URL1"
summarize "$URL1"
echo "URL2: $URL2"
summarize "$URL2"
echo
fmt_line() {
# args: hex node domain
local hex="$1" node="$2" dom="$3"
if [ -n "$node" ] && [ -n "$dom" ]; then
printf "%s (%s) | %s\n" "$hex" "$node" "$dom"
elif [ -n "$node" ]; then
printf "%s (%s)\n" "$hex" "$node"
elif [ -n "$dom" ]; then
printf "%s | %s\n" "$hex" "$dom"
else
printf "%s\n" "$hex"
fi
}
# Added
echo "== Added (present in URL2, not in URL1) =="
comm -13 "$k1" "$k2" | awk -v F="$f2" '
BEGIN {
FS="|"
while ((getline < F) > 0) { m[$1]=$2 "|" $3 }
close(F)
}
{
hex=$0
split(m[hex], parts, "|")
node=parts[1]; dom=parts[2]
printf " + %s", hex
if (node != "") printf " (%s)", node
if (dom != "") printf " | %s", dom
printf "\n"
}
'
echo
# Removed
echo "== Removed (present in URL1, not in URL2) =="
comm -23 "$k1" "$k2" | awk -v F="$f1" '
BEGIN {
FS="|"
while ((getline < F) > 0) { m[$1]=$2 "|" $3 }
close(F)
}
{
hex=$0
split(m[hex], parts, "|")
node=parts[1]; dom=parts[2]
printf " - %s", hex
if (node != "") printf " (%s)", node
if (dom != "") printf " | %s", dom
printf "\n"
}
'
echo
# Domain changes
echo "== Domain changes (same key, different domain) =="
awk -F'|' '
FNR==NR { node1[$1]=$2; dom1[$1]=$3; next }
{
hex=$1; node2=$2; dom2=$3
if (hex in dom1 && dom1[hex] != dom2) {
printf " * %s", hex
if (node2 != "") printf " (%s)", node2
printf "\n URL1: %s\n URL2: %s\n", dom1[hex], dom2
}
}
' "$f1" "$f2" || true
echo
@mankins
Copy link
Author

mankins commented Nov 11, 2025

sample output:

./xrpdiff.sh https://vl.ripple.com https://unl.xrplf.org/unl/next.json
== Summary ==
URL1: https://vl.ripple.com
count=35 sequence=81 expires="2026-01-17 10:09:21" version=1 master=ED2677ABFFD1B33AC6FBC3062B71F1E8397C1505E1C42C64D11AD1B28FF73F4734
URL2: https://unl.xrplf.org/unl/next.json
count=35 sequence=2025111076 expires="2026-03-10 19:02:06" version=1 master=ED42AEC58B701EEBB77356FFFEC26F83C1F0407263530F068C7C73D392C7E06FD1

== Added (present in URL2, not in URL1) ==
 + ED6FCBE961C9B67924155C84AE192023606385DC7BDED3ECFDB6F117FBE12EE8C3 (nHUUgpUVNxXfxkkoyh2QDjfLfHapcut8gYwKeShnJYd3SdPui19A) | peersyst.cloud
 + EDCAD6E02AAFF5467465CBB9E62E021BF4B8E23F7484A6F0F67387549733865CCA (nHUtmbn4ALrdU6U8pmd8AMt4qKTdZTbYJ3u1LHyAzXga3Zuopv5Y) | bifrostwallet.com
 + EDD3DB9E85A9B26772464BE9FCE120007E504AAF7AFF4648FA24E155B35A48FE6D (nHUxjxKPeErbN7pNk9UWA5Ee7ZPMtesSeRGJtmdqkTxe94tqM2YX) | tequ.dev

== Removed (present in URL1, not in URL2) ==
 - ED580AD4FA5DA989FA999535ECC20197A5B53A1A49A971F6652ED8D5D466CA605D (nHUpDEZX5Zy9auiu4yhDmhirNu6PyB1LvzQEL9Mxmqjr818w663q) | xspectar.com
 - ED7FB8CC142B70AC837A9A0F57B38EB17DD569E388F33558D90B60170F1ACA5F1E (nHUKgFa4diHC9sN7YnVt4AUPGkaHtShrT76xJBUFdH2B5Tz5nwVQ) | aureusox.com
 - EDC2A138B3771C208965596D4D372331C17A5476BD2CE2BC7A6D3CD273DF330D99 (nHUq9tJvSyoXQKhRytuWeydpPjvTz3M9GfUpEqfsg9xsewM7KkkK)

== Domain changes (same key, different domain) ==

@Vjkhannaripple
Copy link

Hey @mankins
I see "aureusox.com" being removed, and "bifrostwallet.com" is added to the output.
Is this accurate?

@mankins
Copy link
Author

mankins commented Nov 11, 2025

@Vjkhannaripple the above output just shows a sample for this script run! ...not related to our next unl publish.

this is the current state of the next unl publish relative to the current, according to this script.

./xrpdiff.sh https://vl.ripple.com https://unl.xrplf.org/unl/next.json
== Summary ==
URL1: https://vl.ripple.com
count=35 sequence=81 expires="2026-01-17 10:09:21" version=1 master=ED2677ABFFD1B33AC6FBC3062B71F1E8397C1505E1C42C64D11AD1B28FF73F4734
URL2: https://unl.xrplf.org/unl/next.json
count=35 sequence=2025111163 expires="2026-03-11 15:55:38" version=1 master=ED42AEC58B701EEBB77356FFFEC26F83C1F0407263530F068C7C73D392C7E06FD1

== Added (present in URL2, not in URL1) ==
 + ED6FCBE961C9B67924155C84AE192023606385DC7BDED3ECFDB6F117FBE12EE8C3 (nHUUgpUVNxXfxkkoyh2QDjfLfHapcut8gYwKeShnJYd3SdPui19A) | peersyst.cloud
 + EDD3DB9E85A9B26772464BE9FCE120007E504AAF7AFF4648FA24E155B35A48FE6D (nHUxjxKPeErbN7pNk9UWA5Ee7ZPMtesSeRGJtmdqkTxe94tqM2YX) | tequ.dev

== Removed (present in URL1, not in URL2) ==
 - ED580AD4FA5DA989FA999535ECC20197A5B53A1A49A971F6652ED8D5D466CA605D (nHUpDEZX5Zy9auiu4yhDmhirNu6PyB1LvzQEL9Mxmqjr818w663q) | xspectar.com
 - EDC2A138B3771C208965596D4D372331C17A5476BD2CE2BC7A6D3CD273DF330D99 (nHUq9tJvSyoXQKhRytuWeydpPjvTz3M9GfUpEqfsg9xsewM7KkkK)

== Domain changes (same key, different domain) ==

@Vjkhannaripple
Copy link

Ah thanks @mankins . This is helpful.
Appreciate the quick turnaround.

@mankins
Copy link
Author

mankins commented Nov 11, 2025

In case anyone needs it, here's the mentioned tool: https://github.com/elmurci/xrpl-vl-tool

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment