github.com/luxfi/coronad existed only as loose files: no .git, no history, no
remote. Invisible to any sweep that looks for a .git directory, and an rm -rf
would have destroyed it unrecoverably.
Includes two dependency repairs.
1. It did not build: "unknown revision v1.16.98" — the v1.16.x line was DELETED
from github.com/luxfi/geth. The requirement is baked into published modules
(crypto/constants/warp pin the dead tag, some also `exclude` the
alternative), so requiring crypto v1.20.2 / constants v1.6.2 / warp v1.24.1
is what escapes it.
2. Dropped two replace directives pointing at absolute paths on one disk:
replace github.com/luxfi/corona => /Users/z/work/lux/corona
replace github.com/luxfi/warp => /Users/z/work/lux/warp
Those would have made this module resolvable ONLY on this machine — CI or
any other checkout could never build it. They were also unnecessary: the
published versions the `require` lines already name (corona v0.10.4) build
and test clean without them.
Verified: go build ./... exits 0, go test ./... 1 ok / 0 fail.
The compiled coronad binary is ignored rather than committed.
NOTE: no remote — luxfi/coronad does not exist on GitHub. Still one copy.
5.9 KiB
coronad — offline Corona (Ringtail / Module-LWE) threshold-signer service
coronad orchestrates the EXISTING corona kernel (github.com/luxfi/corona) to
produce the warp.CoronaEvidence the warp Corona finality lane verifies. It
adds no lattice crypto — every cryptographic operation is a corona kernel
call. It is the Corona-lane realization of warp's CoronaDKG2Signer boundary
(warp/offline_signers.go).
Why a standalone module (not corona/cmd/coronad)
The dependency law: warp imports corona; corona MUST NOT import warp (that
closes the cycle warp -> corona -> warp). coronad emits warp.CoronaEvidence
and uses warp/pulsar.SerializeCoronaSig + warp.CoronaSigningBytes, so it must
sit ABOVE both. Putting it in corona/cmd would make the corona module import
warp — forbidden. Hence github.com/luxfi/coronad, importing corona + warp.
corona (kernel: keyera DKG, threshold sign, reshare) -- never imports warp
warp (CoronaEvidence, RingtailVerifier, suites) -- imports corona
coronad (this module) -- imports both
Module wiring (go.mod)
require github.com/luxfi/corona v0.8.0,github.com/luxfi/warp v1.23.0.replaceBOTH to their local working trees. corona because coronad drives the unreleased keyera/threshold/reshare API (v0.8.0 line; the v0.7.6 warp pins is older —GenerateKeysvsGenerateKeysTrustedDealer,Round1w/o error). warp because the typed-evidence API the task targets (CoronaEvidence, subject-agnosticVerifyRingtailThreshold(subject, ev),CoronaDKG2Signer) is the working-tree shape. Forcing the WHOLE graph to local corona keepscorona.Signature/GroupKeya SINGLE type across coronad and warp/pulsar (no version-skew type mismatch). warp's library (pulsar.go) uses only stable corona symbols, so it compiles fine against local corona; only warp's own _test.go uses the renamed API, and coronad's build never compiles those.- Build env:
SDKROOT="$(xcrun --show-sdk-path)" GOWORK=off GOFLAGS=-mod=mod GOPRIVATE=github.com/luxfi/*. Theld: warning: search path .../luxcpp/install/lib not foundis harmless (accel CGO dep).
Files (orthogonal concerns, one each)
era.go—Era: committee key-era state.Bootstrap(dealerless Pedersen-VSS DKG viakeyera.Bootstrap),Reshare/Refresh(advance Generation, preserve GroupKey), accessors,Member.member.go—Member: one member's OWN share +sessionSigner(swaps in the active-set Lagrange coefficient; secret never leaves the member).transport.go—Transportinterface (two all-gather rounds + Abort) andTransportFactory.loopback.go—Loopback: in-process, concurrent, barrier-synchronized transport (race-clean). Production wraps corona'snetworking(ZAP P2P).session.go—SigningSession.Run: Round1->Round2->Finalize over the transport; recomputes Lagrange for the ACTIVE participant set (true t-of-n).signer.go—Signerimplementswarp.CoronaDKG2Signer.ThresholdSignderiveswarp.CoronaSigningBytes(subject), runs the session, self-verifies fail-closed, emits evidence.evidence.go—ToEvidence:*threshold.Signature->warp.CoronaEvidenceviawarp/pulsar.SerializeCoronaSig.resolver.go—EraResolverimplementswarp/pulsar.CoronaGroupKeyResolver.activation.go— post-reshare circuit-breaker:SignActivation/VerifyActivationCert(wrapsreshare.VerifyActivation).cmd/coronad— CLI:version,demo(in-process bootstrap->sign->verify).
Threshold semantics
The KEY is a genuine (t,n) Shamir sharing of the lattice secret, generated
DEALERLESSLY. SIGNING recomputes Lagrange for whatever participant set shows up
(>= t), so ANY t-of-n subset can sign — proven by the e2e (strict 3-of-5
{0,2,4} verifies). Fewer than t cannot (kernel minority_soundness_test:
PRF masks telescope to zero only over the declared set).
No-reconstruct
Each Member holds only its own SkShare. The Transport carries only public
Round1Data/Round2Data (structurally — neither type contains a share);
Finalize aggregates only public z partials. A secret share is NEVER
centralized during signing. (The in-process Era holds all shares only because
the reference DKG runs in one process; production = one share per node via the
distributed dkg2 ceremony. Signing is byte-identical either way.)
Fail-closed
ThresholdSign runs threshold.Verify before emitting. coronad NEVER emits
evidence that won't verify on chain. Worst case under any fault = no evidence
(liveness), never invalid evidence (safety).
Tests (all go test -race ./... green, ~23s)
TestEndToEnd_CoronadSigns_WarpVerifies— headline: 3-of-5 bootstrap, sign a quasar-M subject, warpRingtailVerifierACCEPTS (33052-byte sig).TestTrueThreshold_StrictSubsetSigns— strict t-subsets verify; t-1 refused.TestVerifierRejectsTamperedAndWrongEra— tampered subject / wrong gen / corrupt sig all rejected.TestReshareAndRefreshPreserveGroupKey— Refresh+Reshare advance Generation, key preserved.TestOnChainRegistryPath— verify under a WIRE-deserialized GroupKey (fresh ring Params) — the production registry path.TestSuiteMismatchRejected,TestActivationCircuitBreaker.
Production hardening (NOT done — see RED HANDOFF in handoff notes)
- Real network transport (wrap corona/networking ZAP P2P; add round timeouts + authenticated channels). The loopback passes pointers in-process.
- Distributed DKG custody (one share per node) instead of the in-process Era.
- Liveness layer to choose a live t-subset and retry (coronad signs the set it is given; it does not detect availability).
- Distributed reshare VSR exchange (commit/complaint transcript) feeding the activation cert; coronad provides the sign/verify halves only.
- Share persistence + committee membership sourced from chain.