Skip to content

Running a ledger node

Adam Waldenberg edited this page Oct 18, 2026 · 4 revisions

Running a ledger node

Upcoming. This page describes the ledger-node branch. It is not part of a release, and details may change. No genesis comes with the node yet, so a ledger runs only on a genesis that is given to it.

Part of Ledger. A node runs the ledger when it is started with --ledger. It then follows the chain, checks every block and serves the blocks it holds to its peers. Given the key of a validator that the genesis lists, it also votes and signs blocks. Without --ledger a node behaves as one of the earlier release, and nothing on this page applies.

What a ledger node needs

Observer Validator
A build of the ledger-node branch, see Build, testing and native image yes yes
--ledger yes yes
The genesis of the chain, given with --ledger-genesis yes yes
The bootstrap snapshot that the genesis names, bootstrap.dat in the data directory yes yes
A key file, given with --ledger-key, whose public key the genesis lists yes
The ledger sporks held by the node, for mints, claims and transfers to be taken yes

The snapshot is the one of Legacy chain snapshot. A daemon downloads it in the background when it has none, and the ledger waits for it, since every claim is checked against it.

Starting

A validator first makes its key and gives the public key to whoever writes the genesis:

$ hedgehog util ledger-key-generate ~/.ledger.key
3f2a…c9

The command writes a random seed into a new file that only its owner can read, prints the public key, and refuses a path that exists, so a key is never replaced. The seed is never printed. Keep the file: a validator that loses it can no longer sign as that key.

Then start the daemon:

$ hedgehog daemon --ledger --ledger-genesis genesis.json --ledger-key ~/.ledger.key

Leave out --ledger-key to run an observer. The node logs, in order:

  1. The ledger is of the chain <chain id>. The chain id is the hash of the genesis: compare it with other operators to know that you are on the same chain.
  2. Waiting for the bootstrap snapshot <file> before the ledger starts, once a minute while the snapshot is missing. The ledger looks for it every 5 seconds.
  3. The ledger keeps its block log and its sign state in <folder>, once the snapshot is there and is the one of the genesis. The folder is the one of this chain, see Where the files are.
  4. Nothing more while all is well. The node replays its block log, starts as an observer, catches up with its peers, and after a grace period of 30 seconds a validator starts to vote.

The seven steps from the start of the daemon to the first vote, and where each can stop

Options

These go after daemon. They are checked before anything else starts, and a value that is wrong stops the daemon with a message and exit code 2.

Option Meaning
--ledger Run the ledger: follow and serve its blocks. Off by default
--ledger-genesis <file> The genesis of the chain. Needed until a genesis comes with the node
--ledger-key <file> Validate with the seed in this file, as made by util ledger-key-generate. Without it the node follows the ledger and never signs
--ledger-trusted <height>:<hash> The block a synced ledger must have, with the hash as 128 hexadecimal characters and a height of 1 or more

A key that the genesis does not list is not an error. The node warns The key <public key> is not a validator of this genesis, this node will not sign and runs as an observer.

A trusted checkpoint protects a new node against a fork. Take the height and hash of a block from a node you trust. A node whose ledger holds another block at that height, or that sees two certified blocks at one height, halts its ledger instead of following a fork.

The genesis

The genesis is a JSON file with exactly these fields:

{
  "time": 1796083200000,
  "roundLength": 100,
  "validators": [
    "3f2a…c9",
    "8be0…14"
  ],
  "snapshotHash": "<128 hex characters>",
  "snapshotTotal": 12345678900000000
}
Field Meaning
time When the chain starts, in Unix milliseconds
roundLength The blocks of a round, after which votes on the validator set take effect
validators The public keys of the first validators, 64 hex characters each, 1 to 1000 of them, in the order that decides who proposes. They can never be voted out
snapshotHash The SHA-512 of the bootstrap snapshot, without its signature
snapshotTotal The sum of the snapshot's balances, in units of 1e-8 coins

A field that is missing, repeated or unknown is refused, and so is a key that is listed twice, so a typo cannot pass. The file is public: it holds no secret. Every node of a chain must use the same values, since the chain id is computed from them and every signature on the chain covers the chain id. Spacing and the order of the fields do not matter, the order of the validators does.

Where the files are

The ledger keeps its files in the folder ledger of the data directory, see Running Hedgehog, in a folder of its own for each chain. The name of that folder is the first 16 characters of the chain id, and the node logs it at start as The ledger keeps its block log and its sign state in <folder>:

File Holds
ledger/<chain>/blocks.log Every block of the chain
ledger/<chain>/sign.state What this validator signed for the height it works on, with sign.state.lock beside it
  • Never delete or roll back sign.state of a validator. It is what keeps the validator from signing two different blocks at one height after a restart, and a deleted or older copy is not noticed.
  • The block log can be fetched again from peers, but not on its own: a log that is shorter than the sign state needs stops the node, which asks for the tail of the log to be restored from a peer.
  • The state is not stored. At every start the node replays the whole log and checks every block again, so a long chain takes a while to start. A validator replays it twice.
  • Another genesis, another folder. A genesis with other values has another chain id, so the node starts on an empty folder and never meets the files of the chain it had. The folders of chains that are left stay as they were.

When the ledger does not run

A wrong option, genesis or key file stops the daemon at start, before anything runs, with one of these messages and exit code 2:

Message Cause What to do
--ledger needs a genesis: give --ledger-genesis <file> (no genesis is bundled with this release) No genesis given, and none comes with the node Give --ledger-genesis
The ledger genesis <file>: … The genesis is not valid; the rest names the field Fix the file, or get it again
… must be readable by its owner only, try chmod 600 The key file can be read by others chmod 600 the key file
--ledger-trusted must be <height>:<128 hexadecimal characters of the hash>, … The checkpoint is malformed Give a height of 1 or more and the full hash

What goes wrong later leaves the node running without its ledger, and the node logs why once:

Log line Cause What to do
The ledger is not running, the node goes on without it: The snapshot <file> has the content hash … The snapshot is not the one of the genesis Fetch the snapshot release that the genesis names
The ledger driver halted: The ledger node could not be built: … Cannot replay the block log … The log does not fit this genesis or snapshot, or is damaged in the middle Check the genesis; move the folder of the chain away to start over
The ledger driver halted: … The checkpoint differs, two certified blocks were seen at one height, or the node failed twice within 10 seconds Read the reason; a fork needs an operator

After a halt the service also says once: The ledger halted and serves the blocks it has, the node goes on: <reason>.

A halted ledger still serves the blocks it holds, so its peers can go on syncing from it. Restart the daemon to try again.

Using a running node

A node that runs the ledger answers over the REST interface, and the commands of Ledger command line use it. This one shows that the node is up and on your chain:

$ hedgehog cli ledger info

It shows "running" : true, the chain id to compare with other operators, the height of the tip, and a signing of true once a validator that holds a key the genesis lists has caught up and votes. Its unsetSporks and laggingSporks name the ledger sporks the node still waits for. While one is listed, what depends on it is not minted, claimed or taken, and a chain on a running node may stand still. A transfer, a claim and a vote are sent with the same command, signed on the machine that runs it.

What a node cannot do yet

  • Start without a genesis file. No genesis comes with the node.
  • Keep waiting transactions across a restart. They are lost when the process stops.

In the source

  • application/src/main/java/org/unigrid/hedgehog/command/option/LedgerOptions.java: the options
  • application/src/main/java/org/unigrid/hedgehog/service/ledger/LedgerSetup.java: the checks at start
  • application/src/main/java/org/unigrid/hedgehog/service/ledger/GenesisFile.java: the genesis
  • application/src/main/java/org/unigrid/hedgehog/service/ledger/LedgerService.java: what the node does with the ledger
  • application/src/main/java/org/unigrid/hedgehog/server/rest/LedgerResource.java: how the ledger is read and used over REST

Clone this wiki locally