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:
parent
920d7cf177
commit
599085eaa9
8 changed files with 905 additions and 90 deletions
201
README.md
201
README.md
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue