mirror of
https://github.com/simplex-chat/simplexmq.git
synced 2026-07-03 02:32:02 +00:00
226 lines
7.7 KiB
Markdown
226 lines
7.7 KiB
Markdown
# Ethereum stack for SMP names role
|
|
|
|
Reth (execution) + Nimbus (consensus) on Holesky testnet by default.
|
|
|
|
## Quickstart
|
|
|
|
```sh
|
|
cd scripts/docker/reth-nimbus
|
|
docker compose up -d
|
|
docker compose logs -f reth nimbus
|
|
```
|
|
|
|
Sync takes a few hours on Holesky, ~1 day on mainnet. When synced:
|
|
|
|
```sh
|
|
curl -s -X POST http://127.0.0.1:8545 \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
|
|
```
|
|
|
|
Point smp-server: `[NAMES] ethereum_endpoint: http://127.0.0.1:8545`.
|
|
|
|
## How the trust bootstrap works
|
|
|
|
- **Reth** holds Ethereum state and runs the EVM. It does not decide which fork is canonical.
|
|
- **Nimbus** follows the beacon chain and tells Reth which payloads to execute.
|
|
- Nimbus needs **one trusted starting point** to break the chicken-and-egg of peer-claims. `--trusted-node-url` fetches that checkpoint once from a public beacon API; from that point on every block is verified locally against the validator set.
|
|
- The default `TRUSTED_NODE_URL` is publicnode.com (no API key, no rate limits). Replace with any beacon API you trust — only consulted once on first sync.
|
|
|
|
## Switching to mainnet
|
|
|
|
Edit `.env`:
|
|
|
|
```
|
|
NETWORK=mainnet
|
|
TRUSTED_NODE_URL=https://ethereum-beacon-api.publicnode.com
|
|
```
|
|
|
|
Then `docker compose down -v && docker compose up -d` (the `-v` wipes state so Nimbus re-bootstraps against the new network). Reth on mainnet needs ~260 GB pruned NVMe.
|
|
|
|
## Notes
|
|
|
|
- Reth's RPC is bound to `127.0.0.1:8545` only. For remote access (multiple smp-server hosts → one Reth), put Caddy + Let's Encrypt + Basic auth in front — see `plans/20260522_01_smp_public_namespaces.md` §"Operator deployment".
|
|
- Ports 30303/9000 are p2p — open on your firewall for sync.
|
|
- `jwt.hex` is generated on first run by the `jwt-init` service and shared between Reth and Nimbus via the `jwt` volume.
|
|
- To wipe state and re-sync: `docker compose down -v`.
|
|
|
|
## SNRC resolver REST API (`snrc-resolve.py`)
|
|
|
|
The companion script `snrc-resolve.py` exposes the SimpleX Namespace
|
|
Registry (SNRC) over a small JSON HTTP API. It talks to the same local
|
|
Reth + Nimbus stack described above (set `NETWORK=mainnet` in `.env`),
|
|
reading the SNRC contracts directly on Ethereum mainnet.
|
|
|
|
Dependencies are declared inline (PEP 723) at the top of `snrc-resolve.py`
|
|
and in a sibling `pyproject.toml`. The simplest local run uses
|
|
[`uv`](https://docs.astral.sh/uv/):
|
|
|
|
```sh
|
|
uv run scripts/resolver/snrc-resolve.py
|
|
```
|
|
|
|
`uv` resolves and caches `eth-hash[pycryptodome]` on first run. No
|
|
virtualenv juggling, no `--break-system-packages`. If you'd rather
|
|
manage Python deps yourself:
|
|
|
|
```sh
|
|
pip install 'eth-hash[pycryptodome]>=0.7'
|
|
python scripts/resolver/snrc-resolve.py
|
|
```
|
|
|
|
### Deployed registries
|
|
|
|
| TLD | Network | ENSRegistry address |
|
|
|------------|------------------|----------------------------------------------|
|
|
| `.testing` | Ethereum mainnet | `0x03f438da0bd44da3c6c1d0392f8ba183b8b3a7a6` |
|
|
| `.simplex` | — (not deployed) | — |
|
|
|
|
Each TLD is an independent ENS-shaped deployment with its own
|
|
`ENSRegistry`. The resolver dispatches by the queried name's rightmost
|
|
label, so a single instance can serve both TLDs concurrently once
|
|
`.simplex` launches.
|
|
|
|
### Running
|
|
|
|
With Reth bound to `127.0.0.1:8545` (the default Quickstart layout
|
|
above), no env vars are required — the script defaults to that RPC and
|
|
to the mainnet `.testing` registry:
|
|
|
|
```sh
|
|
./scripts/resolver/snrc-resolve.py
|
|
```
|
|
|
|
Output on startup:
|
|
|
|
```
|
|
snrc-resolve listening on 0.0.0.0:8000
|
|
RPC = http://127.0.0.1:8545
|
|
Registries:
|
|
.testing = 0x03f438da0bd44da3c6c1d0392f8ba183b8b3a7a6
|
|
.simplex = (not configured)
|
|
GET /resolve/<name> GET /health
|
|
```
|
|
|
|
Override the listen port or bind address with `SNRC_PORT` / `SNRC_BIND`.
|
|
|
|
### Running in Docker
|
|
|
|
The compose file ships a `resolver` service alongside reth and nimbus.
|
|
`docker compose up -d` builds the image from `Dockerfile` (multi-stage,
|
|
non-root, `uv`-based) and exposes the API on `127.0.0.1:8000`:
|
|
|
|
```sh
|
|
docker compose up -d resolver
|
|
docker compose logs -f resolver
|
|
curl -s http://127.0.0.1:8000/health
|
|
```
|
|
|
|
The container points `SNRC_RPC` at `http://reth:8545` (the compose-internal
|
|
DNS name) so the resolver and reth share the bridge network without
|
|
exposing reth's RPC to the host beyond loopback.
|
|
|
|
To change the host-side port, edit the LEFT side of the port mapping in
|
|
`docker-compose.yml`:
|
|
|
|
```yaml
|
|
resolver:
|
|
ports:
|
|
- "127.0.0.1:8000:8000" # host:container
|
|
```
|
|
|
|
The registry address defaults to mainnet `.testing` — to override (Holesky,
|
|
a private deployment, or future `.simplex`), uncomment and set the values
|
|
in `docker-compose.yml` under the resolver service's `environment:` block.
|
|
|
|
The image declares a `HEALTHCHECK` against `/health`; `docker compose ps`
|
|
will mark the service `(healthy)` once reth is queryable.
|
|
|
|
### Resolving a name
|
|
|
|
`foobar.testing` is registered on mainnet with every text and
|
|
multicoin record populated (useful as a smoke-test target):
|
|
|
|
```sh
|
|
curl -s http://127.0.0.1:8000/resolve/foobar.testing | jq .
|
|
```
|
|
|
|
```json
|
|
{
|
|
"name": "foobar.testing",
|
|
"nickname": "Foo",
|
|
"website": "https://foo.bar",
|
|
"location": "",
|
|
"simplexContact": "https://smp16.simplex.im/a#Q_F00BA7",
|
|
"simplexChannel": "",
|
|
"eth": null,
|
|
"btc": "bc1qpzht4wp64yg7z6sgl07vvrnepyux740juynfcn",
|
|
"xmr": "4ANzdVJFxLtCKcBgNGkFSEA41zJFgrTX93LWt9UR6xpg7YNCsdrSV817cw2xKT8NXeS5euBBqTApS2u8kRTxMhyiDGN3Qgt",
|
|
"dot": "139GgyEsXDyGLhmhBTPmDmGCyTvTVuLad3YjHax2PWLK6p3s",
|
|
"owner": "0xd83bb610fbad567fb5d8755ec162881e46d1fbc9",
|
|
"resolver": "0x80fa1903e70af03e79c73fb7feae2fb33aebae01"
|
|
}
|
|
```
|
|
|
|
All field names are lowercase-initial and contain no dots, so they map
|
|
directly onto Haskell record fields and can be consumed via aeson's
|
|
`Generic`-derived `FromJSON` without a key-rewriting layer. Equivalent
|
|
Haskell record:
|
|
|
|
```haskell
|
|
data SnrcRecord = SnrcRecord
|
|
{ name :: Text
|
|
, nickname :: Text
|
|
, website :: Text
|
|
, location :: Text
|
|
, simplexContact :: Text
|
|
, simplexChannel :: Text
|
|
, eth :: Maybe Text
|
|
, btc :: Maybe Text
|
|
, xmr :: Maybe Text
|
|
, dot :: Maybe Text
|
|
, owner :: Text
|
|
, resolver :: Text
|
|
} deriving (Generic, FromJSON)
|
|
```
|
|
|
|
(The on-chain text-record keys still use the ENSIP-5 dot convention —
|
|
`simplex.contact` and `simplex.channel`. Only the resolver's JSON
|
|
surface camelCases them.)
|
|
|
|
Address encoding matches each chain's canonical user-facing form:
|
|
EIP-55 mixed-case for `eth`, bech32/bech32m for `btc` segwit/taproot
|
|
(base58check for legacy P2PKH/P2SH), SS58 with Polkadot prefix 0 for
|
|
`dot`, Monero-base58 for `xmr`. Unrecognised payloads fall back to
|
|
`0x`-prefixed hex.
|
|
|
|
#### Subnames
|
|
|
|
Subnames work exactly the same. try `bar.foobar.testing`.
|
|
|
|
```sh
|
|
curl -s http://127.0.0.1:8000/health
|
|
# → {"ok": true, "rpc": "http://127.0.0.1:8545", "registries": {"testing": "0x…", "simplex": ""}}
|
|
```
|
|
|
|
### Pointing at multiple deployments
|
|
|
|
Once `.simplex` deploys, point a single resolver instance at both
|
|
registries — requests are dispatched by the rightmost label:
|
|
|
|
```sh
|
|
SNRC_REGISTRY_SIMPLEX=0x...mainnet-simplex-ENSRegistry... \
|
|
./scripts/resolver/snrc-resolve.py
|
|
```
|
|
|
|
Queries for a TLD with no registry configured return HTTP 400 with the
|
|
list of supported TLDs.
|
|
|
|
### Error responses
|
|
|
|
| Status | When |
|
|
|--------|-----------------------------------------------------------------------|
|
|
| 400 | TLD not configured (`/resolve/foo.simplex` while `.simplex` is empty) or path not a fully-qualified name |
|
|
| 404 | Name has no resolver set on the registry (`ENSRegistry.resolver(node)` is zero) |
|
|
| 502 | Upstream RPC error / unreachable (Reth not running or not synced) |
|