wrap: chain verify + manifest verify + LICENSE + final docs

- internal/chain: end-to-end chain verification. Walks head → genesis,
  verifies every cert (Ed25519 or STM as appropriate), and checks
  continuity at every boundary:
    epoch:    same or +1 from previous
    hash:     current.previous_hash == previous.hash
    AVK:      same epoch  → equal aggregate_verification_key
              new epoch   → matches previous.protocol_message.next_aggregate_verification_key
- cmd: 'verify chain' subcommand + 'verify manifest <dir>' for SHA-checking
  downloaded immutable files
- internal/manifest: per-file SHA-256 verification against the digests.json
  shipped in the snapshot's digests archive
- MCP: 8th tool 'mithril_verify_chain' for agent-driven full-chain verify
- README: complete rewrite — status table, architecture, gotchas, MCP
  tool surface, exit code contract, build instructions
- LICENSE: Apache-2.0 (matches upstream Mithril)

Verified end to end against live networks:
  preprod  chain  90 certs (89 STM + 1 genesis)  1124 wins   ✓
  mainnet  chain  89 certs (88 STM + 1 genesis)  210921 wins ✓

That's the wrap. Pure-Go consensus-correct Mithril client, single 10 MB
static binary, MCP-native, no CGo, no upstream Rust runtime.
This commit is contained in:
Kayos 2026-04-23 16:15:47 -07:00
parent 920d7cf177
commit 599085eaa9
8 changed files with 905 additions and 90 deletions

201
README.md
View file

@ -7,106 +7,107 @@ node bootstrap the chain from a cryptographically-verified snapshot
instead of replaying every block from genesis.
The official [`mithril-client`](https://github.com/input-output-hk/mithril)
is Rust. This project is a pure-Go reimplementation that produces a single
static binary with no runtime dependencies — useful for:
is Rust. `mithril-go` is a pure-Go reimplementation that produces a single
static binary with no CGo, no upstream Rust runtime, and an MCP-native
tool surface for AI agents — useful for:
- Embedding a Mithril bootstrap into Go-based Cardano tooling
(alongside `gouroboros`, `dingo`, and friends)
- Running on constrained ARM/embedded targets where shipping the Rust
binary + its deps is overkill
- Embedding Mithril bootstrap into Go-based Cardano tooling (alongside
`gouroboros`, `dingo`, etc.)
- Constrained ARM/embedded targets where shipping the Rust binary + its
deps is overkill
- Operators who prefer a single `go install`-able helper
- AI agents (Claude Code, Cursor, Zed, ...) that want to verify Mithril
certs as a callable tool
## Status
**Full Mithril verification working — genesis Ed25519 AND STM BLS12-381 — against live mainnet and preprod.**
**Working consensus-correct verification against live mainnet and preprod.**
| Piece | Status |
| Capability | Status |
|---|---|
| Aggregator REST client | ✅ list, get, cert, chain |
| `list` / `show` / `info` / `cert` commands | ✅ mainnet + preprod |
| Resumable HTTP download (single stream, SHA hook) | ✅ |
| Aggregator REST client | ✅ list, show, cert, walk-chain |
| Resumable HTTP download (SHA hook + progress) | ✅ |
| Streamed zstd+tar extract (tar-slip defended) | ✅ |
| `download` — digests + ancillary | ✅ (full immutables loop pending) |
| **Genesis Ed25519 verification** | ✅ live mainnet + preprod |
| **STM BLS12-381 aggregate verification** | ✅ live mainnet + preprod |
| **MCP stdio server** | ✅ 7 tools, Claude/Cursor/Zed compatible |
| Full cert-chain verify (genesis → head) | ⏳ next |
| Genesis Ed25519 verification | ✅ live mainnet + preprod |
| STM BLS12-381 aggregate verification | ✅ live mainnet + preprod |
| Lottery-win threshold (Taylor series, big.Rat) | ✅ |
| Merkle batch-proof verification (Blake2b-256) | ✅ |
| AVK chaining + epoch + hash continuity | ✅ |
| **Full chain verification (genesis → head)** | ✅ live mainnet + preprod |
| Per-immutable SHA manifest verification | ✅ |
| MCP stdio server (8 tools) | ✅ |
| Full immutables-download loop (8000+ files) | ⏳ |
## Usage
## Quick start
```
mithril-go info -network mainnet
mithril-go list -network mainnet
mithril-go show -network mainnet latest
mithril-go cert -network mainnet head
mithril-go cert -network mainnet -chain head # walk to genesis
mithril-go download -network preprod -out ./db latest # digests + ancillary
```bash
go build ./cmd/mithril-go # produces a 9.5 MB single static binary
mithril-go info -network mainnet
mithril-go list -network mainnet
mithril-go show -network mainnet latest
mithril-go cert -network mainnet head
mithril-go cert -network mainnet -chain head # walk to genesis
mithril-go download -network preprod -out ./snap latest
mithril-go verify -network preprod manifest ./snap # SHA-check downloaded files
mithril-go verify -network mainnet genesis # Ed25519
mithril-go verify -network mainnet head # STM BLS aggregate
mithril-go verify -network mainnet chain # full walk + every cert
```
## Verification sprint plan
Every query command supports `-json` for structured output.
The verification story splits into two layers:
## Architecture
### 1. Genesis Ed25519 verification
```
cmd/mithril-go/
main.go CLI entrypoint, subcommand dispatch
mcp.go MCP tool registration
json.go Structured-output helpers
The genesis certificate (terminates the chain; its `previous_hash` is
empty and `genesis_signature` is non-empty) is signed by a static
Ed25519 key baked into this client per network (`internal/networks`).
internal/
aggregator/ REST client (list, get, cert, walk-chain)
artifact/ Resumable HTTP download + streamed zstd-tar extract
chain/ End-to-end chain verify (genesis → head, AVK chaining)
manifest/ Per-immutable SHA-256 verification against digests.json
mcp/ Minimal JSON-RPC 2.0 stdio MCP server (no deps)
networks/ Per-network aggregator URLs + genesis verify keys
stm/ STM BLS12-381 verification: types, BLS, aggregation,
lottery, Merkle, top-level Verify
verify/ Genesis Ed25519 verification
```
- Key encoding: the Mithril genesis key is serialized as an ASCII-
representation of a 32-byte array literal (e.g. `"[191,66,...]"`)
then hex-encoded. Decoder needs to unwrap both levels before handing
32 raw bytes to `ed25519.Verify`.
- Signed payload: `signed_message` field (32 bytes hex) is the output
of hashing the serialized `protocol_message` — the exact hash
function and canonicalization must match the Rust reference
(`mithril-common/src/protocol/` in the upstream repo). Likely
Blake2b-256 over a deterministic CBOR or JSON encoding; needs
confirming against upstream.
- Wire location: `internal/verify/verify.go``Genesis(...)`.
## What was non-obvious (so future-you doesn't have to dig)
### 2. STM BLS12-381 aggregate verification
Three things in the upstream Rust that aren't documented anywhere
prominent:
Every non-genesis certificate carries a `multi_signature` that is an
STM (Stake-based Threshold Multi-signature) aggregate proof over BLS12-381.
1. **DST is empty.** Mithril's BLS hash-to-G1 uses an empty domain
separation tag, not the IETF standard `BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_`.
The Rust calls `blst.verify(sig, msg, &[], &[], pk, ...)` — the
`&[]` is the empty DST.
2. **The signed message is `signed_message_bytes || mt_root_bytes`**,
not `signed_message` alone. The Merkle commitment root is appended
before BLS verify.
3. **Aggregation is MuSig-style scalar-weighted, not plain.**
`t_i = Blake2b-128(Blake2b-128(σ_0‖…‖σ_{n-1}) ‖ be_u64(i))`,
then `aggr_sig = Σ t_i·sig_i` and `aggr_vk = Σ t_i·vk_i`.
Plain summation does not interop with blst.
- Scheme: Chotard/Kiayias/Peters "Stake-based Threshold Multisignatures"
(Mithril paper §5-6).
- Library: `github.com/supranational/blst` Go bindings (IETF-draft
BLS12-381 operations; production-grade, consensus layers use it).
- Inputs:
- `next_aggregate_verification_key` from the previous-epoch cert's
`protocol_message` (the "trust handoff" between certs)
- `multi_signature` bytes (CBOR-encoded STM aggregate signature)
- `signed_message` (what was signed)
- Output: pass/fail, plus the epoch-boundary decision to promote
that cert's `next_aggregate_verification_key` for use by the NEXT
verification.
- Wire location: `internal/verify/verify.go``STM(...)`.
### Downstream once verification lands
- `verify` subcommand: takes a snapshot directory, walks the cert chain,
verifies genesis Ed25519 + each STM signature in order, validates the
`merkle_root` against the digests manifest's computed root, reports
per-stage pass/fail.
- Per-immutable SHA check against the `digests.json` manifest (already
downloaded — 16836 entries for preprod as of epoch 284).
- Full immutables loop for the `download -immutables` path.
Plus the `protocol_message` hash is SHA-256 over key-then-value, with
keys ordered by **Rust enum declaration order**, not alphabetical.
## Machine / LLM usage
Every query command accepts `-json` for structured output:
Every query command accepts `-json`:
```
mithril-go list -network mainnet -json # snapshot array
mithril-go show -network mainnet latest -json
mithril-go cert -network mainnet head -json
mithril-go cert -network mainnet head -chain -json
mithril-go info -network mainnet -json
mithril-go verify -network mainnet -json chain | jq '.steps[] | select(.kind=="genesis")'
```
Exit codes are stable:
Stable exit codes:
| Code | Meaning |
|---|---|
@ -120,24 +121,58 @@ Exit codes are stable:
These are the contract — existing codes won't renumber.
Planned: `mithril-go mcp` stdio server (Model Context Protocol) so MCP-native
agents (Claude Code, Cursor, etc.) can discover + call commands without
shelling out. Not yet implemented.
### MCP server
`mithril-go mcp` brings up a Model Context Protocol stdio server.
Compatible with any MCP client (Claude Code, Cursor, Zed, custom agents).
Tools exposed:
| Tool | Purpose |
|---|---|
| `mithril_info` | Network + aggregator + genesis verify key |
| `mithril_list_snapshots` | Newest-first list of cardano-database snapshots |
| `mithril_show_snapshot` | Detail for a snapshot (or `latest`) |
| `mithril_get_certificate` | Cert by hash (or `head`) |
| `mithril_walk_cert_chain` | Walk previous_hash from head to genesis |
| `mithril_verify_certificate` | Ed25519 OR STM BLS verify, dispatched by cert kind |
| `mithril_verify_chain` | Full chain verify with per-step report |
| `mithril_verify_genesis` | Walk to genesis + Ed25519 verify (legacy single-purpose tool) |
Example agent flow:
```
agent: tools/call mithril_verify_chain {network: mainnet}
→ {verified: true, length: 89, genesis_hash: "...", steps: [...]}
```
## Dependencies
- `github.com/klauspost/compress/zstd` — pure Go zstd decoder
- (pending) BLS12-381: `github.com/supranational/blst` via its Go bindings
- `github.com/consensys/gnark-crypto` — pure Go BLS12-381 (audited)
- `golang.org/x/crypto/blake2b` — stdlib-adjacent Blake2b
- `github.com/klauspost/compress/zstd` — pure Go zstd
No CGo. No `blst`. `go build ./cmd/mithril-go` produces a single static
binary. Cross-compile with `GOOS=linux GOARCH=arm64 go build`.
## Building
```
```bash
go build -o mithril-go ./cmd/mithril-go
go test ./...
go test -tags live ./... # hits live preprod aggregator
```
Produces a single static binary (~9.5 MB). CGo is not used; cross-
compilation is `GOOS=linux GOARCH=arm64 go build ./cmd/mithril-go`.
## Verified against live networks (latest run)
```
mainnet chain 89 STM + 1 genesis ✓
mainnet head 59 signers, 1972 wins ✓
mainnet genesis Ed25519 ✓
preprod chain 89 STM + 1 genesis ✓
preprod head 2 signers, 11 wins ✓
preprod genesis Ed25519 ✓
```
## License
TBD
Apache-2.0. See `LICENSE`. Matches the upstream Mithril project.