Implemented the remaining STM verification layers: - internal/stm/lottery.go: EvaluateSigma (Blake2b-512 lottery draw) + IsLotteryWon with Taylor-series threshold comparison (ported from mithril-stm::eligibility), big.Rat-based to match Rust's num_bigint/ num_rational path - internal/stm/merkle.go: Blake2b-256 Merkle batch-proof verification, faithful port of mithril-stm's verify_leaves_membership_from_batch_path including the 'current is left/right child' branch logic and the 1-byte zero pad for missing siblings - internal/stm/verify.go: top-level stm.Verify(msg, ms, avk, params) glues all four checks: k-threshold, lottery, Merkle, BLS aggregate - cmd: 'verify head' now runs full STM verification; JSON output shows signers, wins, params, verified flag - MCP: new 'mithril_verify_certificate' tool dispatches genesis Ed25519 vs STM by cert kind Verified against live networks: mainnet head cert bc00b551… epoch=626 59 signers 1972/16948 wins ✓ mainnet genesis 25acfcfe… epoch=539 Ed25519 ✓ preprod head dd9c4fcb… epoch=284 2 signers 11/100 wins ✓ preprod genesis 69bc3bdf… epoch=196 Ed25519 ✓ This is a consensus-correct pure-Go Mithril client. Single binary, CGo-free, no upstream Rust dependency. Next: full chain verification (walk head → genesis, check continuity).
72 lines
1.9 KiB
Go
72 lines
1.9 KiB
Go
//go:build live
|
|
|
|
package stm
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestFullSTMVerify_LivePreprodHead(t *testing.T) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
|
defer cancel()
|
|
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, preprodHead+"/artifact/cardano-database", nil)
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
t.Skipf("network: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
var snaps []struct {
|
|
CertificateHash string `json:"certificate_hash"`
|
|
}
|
|
if err := json.NewDecoder(resp.Body).Decode(&snaps); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
req, _ = http.NewRequestWithContext(ctx, http.MethodGet, preprodHead+"/certificate/"+snaps[0].CertificateHash, nil)
|
|
resp, err = http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer resp.Body.Close()
|
|
body, _ := io.ReadAll(resp.Body)
|
|
var cert struct {
|
|
MultiSignature json.RawMessage `json:"multi_signature"`
|
|
AggregateVerificationKey json.RawMessage `json:"aggregate_verification_key"`
|
|
SignedMessage string `json:"signed_message"`
|
|
Metadata struct {
|
|
Parameters struct {
|
|
K uint64 `json:"k"`
|
|
M uint64 `json:"m"`
|
|
PhiF float64 `json:"phi_f"`
|
|
} `json:"parameters"`
|
|
} `json:"metadata"`
|
|
}
|
|
if err := json.Unmarshal(body, &cert); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ms, err := DecodeMultiSig(cert.MultiSignature)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
avk, err := DecodeAVK(cert.AggregateVerificationKey)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
params := Parameters{
|
|
K: cert.Metadata.Parameters.K,
|
|
M: cert.Metadata.Parameters.M,
|
|
PhiF: cert.Metadata.Parameters.PhiF,
|
|
}
|
|
t.Logf("params: k=%d m=%d phi_f=%v", params.K, params.M, params.PhiF)
|
|
|
|
msg := []byte(cert.SignedMessage)
|
|
if err := Verify(msg, ms, avk, params); err != nil {
|
|
t.Fatalf("STM Verify: %v", err)
|
|
}
|
|
t.Logf("✓ FULL STM verification passed against live preprod head cert")
|
|
}
|