Skip to main content
Quantum Chain uses Clique Proof-of-Authority consensus. A fixed list of approved sealers — set at genesis and updatable through on-chain votes — produces blocks in round-robin.

Networks

NetworkChain IDBlock timeNotes
Mainnet2080315 sProduction network
Testnetconfigurable5–15 sFor staging and integration
Devnetany1–5 sLocal development; single sealer
Quantum Chain mainnet genesis is checked into configs/mainnet/genesis.json.

Genesis file structure

{
  "config": {
    "chainId": 20803,
    "homesteadBlock": 0,
    "eip150Block": 0,
    "eip155Block": 0,
    "eip158Block": 0,
    "byzantiumBlock": 0,
    "constantinopleBlock": 0,
    "petersburgBlock": 0,
    "istanbulBlock": 0,
    "berlinBlock": 0,
    "londonBlock": 0,
    "quantumVerificationBlock": 0,
    "clique": { "period": 15, "epoch": 30000 }
  },
  "publicKey": "<post-quantum public key bound to genesis>",
  "difficulty": "1",
  "gasLimit": "30000000",
  "extraData": "0x...<32 vanity bytes><20 bytes per sealer>...<65 bytes seal>",
  "alloc": { "0x...": { "balance": "..." } }
}
The extraData field encodes the initial sealer set:
[32 bytes vanity] [20 bytes sealer #1] [20 bytes sealer #2] ... [65 bytes signature placeholder]
The placeholder is zeroed at genesis. To build it, use the extradatagenerator tool:
./build/bin/extradatagenerator \
  --validators 0xAddr1,0xAddr2,0xAddr3 \
  > extra.hex

Initialize a new network

./build/bin/geth --datadir ./node1 init genesis.json
./build/bin/geth --datadir ./node2 init genesis.json
init writes the genesis block. Run init once per data directory; running it again on a populated directory is a no-op.

Run a validator (Clique sealer)

A validator must be in the sealer set encoded in extraData, and must run with --mine:
geth \
  --datadir /var/lib/geth \
  --networkid 20803 \
  --syncmode full \
  --mine \
  --miner.etherbase 0xMySealerAddress \
  --unlock 0xMySealerAddress \
  --password /etc/geth/password \
  --port 30303 \
  --maxpeers 25 \
  --metrics --metrics.addr 127.0.0.1
Sealers take turns proposing blocks. With period 15s and N sealers, each sealer proposes one block every 15 × N seconds on average.

Add or remove a sealer

Sealers vote on changes through the clique_propose API. Voting completes when a majority of current sealers agree.
// Add a sealer
clique.propose("0xNewSealerAddress", true)

// Remove a sealer
clique.propose("0xOldSealerAddress", false)

// Check pending proposals
clique.proposals
Votes are tallied at each epoch (every 30 000 blocks on mainnet). At an epoch transition, the active sealer set is finalized in the next checkpoint header’s extraData.

Inspect the active sealer set

// Current sealers
clique.getSigners()

// At a specific block
clique.getSignersAtHash("0x...")
Or via JSON-RPC:
curl -s -X POST $RPC -H 'Content-Type: application/json' --data '{
  "jsonrpc":"2.0","method":"clique_getSigners","id":1,"params":["latest"]
}'

Static peers

For private networks where discovery is undesirable, hard-code peers in static-nodes.json under your --datadir:
[
  "enode://abc...@10.0.0.1:30303",
  "enode://def...@10.0.0.2:30303"
]
Or pass --bootnodes.

Bootnodes

For public networks, run a bootnode:
./build/bin/bootnode -genkey boot.key
./build/bin/bootnode -nodekey boot.key -addr :30301
Distribute the resulting enode://... URL.

Operating a private testnet

A typical 3-validator testnet:
  1. Generate three keystores with keygenerator.
  2. Build genesis with extradatagenerator --validators <3 addresses>.
  3. geth init on each node.
  4. Connect them via static-nodes.json.
  5. Start each with --mine --unlock.
Block production starts when the first validator runs --mine and discovers at least one peer.

Reorg safety

Clique reorgs are bounded by the sealer-out-of-turn rule and are typically 1–2 blocks. For institutional flows, Qustody waits for CUSTODY_CONFIRMATION_DEPTH (default 12) blocks before marking a transaction CONFIRMED. Increase for high-value transfers.

Where to go next