The published line delivered the anchor (source and patch 0003) but had lost the chain configuration: no genesis, no run recipe. The unpublished local line had genesis.json, RUN-A-NODE.md and the measured claims, but predated the anchor. This commit carries the package files onto the anchor line: - genesis.json restored, byte for byte as the running nodes hold it - RUN-A-NODE.md restored; the basefee-floor patch returns as 0004; the anchor section states plainly that a build without patch 0003 has not been shown to import headers past block 13,014,000, and that an import proof for a patches-only build is the next thing this repository owes its reader - bootnodes are deliberately NOT published: today's entry points are consensus or origin hosts, and this project does not print its machines' addresses; a dedicated bootnode host is the planned replacement, an enode is available by mail until then
236 lines
12 KiB
Markdown
236 lines
12 KiB
Markdown
# Run a node on Aere Network mainnet, chain 2800
|
|
|
|
This file plus `genesis.json` plus one bootnode enode (see the bootnodes section below) is
|
|
everything an outsider needs to bring up a full node that syncs chain 2800 from block 0. Until
|
|
2026-08-03 this repository shipped the precompile sources and the NIST vectors but not the chain
|
|
configuration, so nobody outside the Foundation could actually start a node, and the "verify it
|
|
yourself" claim the repository is built on could not be exercised end to end.
|
|
|
|
Nothing here is a key. `genesis.json` is public chain configuration; a node that nobody can dial
|
|
is a node nobody can sync from, and the bootnodes section says plainly how dialing works today.
|
|
|
|
---
|
|
|
|
## The three things you need
|
|
|
|
| What | Where |
|
|
|---|---|
|
|
| `genesis.json` | The genesis file, byte for byte as it sits on the Foundation nodes. sha256 `6f56eacada94457c1aa325c878399989e344dcf1d763e2d3b479f9b068784ed1` |
|
|
| a bootnode enode | requested by mail for now; see the bootnodes section below |
|
|
| `patches/`, `precompiles/` | The Besu fork, see `README.md`. Needed to sync past block 9,189,161 |
|
|
|
|
---
|
|
|
|
## Which Besu
|
|
|
|
Stock upstream Besu syncs chain 2800 correctly up to block **9,189,161** and then stops being able
|
|
to follow it. That block is the first with a timestamp at or after `activationTime` 1783820272, when
|
|
the five Aere post-quantum precompiles come into existence. A node without them computes a different
|
|
result for any block containing a call to `0x0AE1` through `0x0AE5` and falls off the chain.
|
|
|
|
So there are two honest paths:
|
|
|
|
- **Follow the tip.** Build the fork: apply `patches/0001-aere-pqc-precompiles-mainnet.patch` to
|
|
upstream Besu commit `d2032017bb3b8cb215a97303980a1e4a643f7180` and build it. Full instructions,
|
|
including the pre-image hashes that prove you are on the right base, are in `README.md`.
|
|
- **Verify the early chain only.** Stock Besu 26.4 or later will import blocks 0 through 9,189,160
|
|
from this genesis. That is enough to check the genesis allocation and the sub-second block period
|
|
transition against what we publish. It is not enough to reach the head.
|
|
|
|
There is a second Aere change to the client, and it is not in patch `0001`:
|
|
`patches/0004-aere-basefee-floor.patch`, one file, `LondonFeeMarket.java`. From block **10,141,734**
|
|
chain 2800 clamps the EIP-1559 base fee to a floor of 1 Gwei. The patch is fork gated by two JVM
|
|
system properties and is a byte behaviour no-op when they are unset, so a node that applies it
|
|
without setting them is stock Besu:
|
|
|
|
```
|
|
-Daere.basefee.floor.forkBlock=10141734 -Daere.basefee.floor.value=1000000000
|
|
```
|
|
|
|
Pass those through `BESU_OPTS`. Apply this patch and set these flags if you want a client that
|
|
computes what the network computes. It was measured on 2026-08-02 that QBFT and IBFT2 in Besu do not
|
|
validate the base fee at import at all, upstream or in our fork, so a node without this change is
|
|
expected to import the chain anyway and only to disagree if it ever proposed a block. That is a
|
|
finding about a missing upstream rule and not a licence to skip the patch.
|
|
|
|
Do not apply `patches/0002-...-testnet.patch` for a mainnet node. It adds `0x0AE6` and `0x0AE7`,
|
|
which are not on mainnet, and a node that has them will not disagree with the chain today but is
|
|
running a configuration the chain does not.
|
|
|
|
## The post-quantum header anchor: apply patch 0003 to follow the current chain
|
|
|
|
Since block 13,014,000 every 32nd header of chain 2800 carries a sixth element in its QBFT
|
|
`extraData`: a certificate of validator Falcon-512 seals, universal since block 13,889,296 and
|
|
enforced at a minimum of three seals per anchor block since 2026-08-14. From block 14,050,000 the
|
|
network refuses to finalize blocks without that post-quantum quorum.
|
|
|
|
The anchor ships in this repository both as source, under `anchor/`, and as
|
|
`patches/0003-aere-pq-anchor.patch`. A node built without it has not been shown to import
|
|
headers past block 13,014,000: the second client needed an explicit change to accept the sixth
|
|
element, and no floor-only build has been observed following the chain past that height. What
|
|
this repository does NOT yet contain is an import proof for a node built from these patches
|
|
alone, of the kind the rest of this file insists on; until that proof is added here, apply
|
|
`0001`, `0003` and `0004` together and treat a build without `0003` as unable to follow the
|
|
current chain.
|
|
|
|
---
|
|
|
|
## Start it
|
|
|
|
```bash
|
|
besu \
|
|
--genesis-file=/path/to/aere-node/genesis.json \
|
|
--data-path=/path/to/data \
|
|
--network-id=2800 \
|
|
--sync-mode=FULL \
|
|
--data-storage-format=BONSAI \
|
|
--bootnodes=<the enode you were given> \
|
|
--p2p-port=30303 \
|
|
--rpc-http-enabled --rpc-http-port=8545 \
|
|
--rpc-http-api=ETH,NET,WEB3,QBFT \
|
|
--min-gas-price=0
|
|
```
|
|
|
|
Bootnodes are deliberately NOT published in this repository yet. The network entry points
|
|
available today are consensus or origin hosts, and this project does not print the addresses
|
|
of its own machines in public documents; a dedicated public bootnode, on a host that serves
|
|
nothing else, is the planned replacement, and this paragraph will then carry its enode. Until
|
|
then, write to office@aere.network with the subject "bootnode" and you will receive a current
|
|
enode by return. Everything else on this page works without one; discovery is the only step
|
|
that needs it.
|
|
|
|
`--sync-mode=FULL` is deliberate. Chain 2800 runs QBFT with a half second block period, so the head
|
|
is around twelve million blocks and climbing by two per second. Full sync from block 0 is the mode
|
|
that actually verifies the history we publish, which is the point of this repository. Snap sync
|
|
against a QBFT chain trusts a peer for the state and verifies far less.
|
|
|
|
`--min-gas-price=0` matches the network. Chain 2800 has a real 1 Gwei base fee from block
|
|
10,141,734, but the minimum gas price a node will accept into its own pool is a local policy and the
|
|
Foundation nodes run it at zero.
|
|
|
|
Outbound and inbound TCP and UDP on your p2p port must be open. Discovery is UDP; a TCP only
|
|
firewall gives you a node that dials out and is never dialled back.
|
|
|
|
---
|
|
|
|
## Check that you are on the right chain, before you trust anything else
|
|
|
|
Two facts settle it. Run these against your own node once it is up.
|
|
|
|
**1. Your block 0 must be the network's block 0.**
|
|
|
|
```bash
|
|
curl -s -X POST http://127.0.0.1:8545 -H 'content-type: application/json' \
|
|
-d '{"jsonrpc":"2.0","id":1,"method":"eth_getBlockByNumber","params":["0x0",false]}' \
|
|
| grep -o '"hash":"0x[0-9a-f]*"'
|
|
```
|
|
|
|
Expected, and this is the whole test:
|
|
|
|
```
|
|
0xd86d57a899cbfa580669f0e1c7cd4ae5f525247c3c823f8f75df176a0c5d7f1a
|
|
```
|
|
|
|
That hash covers every consensus relevant field in `genesis.json`, including the state root of the
|
|
six genesis accounts. If it matches, your genesis file is the network's genesis file and no further
|
|
comparison of individual fields is needed. If it does not match, your node will never peer, and no
|
|
amount of correct bootnodes will help.
|
|
|
|
Compare against the public endpoints, which are separate machines with separate operators of the
|
|
same file:
|
|
|
|
```bash
|
|
curl -s -X POST https://rpc.aere.network -H 'content-type: application/json' \
|
|
-d '{"jsonrpc":"2.0","id":1,"method":"eth_getBlockByNumber","params":["0x0",false]}'
|
|
curl -s -X POST https://rpc2.aere.network -H 'content-type: application/json' \
|
|
-d '{"jsonrpc":"2.0","id":1,"method":"eth_getBlockByNumber","params":["0x0",false]}'
|
|
```
|
|
|
|
**2. You must have peers.**
|
|
|
|
```bash
|
|
curl -s -X POST http://127.0.0.1:8545 -H 'content-type: application/json' \
|
|
-d '{"jsonrpc":"2.0","id":1,"method":"net_peerCount","params":[]}'
|
|
```
|
|
|
|
A nonzero answer means discovery worked. Zero after a few minutes means either your UDP port is
|
|
closed or the bootnode you were given is down; a plain TCP connect to its address distinguishes
|
|
the two.
|
|
|
|
---
|
|
|
|
## What the two bootnodes are, and what they are not
|
|
|
|
They are two full nodes that are **not** in the validator set. Chain 2800 seals blocks with seven
|
|
QBFT validators, and none of the seven is listed here, on purpose. Publishing a validator's p2p
|
|
address hands an attacker a target whose downtime costs the network consensus headroom, which is why
|
|
sentry style architectures exist. The two nodes above carry no consensus responsibility: if both are
|
|
taken down, new nodes cannot bootstrap until they are replaced, and block production is unaffected.
|
|
|
|
A written address is not a live address. This file cannot prove the bootnodes are up at the moment
|
|
you read it; only your own `net_peerCount` can.
|
|
|
|
Both were reachable and serving blocks when this file was written. Both are Foundation operated, so
|
|
this is one operator's word for two addresses. That is a real centralisation fact about chain 2800
|
|
today and not a documentation gap.
|
|
|
|
---
|
|
|
|
## Measured, 2026-08-03
|
|
|
|
The recipe on this page was executed rather than written. A Besu node with an **empty data
|
|
directory** was started from exactly this `genesis.json` and exactly these two bootnode URLs, on a
|
|
machine that is not a validator, with `--sync-mode=FULL` and no static nodes, no other peer hints
|
|
and no copied database.
|
|
|
|
- Its own block 0 came out as
|
|
`0xd86d57a899cbfa580669f0e1c7cd4ae5f525247c3c823f8f75df176a0c5d7f1a`, which is the live chain's
|
|
block 0. That hash covers the state root, so the six accounts in `alloc` are the network's down to
|
|
the last wei, and so is every header field in the file.
|
|
- It reached **11 peers** knowing nothing but the two enode URLs above.
|
|
- It imported from block 0 to **block 2,221,580** in about thirty minutes, with **zero** rejected
|
|
blocks, which carries it through the sub-second block period change at roughly 2,137,652 and out
|
|
the other side.
|
|
|
|
The honest limits on that run, stated because they are the difference between this section and an
|
|
advertisement:
|
|
|
|
- It was stopped at ~2.2 million rather than carried to the head, so it never reached the precompile
|
|
fork at 9,189,161 or the base fee floor at 10,141,734. **"Reaches the current head" is not
|
|
measured.**
|
|
- It ran the Foundation's build of the fork, not a build a third party made from `patches/`. That
|
|
those patches build, and that the built precompiles answer the NIST vectors, is measured
|
|
separately in `README.md`; that the two together sync to the head is not.
|
|
- Nothing here proves the bootnodes will be up when you read this.
|
|
|
|
---
|
|
|
|
## A number in this file that the chain does not agree with
|
|
|
|
`config.transitions.qbft[0].block` reads **2138451**. That is the height at which the file says the
|
|
block period drops from one second to half a second. The chain says otherwise. Sampling header
|
|
timestamps on the public endpoints puts the change just after **2,137,652**. Measured spans of
|
|
twenty blocks: from 2,137,600 the twenty blocks take 20 s, from 2,137,656 they take 15 s, from
|
|
2,137,657 they take 12 s, and from 2,137,700 they take a flat 10 s, which is 500 ms a block and
|
|
stays there through 2,138,400. The published 2138451 is roughly eight hundred blocks late and
|
|
describes a boundary that is not in the data.
|
|
|
|
It is published anyway, unaltered, because this file is the file the network runs and its value as
|
|
evidence comes from being exactly that. Two things make the discrepancy harmless to you rather than
|
|
merely tolerated: the field only governs how fast a proposer may seal, so it has no effect on a node
|
|
that follows; and it was measured, on the sync run described above, that a node carrying this exact
|
|
value imports the blocks either side of both numbers without rejecting one.
|
|
|
|
Correcting it means editing a file on live validators, which is a change to running consensus
|
|
infrastructure and is not made as a side effect of publishing a document.
|
|
|
|
## A note on the `comment` fields in `alloc`
|
|
|
|
The six genesis accounts carry `comment` strings. No client reads them; they are not consensus data
|
|
and they are inside the file only because they have always been. Two of them are wrong and are left
|
|
uncorrected here rather than quietly edited, because this file is published byte for byte as the
|
|
running nodes hold it: the dash characters are mangled, and the 1,400,000,000 AERE account is
|
|
labelled "Mining Reserve" although chain 2800 has no mining and never has. It is the staking and
|
|
emissions reserve. Correcting the text means editing a file on live nodes, which is a change to
|
|
running infrastructure and is not made as a side effect of a documentation fix.
|