certificate chain walker + progress bar fix

- aggregator.CertChain: walks previous_hash from head until genesis_signature
- cmd: 'cert' subcommand, -chain flag for full walk, 'head' shortcut resolves
  latest snapshot's certificate_hash
- ProgressFn now signals both bytes-read and total-from-Content-Length so
  percent is computed against the actual transfer size, not the uncompressed
  target
- verified against preprod: 90-cert chain head→genesis, Ed25519 genesis cert
  shape (64-byte sig over 32-byte signed_message, protocol_message carries
  next_aggregate_verification_key for BLS), STM-signed non-genesis certs

pipeline is now verification-sprint ready
This commit is contained in:
Sulkta 2026-04-23 15:20:32 -07:00
parent e4012bf79a
commit 517df1130d
3 changed files with 120 additions and 15 deletions

View file

@ -48,6 +48,8 @@ func main() {
os.Exit(cmdDownload(ctx, args))
case "verify":
os.Exit(cmdVerify(ctx, args))
case "cert":
os.Exit(cmdCert(ctx, args))
case "info":
os.Exit(cmdInfo(args))
case "version", "--version", "-v":
@ -70,6 +72,7 @@ Usage:
Commands:
list List available cardano-database snapshots
show Show detail for one snapshot (hash or "latest")
cert Show a certificate or walk the chain back to genesis
download Download a snapshot to a target directory
verify Verify an already-downloaded snapshot (not yet implemented)
info Show network + aggregator info
@ -190,7 +193,7 @@ func cmdDownload(ctx context.Context, args []string) int {
return 1
}
digestsArchive := filepath.Join(*out, "digests.tar.zst")
if err := downloadWithBar(ctx, digestsURIs[0], digestsArchive, snap.Digests.SizeUncompressed); err != nil {
if err := downloadWithBar(ctx, digestsURIs[0], digestsArchive); err != nil {
fmt.Fprintln(os.Stderr, "digests download:", err)
return 1
}
@ -209,7 +212,7 @@ func cmdDownload(ctx context.Context, args []string) int {
return 1
}
anciArchive := filepath.Join(*out, "ancillary.tar.zst")
if err := downloadWithBar(ctx, anciURIs[0], anciArchive, snap.Ancillary.SizeUncompressed); err != nil {
if err := downloadWithBar(ctx, anciURIs[0], anciArchive); err != nil {
fmt.Fprintln(os.Stderr, "ancillary download:", err)
return 1
}
@ -235,6 +238,68 @@ func cmdVerify(ctx context.Context, args []string) int {
return 1
}
func cmdCert(ctx context.Context, args []string) int {
fs := flag.NewFlagSet("cert", flag.ExitOnError)
chain := fs.Bool("chain", false, "walk previous_hash back to the genesis certificate")
maxDepth := fs.Int("max-depth", 1024, "chain walk safety cap")
n, rest, err := resolveNetwork(fs, args)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return 2
}
if len(rest) == 0 {
fmt.Fprintln(os.Stderr, "cert: hash required (or 'head' to use the latest snapshot's cert_hash)")
return 2
}
head := rest[0]
c := aggregator.New(n.AggregatorURL)
if head == "head" {
snap, err := resolveSnapshot(ctx, c, "latest")
if err != nil {
fmt.Fprintln(os.Stderr, "resolve head:", err)
return 1
}
head = snap.CertificateHash
}
if *chain {
certs, err := c.CertChain(ctx, head, *maxDepth)
if err != nil {
fmt.Fprintln(os.Stderr, "chain:", err)
return 1
}
fmt.Printf("chain length: %d (head → genesis)\n\n", len(certs))
for i, ct := range certs {
role := ""
if ct.GenesisSignature != "" {
role = " [GENESIS]"
}
fmt.Printf("[%3d] %s epoch=%d prev=%.16s…%s\n",
i, ct.Hash, ct.Epoch, ct.PreviousHash, role)
}
return 0
}
cert, err := c.GetCertificate(ctx, head)
if err != nil {
fmt.Fprintln(os.Stderr, "cert:", err)
return 1
}
fmt.Printf("hash: %s\n", cert.Hash)
fmt.Printf("previous_hash: %s\n", cert.PreviousHash)
fmt.Printf("epoch: %d\n", cert.Epoch)
fmt.Printf("signed_message: %s\n", cert.SignedMessage)
fmt.Printf("genesis sig: %s\n", yesNo(cert.GenesisSignature != ""))
fmt.Printf("multi_signature: %d bytes (raw)\n", len(cert.Multisignature))
fmt.Printf("protocol_message: %d bytes (raw)\n", len(cert.ProtocolMessage))
return 0
}
func yesNo(b bool) string {
if b {
return "yes"
}
return "no"
}
func cmdInfo(args []string) int {
fs := flag.NewFlagSet("info", flag.ExitOnError)
n, _, err := resolveNetwork(fs, args)
@ -272,19 +337,19 @@ func cloudURIs(locs []aggregator.Location) []string {
return out
}
func downloadWithBar(ctx context.Context, uri, dest string, expectedSize uint64) error {
func downloadWithBar(ctx context.Context, uri, dest string) error {
fmt.Printf(" %s\n", uri)
start := time.Now()
var last int64
cb := func(b int64) {
cb := func(read, total int64) {
elapsed := time.Since(start).Seconds()
rate := float64(b) / elapsed
rate := float64(read) / elapsed
pct := ""
if expectedSize > 0 {
pct = fmt.Sprintf("%5.1f%% ", float64(b)/float64(expectedSize)*100)
if total > 0 {
pct = fmt.Sprintf("%5.1f%% ", float64(read)/float64(total)*100)
}
fmt.Printf("\r %s%s @ %s/s ", pct, humanSize(uint64(b)), humanSize(uint64(rate)))
last = b
fmt.Printf("\r %s%s @ %s/s ", pct, humanSize(uint64(read)), humanSize(uint64(rate)))
last = read
}
err := artifact.Download(ctx, uri, dest, "", cb)
fmt.Printf("\r %s in %s \n", humanSize(uint64(last)), time.Since(start).Round(time.Second))