987 Commits
Author SHA1 Message Date
zeekay 970b3009de chore: sync working tree
Commits 1 outstanding change(s) that were sitting uncommitted.
No build artifacts and no secrets in the changeset (both checked).
2026-07-26 10:06:40 -07:00
zeekay ef0f405ec6 fix(deps): repair the module graph + patch the AWS EventStream DoS
`go build ./...` failed with "missing go.sum entry for module providing
package" (pkg/thresholdd) — the go.mod was out of sync with the imports, so
individual `go get`s just walk further down the chain; tidy is the fix.

Also patches the panic-driven denial of service in the AWS SDK for Go v2
EventStream decoder, at minimum patched versions: eventstream v1.7.8,
service/s3 v1.97.3.

Verified: go build ./... goes from exit 1 to exit 0.
2026-07-26 06:44:48 -07:00
zeekayandHanzo Dev 128d8e4840 quorum: make Policy self-describing on the wire (3-of-5)
A quorum written as `"threshold": 3` is ambiguous — it reads as either the
signer count or the polynomial degree, and the two differ by one. That
ambiguity is what produced degree-0 (1-of-n) custody keys from configs that
said 3-of-5.

Policy now implements TextMarshaler/TextUnmarshaler over the operator form, so
any config, genesis blob or API payload that carries a quorum carries it in the
one representation that cannot be misread. Decode of an absent or undeployable
policy is an error rather than a zero value: {0,0} would reach cmp.Keygen as
degree -1, and a wrong-degree key that already holds funds has no recovery path
short of a resharing ceremony.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-25 13:00:38 -07:00
zeekayandHanzo Dev 247e5e4740 threshold: make 3-of-5 unambiguous, and prove it drives a real Safe
The stack had two different numbers called "threshold". cmp.Keygen and
frost.Keygen take t, the polynomial degree, and need t+1 signers. Operators,
dashboards and runbooks say k-of-n. Provisioning a "3-of-5" wallet by passing
threshold=3 produces a 4-of-5, silently — nothing errors, and the mistake only
surfaces when three custodians hold a ceremony and cannot sign a key that
already holds value.

pkg/quorum is now the single place those two numbers meet. Policy{K,N} states
what operators mean; Degree() == K-1 is the only k->t conversion in the tree.
Callers pass policy.Degree() and never compute the off-by-one themselves.

Proofs, all against the real protocols rather than mocks:

  protocols/quorum_3of5_test.go — five-party CGGMP21 DKG at the policy degree,
  three parties sign, and the signature both verifies under the group key and
  ECRECOVERS to the group's EVM address. Recovery is the part that matters for
  custody: a key can verify correctly and still resolve to an address no one
  controls. Four different 3-subsets all sign for one address. Negatives: every
  2-of-5 subset is refused, and a forged third share cannot produce a signature
  that verifies. FROST covered at the same policy.

  protocols/safe_3of5_test.go — the custody claim end to end. Real SafeL2 v1.5.0
  bytecode behind the real SafeProxyFactory on the luxfi/geth EVM, owned solely
  by the 3-of-5 group address. The digest comes from the deployed Safe's own
  getTransactionHash, so the test cannot sign something the contract would not
  check. execTransaction succeeds, value moves, the nonce advances, a replay is
  rejected, and a valid signature from a different 3-of-5 key is rejected.

Fixes found along the way:

  protocols/lss/adapters/evm.go — GenerateEVMAddress hashed the COMPRESSED
  point, i.e. 32 bytes of X, where the EVM requires Keccak over the 64-byte
  uncompressed X||Y. Every address it produced was well-formed and belonged to
  no key; value sent to one is unspendable. It had no callers, so this was a
  loaded gun rather than a live wound. Now decompresses, and reports failure
  instead of returning a plausible wrong answer.

  internal/test/harness.go — WithTimeout moved the harness deadline but not the
  handler's own ProtocolTimeout, which was hard-coded to five minutes, so a
  raised timeout silently did nothing and a CGGMP21 DKG under -race died at
  five minutes blaming the context. The harness now owns one deadline. Also
  removes a duplicate RunProtocolWithTimeout.

  protocols/cmp/cmp_threshold_test.go — cases labelled "3-of-5" passed degree 3
  and signed with four parties. They were 4-of-5. Labels now derive from the
  policy, so the name and the behaviour are one fact.

  go.sum — was missing entries for luxfi/metric and mattn/go-isatty; the FROST
  packages did not compile from a clean checkout.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-25 11:40:29 -07:00
zeekayandHanzo Dev 74672ad61e test(ecdsa): live devnet proof that SigEthereum lands a real EVM tx
Env-gated integration test (LUX_DEVNET_RPC + LUX_DEVNET_PK): signs an EIP-155
legacy tx with the deployer key via SigEthereum, recovers the sender locally,
broadcasts it, and asserts the receipt lands with status=success. EIP-155
(chainID-protected) is used deliberately — Lux rejects unprotected pre-155 txs,
which is exactly why the KMS /sign format matters.

Verified on devnet chainID 31337: SigEthereum-signed tx mined, status=success.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-21 08:30:12 -07:00
zeekayandHanzo Dev ba60fc96c2 fix(ecdsa): make SigEthereum correct, non-mutating, and tested
SigEthereum had ZERO callers (test or otherwise) and two latent hazards that
made it unsafe to wire: it mutated its receiver's S (Negate) and R
(UnmarshalBinary) in place — curve.Scalar/Point are pointer-backed, so a
value receiver still aliases — and it did a pointless post-hoc R rewrite.

Rewrite: copy S before low-S normalization (no aliasing), validate the R/S
encodings, and derive V from the compressed-R Y-parity byte (flipped when S is
negated). Output is the canonical 65-byte R.x‖S‖V (V in {0,1}), EIP-2 low-S,
directly accepted by luxfi/evm ecrecover.

Tests (SDKROOT="$(xcrun --show-sdk-path)" go test ./pkg/ecdsa):
  - EVMRecoverable: 256 random signatures — each emits low-S, recovers to the
    signer's pubkey via decred secp256k1 RecoverCompact (the routine luxfi/mpc
    and the EVM's ecrecover use), and leaves the receiver unmutated.
  - NormalizesHighS: forces the high-S branch and proves the low-S flip still
    recovers to the signer.

This is the shared helper the mpcd signing sessions now wire for canonical
EVM-ready threshold-ECDSA signatures.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-20 15:31:49 -07:00
zeekay beb54a6373 chore(deps): bump geth v1.20.1 + luxfi deps — stack unification 2026-07-15 11:17:06 -07:00
zeekayandHanzo Dev e12eeeeae4 test(cmp): pin degree-2 => 3-of-5 threshold property (2 signers refused, 3 verify)
Regression guard for the KMS/MPC threshold fix (mpc 1e1d318): a CGGMP21 key at
polynomial degree t=2 (keyInfo.Threshold for a 3-of-5 wallet) MUST reject any
2-signer committee at cmp.Sign -> CanSign -> ValidThreshold(2,2)=false, before
any round, and MUST produce a verifying signature with any 3 signers. This is
the line between genuine threshold security and the degree-0 (1-of-n) bug.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-14 17:45:11 -07:00
hanzo-dev 46585c947d ci: run linux jobs on lux-build-amd64 ARC scale set (no GitHub-hosted builders) 2026-07-11 00:12:27 -07:00
Hanzo AI 7ca3cc2844 fix(lss/adapters): sample Dilithium/ML-DSA secrets and masks via crypto/rand
Item5, re-targeted onto SHIPPED threshold v1.12.1 (cloud go.mod pin), not
the stale on-disk main.

The LSS Dilithium adapter (dilithium.go: sampleSecret, sampleError,
sampleMask) and the true-threshold ML-DSA Shamir adapter
(mldsa_threshold.go: sampleMaskingVector) sampled secret-key, error, and
masking-polynomial coefficients with math/rand, a non-cryptographic PRNG.
A predictable mask y is the lattice-signature analogue of ECDSA nonce
reuse and can lead to key recovery. Both adapters are gated out of the
production Corona/LSS path (factory_corona_prod.go) and unreferenced by
any consumer, but the footgun is loaded for whoever wires them in next.

Route all such sampling through a single crypto/rand-backed randIntn
helper (csprng.go). crypto/rand.Int already does unbiased rejection
sampling internally, so this is the one and only sampling path.

Regression tests (csprng_test.go): randIntn bounds/panic behavior,
freshness (non-determinism) of sampled secrets/masks across independent
DKG and signing runs, and an AST-based source scan (TestPackage_NoMath
RandForSecrets) that fails the build if math/rand (v1 or v2) is imported
into this package again.

No go.mod/go.sum change.
2026-07-08 07:26:29 -07:00
zeekayandHanzo Dev 2f9bfb1b09 scheme/bls: home the BLS threshold Scheme impl here (was crypto/threshold/bls)
Consolidates all threshold-signature IMPLEMENTATIONS under luxfi/threshold
(alongside cmp/frost/lss/corona/pulsar). The Scheme/Signer/Aggregator/
Verifier INTERFACE stays in luxfi/crypto/threshold — it is the shared
low-level contract that crypto/signer + hsm depend on, so it cannot move
up without a module cycle. One home for impls, one home for the contract.

scheme/bls/ carries the native BLS Scheme + the dealerless Pedersen-VSS
DKG (RunDKG). It implements crypto/threshold.Scheme using crypto/bls
primitives. Consumers blank-import luxfi/threshold/scheme/bls to register.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-03 14:34:58 -07:00
zeekayandHanzo Dev b4b124eeff migrate thresholdd daemon to dealerless RSS keygen + no-reconstruct hyperball sign (pulsar v1.9.0 pkg/ layout)
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-06-30 16:14:25 -07:00
z 94aaad1e18 docs(brand): add hero banner 2026-06-28 20:36:34 -07:00
z adf5bcddbf chore(brand): dynamic hero banner 2026-06-28 20:36:33 -07:00
zeekay 3d5946a863 Merge gate-b/threshold-talus-wip: dealerless committee-DKG pulsar+corona dispatchers (pulsar v1.2.0, corona v0.8.0)
Port pkg/thresholdd pulsar dispatcher off the removed v0.3 trusted-dealer
API onto the dealerless GF(q) committee DKG (NewLargeDKGSession +
LargeThresholdSigner + LargeCombine). Corona dispatcher on dealerless
keyera.Bootstrap, full-committee signing. lss corona-Round1 drift fixed,
dead parity bench removed. Strict-PQ gate kept (reconstruct-at-combine
reasoning). go build ./... + pkg/thresholdd + protocols/{pulsar,corona,lss}
all green.
2026-06-27 19:26:50 -07:00
zeekay 0d5c5d13a3 gate-b: go mod tidy (prune 6 stale go.sum entries; pulsar v1.2.0 + corona v0.8.0 pins unchanged) 2026-06-27 19:26:34 -07:00
zeekay ef20043a76 gate-b: fix lss corona-Round1 drift, drop dead parity bench, correct v0.3 docs
- protocols/lss/lss_pulsar_test.go: corona v0.8.0 Signer.Round1 now
  returns (*Round1Data, error); capture the error (was assign-1-of-2).
- protocols/parity/parity_test.go: DELETED. It benchmarked the removed
  v0.1 pulsar.ThresholdSigner/Combine small-committee API (ripped in H-1,
  gone in pulsar v1.2.0). No backwards compat — dead bench for deleted
  code is removed, not resurrected.
- pkg/thresholdd/{doc,profile,thresholdd_test}.go: correct stale comments
  that named the removed v0.3 DealAlgebraicV03Shares/OrchestrateV03SignCtx
  symbols; describe the dealerless committee-DKG + reconstruct-at-combine
  reality and why the strict-PQ gate stays load-bearing.
2026-06-27 19:25:49 -07:00
zeekay e621ebaab0 fix(gate-b): corona dispatcher must sign with full committee (all n)
corona v0.8.0 bakes the FULL-COMMITTEE Lagrange coefficient into every
share (keyera.computeFullCommitteeLagrange over all n; kernel sets
party.Lambda = share.Lambda, no per-subset recompute). The Lagrange
identity Σλ_i·f(i)=f(0) holds only with all n contributing, so the
threshold sign requires the full committee — t is the Shamir RECOVERY
threshold, signing is n-of-n. Dispatcher signed with only t shares →
incomplete Lagrange basis → self-verify failed. Fix: signers = all n
(matches every corona kernel round-trip test). TestCoronaRoundTrip green.
2026-06-27 19:03:10 -07:00
zeekay e0d4bf8bef wip(gate-b): port thresholdd pulsar+corona dispatchers to DEALERLESS committee DKG
pulsar.go: rewrite onto pulsar v1.2.0 large_* GF(q) committee-DKG API
(NewLargeDKGSession 3-round dealerless keygen + NewLargeThresholdSigner
Round1/2 + LargeCombine). Removes the v0.3 trusted-dealer
DealAlgebraicV03Shares path entirely. Sign/Sign_Ctx collapse into one
ctx-aware largeSign helper. Strict-PQ gate KEPT with corrected reasoning
(LargeCombine is reconstruct-at-combine, weaker than no-reconstruct
TALUS; gate guards dev-tooling dispatcher from feeding strict-PQ chains).

corona.go: dealerless keyera.Bootstrap (Pedersen dkg2) rewire.

Package compiles clean (GOWORK=off go build ./pkg/thresholdd/).
2026-06-27 18:45:34 -07:00
zeekay cc3fb58d23 wip(gate-b): corona v0.8.0 + pulsar v1.2.0 adapters (GenerateKeysTrustedDealer rename)
Agent-started gate-B fix, preserved. protocols/corona + protocols/pulsar build
clean against pulsar v1.2.0 / corona v0.8.0. REMAINING: pkg/thresholdd/pulsar.go
still uses pulsar's removed v0.3 Algebraic API (AlgebraicSetup/DealAlgebraicV03Shares/
OrchestrateV03Sign) — needs the TALUS port.
2026-06-27 17:43:52 -07:00
zeekay 1cd3aa5ef9 corona: fix Pulsar→Corona naming relics in protocols/corona wrapper
This package wires the github.com/luxfi/corona Module-LWE kernel
(Ringtail/Raccoon, q=0x1000000004A01), but carried stale "Pulsar"
labels from when Corona was historically named Pulsar:

- package doc claimed it wired the Pulsar kernel (github.com/luxfi/pulsar)
  and was "the equivalent of protocols/corona" — self-contradictory; the
  imports are github.com/luxfi/corona/{keyera,threshold}. Rewrote the doc
  to state the scheme accurately and note the Corona-vs-Pulsar distinction
  (both Module-LWE; Pulsar is FIPS-204/ML-DSA q=8380417, Corona is
  Ringtail/Raccoon q=0x1000000004A01).
- error strings "pulsar:" → "corona:" (sentinel identities unchanged;
  substrings asserted by tests preserved).
- exported aliases PulsarKeyEraID/PulsarGroupID → CoronaKeyEraID/
  CoronaGroupID (the corona kernel already uses the Corona* names; these
  wrappers had zero external consumers across ~/work/lux).

No wire format, modulus, or test vector changed. Doc-, string-, and
identifier-only. corona pkg builds + tests green.
2026-06-27 16:29:18 -07:00
zeekay 7606b46ee9 security: fail-CLOSED strict-PQ gate — empty/unknown chainID DEFAULT-REFUSE
The one canonical RefuseUnderStrictPQ gate (decomplected — magnetar's
duplicate slhdsatee gate removed with the TEE extraction). Drop the
chainID=="" short-circuit so empty/unknown chains consult the resolver;
production wires def=ProfileStrictPQ → refuse. Proven by the policy matrix.
2026-06-27 11:38:14 -07:00
lux c4044388e0 threshold: extract TEE custody into optional luxfi/tee extension
TEE-backed threshold custody was a threshold-internal, fenced opt-in
surface reachable only from tests (never on the ZAP dispatcher wire).
Decomplect it out: the dealerless/permissionless signing path is the
production default with zero TEE dependency.

Removed from the threshold core:
  - protocols/{mldsa,rlwe,slhdsa}-tee  -> moved to github.com/luxfi/tee
  - pkg/thresholdd/tee_options.go      (cc/attest + go-sev-guest glue)
  - the optional Sign_TEE / SetTEEBackend surface on the pulsar, corona
    and magnetar dispatcher schemes (teeBackend fields + unwired-error
    sentinels)
  - magnetar's duplicate slhdsatee-typed strict-PQ gate
    (profile/magnetarRefuseUnderStrictPQ/SetChainSecurityProfile/
    SetCombinerPool/Combine_TEE/AttestCombinerMember/PoolCombineMember)
  - the dispatcher-only TEE test files + their testdata

The permissionless keygen/sign/sign_ctx/verify paths are byte-unchanged.
The ONE canonical strict-PQ gate (profile.go RefuseUnderStrictPQ, applied
at Sign_Ctx_Profile) is retained; magnetar's second, TEE-typed gate is
gone -- one gate, one place. go mod tidy drops go-sev-guest AND luxfi/mpc
(both were TEE-only deps; the core had zero real imports of either).

Dependency direction is one-way: luxfi/tee depends on the cores
(pulsar/corona/magnetar/mpc), never the reverse.

Build + vet + dispatcher tests green; no consensus/mpc/corona consumer
referenced the removed surface.
2026-06-27 11:31:49 -07:00
zeekay 856794662d security: delete fake TFHE (full-key-per-party); fail-CLOSED strict-PQ gate
- rm protocols/tfhe (ALLOW_FAKE_TFHE stub holding the full master key per
  party; real threshold-FHE is luxfi/fhe). Zero external importers.
- RefuseUnderStrictPQ: drop the chainID=="" short-circuit so empty/unknown
  chains consult the resolver. Production wires def=ProfileStrictPQ → empty/
  unknown DEFAULT-REFUSE. Proven by TestRefuseUnderStrictPQ_PolicyMatrix
  (empty/unknown REFUSED on a strict-default node).
2026-06-27 00:34:51 -07:00
zeekay a00f53240f security(threshold): drop v0.1 Combine/NewThresholdSigner re-exports from pulsar alias — H-1b
The threshold/protocols/pulsar alias is the surface consensus reaches
into the pulsar reference kernel. It re-exported the v0.1
reconstruct-then-sign API — ThresholdSigner, NewThresholdSigner,
Combine, and the Round1Message/Round2Message wire types. That path
reconstructs the full ML-DSA signing key in aggregator memory (H-1);
its only production consumer was consensus's quasar wave_signer, now
deleted.

Removed those five re-exports. The alias now exposes only identity,
DKG, single-party sign/verify, and parameter surfaces — no
reconstruct-then-sign forward. Live-path threshold signing runs through
the corona dealerless kernel (protocols/corona), not this alias.

Verified: GOWORK=off go build ./... exit 0 (alias + full module);
gofmt clean; no threshold-internal consumer of the removed symbols.
2026-06-26 19:57:09 -07:00
zeekay 25a1245b41 chore(threshold): refresh luxfi/mpc + luxfi/zap go.sum h1 hashes
Same upstream same-version tag re-cut churn picked up across the rip
branches (cf. mpc e3dcc3f). go mod verify clean; build green. Was the
'wip in-flight checkpoint' placeholder; this is the actual change.
2026-06-26 19:57:08 -07:00
zeekay d92cad5638 security(thresholdd): remove trusted-dealer BLS scheme — H-2
The thresholdd daemon registered a "bls" scheme whose keygen ran a
TrustedDealer in-process (one node mints the whole group secret key) —
the same single-point-of-compromise trusted-dealer BLS custody removed
from mpc. BLS is consensus-only (native per-validator keys), never a
custodied threshold key. Remove the daemon's bls scheme so the dispatcher
can no longer mint a trusted-dealer BLS key.

- Delete pkg/thresholdd/bls.go (blsScheme / TrustedDealer keygen).
- server.go: drop s.schemes["bls"] + the bls keygen/sign/verify procs.
- zap_schema.go: drop ProcBLS{Keygen,Sign,Verify} opcodes.
- Tests: delete the bls-specific round-trip + benchmarks; rewire the
  auth-gate tests (which used bls only as a fast keygen driver) to frost;
  drop bls from the sign_ctx unsupported-scheme loop.
- Scrub stale bls mentions from doc comments.

NOT touched: protocols/bls (the library primitive) stays — luxfi/chains
(a non-owned repo) consumes bls.TrustedDealer/AggregateSignatures
directly. Deleting protocols/bls must be a coordinated change with the
chains owner; flagged, not forced here.

Verified: GOWORK=off go build ./... green; affected thresholdd tests
(auth-gate, ProcOpcode, ZapShares, FrostRoundTrip, sign_ctx unsupported)
pass without -short. Zero bls refs remain in thresholdd.
2026-06-26 18:46:02 -07:00
90e087aff0 chore: bump luxfi/database v1.19.3 (#26)
Co-authored-by: zeekay <z@zeekay.io>
2026-06-25 15:24:08 -07:00
zeekay bf0bda6934 corona→corona: academic Corona now only in lp-220-p3q-corona 2026-06-11 10:28:39 -07:00
Hanzo AI 8d99f628fc deps: bump Go to 1.26.4 across go.mod, Dockerfiles, GH Actions
Workspace-wide sync. luxfi/node already shipped on 1.26.4 in v1.30.6
(commit 121aca1fa9); this is the cross-repo catch-up.
2026-06-07 11:04:06 -07:00
Hanzo AI 765edeb776 deps: bump Go to 1.26.4 across go.mod, Dockerfiles, GH Actions
Workspace-wide sync. luxfi/node already shipped on 1.26.4 in v1.30.6
(commit 121aca1fa9); this is the cross-repo catch-up.
2026-06-07 10:45:42 -07:00
Hanzo AI 77391f1d1b go: 1.26.3 → 1.26.4 (security: crypto/x509, mime, net/textproto) 2026-06-06 22:10:09 -07:00
Hanzo AI a97e403445 corona → corona — final live-source sweep wave 3 2026-06-06 16:41:26 -07:00
Hanzo AI 66bdf12b74 rip local replace directives — one canonical published version
Per CTO: no local replaces, no backwards compat. Every module
resolves against published semvers.
2026-06-06 14:09:14 -07:00
Hanzo AI 43f3b16210 threshold: kill the corona wire-compat alias
Per CTO directive — no wire compat, no alias, no shim. The R-LWE
threshold signature scheme is named Corona across all live Lux
code. Pre-2026-06 callers using "corona" must migrate to "corona"
on the procedure-routing surface.

- Drop schemeAliases map + canonicalScheme() from types.go.
- Delete TestCoronaCoronaWireAlias from thresholdd_test.go.
- protocols/parity test comment sweep.

Forward-only; no migration window, no fallback path.
2026-06-06 13:56:29 -07:00
Hanzo AI 4146bb328b WIP: session checkpoint 2026-06-05 (codec rip in flight) 2026-06-05 15:34:04 -07:00
Hanzo AI 6c5c1e7ca6 test(parity): apples-to-apples Corona vs Pulsar bench harness
protocols/parity/parity_test.go is the canonical comparison harness for
the two threshold-signature schemes:

- Corona — Ring-LWE threshold sig (MAC-N×N-pairwise Corona-class).
- Pulsar — Shamir seed-reveal aggregator producing a FIPS 204 ML-DSA
  signature.

Both schemes are exercised through their threshold/protocols/<x> alias
surfaces. KAT tests use deterministic readers wherever possible; the
randomized=true path (FIPS 204) uses real RNG and is bench-only.

Run:
  go test -v -bench . -benchmem -run XXX ./protocols/parity/...
2026-06-04 17:07:18 -07:00
Hanzo DevandGitHub b4d36e1829 Merge pull request #22 from abhicris/test/2026-06-01-corona-validation-tests
test(corona): argument-validation tests for Bootstrap/Reshare/Reanchor + alias surface
2026-06-03 13:23:02 -07:00
Hanzo DevandGitHub 4b8004fa49 Merge pull request #23 from abhicris/test/2026-06-01-pulsar-alias-tests
test(pulsar): alias-surface tests for ParamsFor + Sign/VerifyCtx round-trip
2026-06-03 13:22:55 -07:00
Hanzo DevandGitHub 5b6d4b26e4 Merge pull request #24 from luxfi/feat/2026-06-02-lagrange-bigint-for-tfhe-combine
pkg/math/polynomial: add LagrangeAtZeroBigInt for tfhe combine (issue #20 precursor)
2026-06-03 13:22:51 -07:00
Hanzo AI 1f367bb03e threshold: corona→corona sweep (canonicalize naming per 2026-06 audit)
Sweep the residual "corona" name to "corona" across the e2e
validation harness and threshold-dispatcher docs, finishing the
cross-repo follow-up to luxfi/corona AUDIT-2026-06.md §4.3.

Touched files:
- e2e/doc.go, e2e/production_validation_test.go: rename `corona`
  scheme-name string + import alias `coronaKernel`→`coronaKernel`;
  align all narrative comments + the report-printer with the
  canonical Corona name. The previous string `"corona"` would have
  silently routed into KeygenError when calling the dispatcher's
  JSON-RPC since the scheme map was renamed to `"corona"` at HEAD.
- pkg/thresholdd/server.go: add `schemeAliases` + `canonicalScheme`
  helper, and consult it once at the dispatch entry in `ServeHTTP`.
  Inbound `corona.<op>` requests now silently route into the
  `corona` handler so external callers (mpcd in production, teleport
  test rigs, anything still on the legacy wire name) keep working
  through the migration window. Documentation in NewServer's doc
  comment names the alias and points at the deprecation note.
- pkg/thresholdd/thresholdd_test.go: TestCoronaCoronaWireAlias
  pins the alias behavior end-to-end (1-of-2 round-trip through the
  legacy `corona.{keygen,sign,verify}` namespace).
- LLM.md: document the alias in the per-scheme status table.

Wire compat: the JSON-RPC method namespace IS a wire-format string,
so the on-read alias is mandatory until callers migrate. Emit
`"corona"` on all new clients; remove the entry in schemeAliases
once external callers have moved.

Verified:
- go build ./... clean (CGO_CFLAGS=-I../accel/internal/capi/include
  -I$LUXCPP_PREFIX/include — vendored accel header has the fresh
  symbols missing from /opt/homebrew/include/lux/accel/c_api.h; this
  is a luxcpp packaging tail unrelated to this change).
- go vet ./... clean.
- gofmt -l . clean.
- go test -count=1 -short -timeout 600s ./... PASS (61/61 ok, 0
  FAIL). Highlights:
  - pkg/thresholdd 85.6s — TestCoronaRoundTrip +
    TestCoronaCoronaWireAlias both PASS; full scheme matrix
    (cggmp21, frost, pulsar, magnetar, bls) unaffected.
  - e2e 12.3s — TestProductionValidation_All round-trips
    pulsar (FIPS 204 external-verify) + magnetar (FIPS 205
    external-verify) + corona (out-of-band Corona kernel verify
    via luxfi/corona/threshold.VerifyBytes); negative controls
    reject tampered sigs on all three.

Out of scope (flagged for follow-up):
- luxfi/node vms/platformvm/warp/{signature,wire/*}.go still carry
  CoronaSignature/SigCorona Go types — wire-format type rename,
  separate audit / migration.
- luxfi/node docs/out/**.html — generated doc artifacts; do not
  hand-edit, regen from upstream docs source.
- lux-private/gpu-kernels/ops/mpcvm/*/mpcvm_corona.* + ops/crypto/
  corona/ — sister-repo kernel rename owned by lux-private agent.

Companion: luxfi/node Dockerfile.multichain `-tags corona` →
`-tags corona` committed separately on that repo's working branch.
2026-06-03 12:28:31 -07:00
Hanzo AI 79de146ddc brand-scrub: drop 'fuji' from e2e validation harness + report
The validation harness was named after 'fuji' but talks to the Lux testnet
(chain ID 96368 = 0x17870). Rename:
- defaultFujiRPC      → defaultTestnetRPC
- LUX_FUJI_RPC env    → LUX_TESTNET_RPC
- LUX_FUJI_PRIVKEY    → LUX_TESTNET_PRIVKEY
- CHAIN-LIVENESS fuji-C → CHAIN-LIVENESS testnet-C
- 'fuji testnet' prose → 'Lux testnet'

The fuji chain ID 43113 (Avalanche Fuji) and 43114 (Avalanche C-Chain) in
protocols/lss/factory.go were also fixed to use Lux mainnet 96369 / testnet
96368 — staged separately to avoid mixing with the in-flight Corona→Corona
rename.
2026-06-02 23:25:58 -07:00
Hanzo AI 89a196bedf fix: gofmt -s across repo (CI format check) 2026-06-02 23:25:58 -07:00
Antje WorringandHanzo Dev 8f68a280bf threshold/easycrypt: port BLS/CMP/FROST proofs + close 3 admits
EC version drift: True/False -> true/false; program vars must be
lowercase-initial; beta/delta reserved as reduction keywords; a comment closed
early on '^*).'; resolved a logical-binder/program-var name shadow. Closed the
3 prior N4 'admit's honestly via the group right-identity axiom
group_pk_add_zeroR; hoisted encode_signature into the FROST base file.

Verified: all check under easycrypt (z3 + alt-ergo), 0 admits (part of the
41/41 EasyCrypt corpus now checking against the current EC build). Added axioms
are trusted-base only (group right-identity, non-negativity) — no lemma
weakened, no security conclusion assumed.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-06-02 11:33:56 -07:00
Hanzo AI 276661292c chore(deps): go get -u ./...
Updated dependencies to latest minor versions via go get -u + go mod tidy.
2026-06-02 11:14:50 -07:00
Hanzo AI 1951bc7683 rename: Corona → Corona (canonical R-LWE threshold)
Removes all Corona references in favor of the canonical name Corona,
which is the production R-LWE threshold primitive shipping with the
Lux Quasar consensus stack.

Drops protocols/lss/forbid_academic_rlwe_test.go — the regression
guard that policed imports of luxfi/corona is no longer needed
now that Corona is the only name in the codebase.
2026-06-02 03:26:13 -07:00
Hanzo AI a2289cd58e go.mod: bump luxfi/accel v1.1.7 → v1.1.8 (//go:embed c_api.h fresh-clone fix) 2026-06-01 22:00:03 -07:00
Hanzo AI c8f1220d89 go.mod: bump luxfi/accel v1.1.2 → v1.1.7 (fresh-clone CI fix) 2026-06-01 21:55:12 -07:00
Hanzo AI e3179db5b5 deps: pulsar v1.0.23 → v1.1.1; rewire pulsar.Sign_Ctx onto full algebraic-aggregate
Closes PULSAR-V04-CTX cascade in luxfi/threshold. With pulsar v1.1.1
shipping OrchestrateV03SignCtx, the dispatcher's pulsar.Sign_Ctx
path now runs the full Round1 → Round2W → Round2Sign →
AlgebraicAggregateCtx loop with the FIPS 204 §5.4 ctx threaded into
the SHAKE-256 μ prehash. NO master sk is materialised in the
dispatcher process at any point during Sign or Sign_Ctx — the
historical dealerKey single-party shortcut has been deleted from
pulsarSession.

Changes:

  pkg/thresholdd/pulsar.go:
    - Removed pulsarSession.dealerKey (single-party PrivateKey).
    - Keygen no longer derives a per-session dealerKey; master seed
      is consumed by DealAlgebraicV03Shares and wiped immediately.
    - signCtxInternal now drives pulsar.OrchestrateV03SignCtx with
      the supplied ctx (matching pulsar.OrchestrateV03Sign's
      session-key + identity layer).
    - Comments updated to reflect that Sign_Ctx is now full
      algebraic-aggregate; strict-PQ gate semantics rebadged as
      "policy gate" (not "sk leakage gate") since the kernel-side
      claim is now stronger.

  pkg/thresholdd/strict_pq_gate_test.go (concurrently with sister
  agents' strict-PQ landing):
    - TestSignCtx_StrictPQ_RejectsDealerShortcut / _AllowsDealerShortcut
      keep historical names; the test asserts policy refusal/allow
      on strict-PQ chains. Refusal body unchanged. PASS.

  pkg/thresholdd/thresholdd_test.go:
    - TestMain global watchdog 10 → 30 minutes. With the
      algebraic-aggregate Sign_Ctx path (heavier than the v1.0.x
      dealer shortcut) plus -race instrumentation plus parallel
      subtests, the package suite runs ~14 minutes under -race.
      30m gives headroom; beyond that something IS stuck.

Test results:

  GOWORK=off go test -count=1 -timeout 600s ./pkg/thresholdd/
    ok  github.com/luxfi/threshold/pkg/thresholdd  32.6s

  go test -race -count=1 -timeout 1800s ./pkg/thresholdd/
    ok  github.com/luxfi/threshold/pkg/thresholdd  871.6s

  go test -count=1 -timeout 1200s ./...    (all packages green)

Dep matrix:
  pulsar:   v1.0.23 → v1.1.1   (PULSAR-V04-CTX)
  magnetar: v1.1.0  → v1.2.0   (sister cascade; already pinned)
2026-06-01 21:48:49 -07:00
Hanzo AI 7a4c90ca45 protocols/slhdsa-tee + pkg/thresholdd: strict-PQ profile gate + t-of-n attested-combiner pool
Closes the magnetar v1.1 strict-atom transient-SHAKE-bytes residual for
the strict-PQ chain profile.

Why
---

magnetar v1.1's strict-atom Combine path (thbsse_assemble.go) reduces
the FIPS 205 master's residency window to a few microseconds inside
the public combiner's SHAKE-expansion buffers. That window is
acceptable for legacy-compat (public combiner, no host in TCB) but
not for strict-PQ deployments (regulated custody, sovereign
settlement, high-value bridge). Closing the residual fully requires
either MPC-over-SHAKE (open research) or a TEE-attested combiner.
This commit takes the second path: route every strict-PQ Combine
through luxfi/threshold/protocols/slhdsa-tee.

What
----

* `protocols/slhdsa-tee/profile.go` --- `ChainSecurityProfile` value
  type (LegacyCompat / StrictPQ) + canonical refusal sentinels
  (`ErrMagnetarNoTEEAttestation` = `ERR_MAGNETAR_NO_TEE_ATTESTATION`,
  `ErrMagnetarStaleAttestation`, `ErrMagnetarInsufficientQuorum`,
  `ErrMagnetarSignatureDivergence`).

* `protocols/slhdsa-tee/pool.go` --- `CombinerPool` t-of-n attested-
  combiner registry. Each member binds a `Signer` to its
  `LastVerifiedReport`; `Attest` refreshes freshness state via
  `env.VerifyEvidence` and pins vendor against `KnownIssuers`;
  `Combine` selects the first `t` fresh members, drives each signer's
  full slhdsa-tee.Sign machinery, and refuses byte-divergent output.
  Defaults: Threshold=2, RotationWindow=60s, KnownIssuers={amd.sev.snp}.

* `pkg/thresholdd/magnetar.go` --- scheme-bound profile gate. Single
  function `magnetarRefuseUnderStrictPQ` (one function, one place ---
  mirrors `precompile/contract.RefuseUnderStrictPQ`). Called at the
  top of `Sign` and `Sign_Ctx`. Under strict-PQ both refuse with the
  canonical sentinel; only `Sign_TEE` (single-host) and the new
  `Combine_TEE` (t-of-n pool) produce signatures. Three new accessors
  on the scheme: `SetChainSecurityProfile`, `SetCombinerPool`,
  `AttestCombinerMember`.

* `Combine_TEE` --- the canonical strict-PQ surface. Takes a slice of
  `PoolCombineMember` (per-member attestation payload + name) + shared
  jobID/msg/ctx, drives `CombinerPool.Combine`, returns the agreed
  wire bytes + per-member audit signatures.

Composition with the existing resolver gate
-------------------------------------------

The sister-agent resolver gate (`thresholdd.RefuseUnderStrictPQ` +
`ChainProfileResolver`) fires first in the JSON-RPC routing layer
when the per-request `X-Chain-ID` resolves to strict-PQ. The new
scheme-bound gate is the orthogonal second axis: it refuses whenever
the process-wide profile binding says strict-PQ, regardless of
chain-ID resolution state. Both pass = sign proceeds. Either refuses
= hard refusal.

Tests
-----

* `pkg/thresholdd/magnetar_tee_gate_test.go` (5 tests):
  - `TestMagnetarCombine_StrictPQProfile_RequiresTEE`
  - `TestMagnetarCombine_AttestationVerified_AllowsSign`
  - `TestMagnetarCombine_StaleAttestation_RejectsSign`
  - `TestMagnetarCombine_TwoOfThreeAttestedCombiners_MatchSig`
  - `TestMagnetarCombine_SignatureDivergence_HardRefusal`

* `protocols/slhdsa-tee/pool_test.go` (5 tests):
  - `TestCombinerPool_Constructor_Defaults`
  - `TestCombinerPool_AddMember`
  - `TestCombinerPool_Attest_VendorPin`
  - `TestCombinerPool_Combine_FreshnessGate`
  - `TestCombinerPool_Combine_ByteEqualityAcrossQuorum`
  - `TestCombinerPool_Combine_Divergence`

All 11 new tests green against the committed AMD Milan SEV-SNP
fixture. Full `./pkg/thresholdd/...` + `./protocols/slhdsa-tee/...`
suites pass.

Production attestation
----------------------

Strict-PQ deployments today MUST use SEV-SNP --- the only `cc/attest`
verifier currently production-implemented. TDX + NRAS verifiers
return `ErrNotImplemented` (tracked at lux/mpc#222 stages 2-3) and
the pool's `KnownIssuers` allowlist will admit them once shipped ---
no magnetar-side code change required.
2026-06-01 21:28:54 -07:00