Waterfall Node v2 for Docker: Stake Delegation, Key Portability and Built-in Snapshots
Disclosure
- This article text was prepared with AI assistance.
- All commands and outputs shown here were produced on a real Mainnet node.
- What it is: a second-generation Docker image,
registry.waterfall.network/waterfall/protocol/docker:2-mainnet, with stake delegation, key import and export, validator labels, built-in snapshot seeding and configurable ports. - Who it is for: anyone running a Waterfall Mainnet node in Docker, whether starting fresh or already on the first-generation image.
- For existing operators: migration is an image swap. Your validators and your synchronized database carry over — no re-sync, no exit and re-activation.
Stake delegation
A validator can now be created with delegation rules attached, so the stake, the rewards, the right to withdraw and the right to exit no longer have to belong to a single address.
Rules are written as a JSON file and passed to add.sh or import.sh when the validator is
created. They are part of the deposit data, which means the chain enforces them rather than
the node. status.sh shows both what the node has on file and what the chain actually stored:
Can Withdraw: 0x804b943d…,0xf4293794…
Receives rewards: 0x804b943d…=70%, 0xf4293794…=30%
Can Exit: 0x804b943d…,0xf4293794…
Receives stake: 0x804b943d…=100%
Compare with state: true
Compare with state: true is the line that matters — it confirms the rules the node shows are
the rules the chain is applying.
Import and export keys
export.sh packs a validator's coordinator keystore together with its verifier key and
password into one directory; import.sh unpacks that directory onto another node. The export
side selects by label, by index range, or both, so a subset of validators can be moved without
touching the rest; the import side can attach a label and delegation rules as it goes.
Importing the same directory twice is safe: validators already present on the node are reported and skipped rather than duplicated.
Validator labels
label.sh assigns a name to a validator or to a range of them. Labels are then accepted
wherever a validator selector is: status.sh, export.sh and import.sh all take a label in
place of an index, which keeps operations readable on a node running dozens of validators.
Labels live on the node, not on the chain.
Built-in snapshot seeding
Syncing Mainnet from genesis takes a long time. v2 ships snapshot.sh, which fetches a recent
chain snapshot and seeds the database from it:
docker exec -it wf /opt/wf/sh/snapshot.sh status
docker exec -it wf /opt/wf/sh/snapshot.sh download
docker exec -it wf /opt/wf/sh/snapshot.sh apply
docker exec -it wf /opt/wf/sh/snapshot.sh commit
The download resumes where it left off if it is interrupted, and apply refuses to run a
second time over a database that has already moved on, so a mistyped command cannot silently
roll the node back.
Configurable ports
Ports are set through GWAT_COMMANDS and COORDINATOR_COMMANDS:
-e GWAT_COMMANDS="--port 30304 --http.port 9445 --ws.port 9446" \
-e COORDINATOR_COMMANDS="--rpc-port 4001 --p2p-tcp-port 13001 --p2p-udp-port 12001"
In v1 the helper scripts still assumed the default ports and broke once they were changed. In
v2 the scripts read the ports back out of these variables, so status.sh and everything else
keeps working on a node with a custom port layout — which is what makes running several nodes
on one host practical.
One volume, and a container that survives a reboot
v1 mounted three separate volumes (logs, gwat, coordinator) and ran with --rm. v2
mounts the whole data directory as one volume and runs with --restart unless-stopped:
-v $PWD/.wf:/opt/wf/data
This is not only cosmetic. v2 keeps the validator list, labels and delegation rules in
config/, alongside gwat/ and coordinator/ in the data directory. Start v2 with the old
three-volume layout and config/ lands on an anonymous Docker volume instead — one that
--rm throws away on every stop, taking those three with it. The node detects that layout and
warns about it.
The image also exposes two health endpoints on port 8080: / for readiness, which reports
success only once the node is fully synchronized, and /live for liveness, which reports
success while the clients are alive and answering. A node that has been syncing for hours is
alive but not ready, and the two endpoints keep that distinction.
Migrating from v1
The database format has not changed, which is why the upgrade is an image swap rather than a re-sync. The short version:
- Stop and remove the old container — the data stays on disk.
- Pull
:2-mainnet. - Start the new container with the single-volume layout.
- Check
status.sh.
Your validators, their keys and their chain state carry over. Two changes are worth knowing
before you start: the validator list moves from coordinator/validator_keys/deposit_data.json
to config/config.json, and restore.sh now takes the number of validators to restore as a
second argument.
Full procedure, including how to tell which version you are on: Migrate a Docker node from v1 to v2.
Getting started
- New node: Run Waterfall Node Mainnet in Docker (v2)
- Existing node: Migrate a Docker node from v1 to v2