41 Commits
Author SHA1 Message Date
zeekayandHanzo Dev 7dca922b69 go 1.26.8
One toolchain across the estate. The tree carried 1.26.0, .1, .3, .4 and .5
at once, so which compiler built a binary depended on which repo it came
from.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-09 00:00:44 -07:00
zeekay a3861b8c46 Keep the forward pins only where the module builds
The previous commit carried go.mod changes from modules that do not build,
which is not a state worth publishing. Restore those and keep the pins for
the modules that resolve and build clean.
2026-09-01 13:30:03 -07:00
zeekay 2c82107216 Pin past the versions the proxy can no longer serve
The bulk retag moved published tags. go.sum and sum.golang.org hold the
original bytes, proxy.golang.org answers not-found, and the direct fetch
disagrees, so builds stopped at a checksum before reaching a compiler.
Pin forward to versions the proxy still serves.
2026-09-01 13:11:47 -07:00
zeekay 8273105926 The signing curve is derived from the network, not stated by the caller
Solana addresses were correct and receive-only: ConsensusKeyInfoStore.Save was
the only RegisterKey caller and hardcoded "secp256k1", and TriggerSign read the
curve from that field, so no product entry point could reach Ed25519 signing.

The hardcode is not fixed, it is UNREPRESENTABLE. transport.KeyInfo.KeyType is
deleted — the record carries one public key per curve and a PublicKeyFor(curve)
— and RegisterKey lost its keyType parameter, so there is no longer a place to
state a curve wrongly. A scalar KeyType was the wrong shape anyway: one keygen
ceremony produces BOTH a secp256k1 and an Ed25519 key, so "what curve is this
wallet" has no single answer.

The request carries the network instead, and ONE table maps it to a curve, with
NO default arm — KeyTypeForNetwork returns ErrUnknownNetwork rather than
guessing. That is the point: the old default returned secp256k1 for anything
unrecognized, which is how a Solana request would have been signed with the
wrong key.

/sign's 32-byte clamp becomes the SCHEME's rule rather than the endpoint's:
secp256k1 signs a digest and requires exactly 32 bytes, Ed25519 signs the
message at any length. /sign no longer accepts key_type at all — two ways to say
the curve is the misuse being removed — and Network is bound into the
idempotency fingerprint, so the same bytes for SOL and ETH conflict instead of
sharing one cached signature from a different key.

⚠ The trap this nearly shipped with: every pre-existing KeyInfo record has EMPTY
public keys, because the old Save wrote "", "". A naive "does this wallet have
this curve" gate would have refused every existing wallet on every curve. The
check is gated on RecordsCurves() — a record naming no curve denies none, and
the missing share stays the thing that fails closed.

Verified end to end, not by mirroring: a Solana request through
ConsensusMPCBackend.TriggerSign with a real 2-of-3 FROST ring produces a
signature crypto/ed25519.Verify accepts, over a 63-byte message so any
reintroduced clamp or pre-hash fails it; ETH/EVM/BTC/LUX still route to
secp256k1 and must RECOVER to the wallet's key. Four mutations tried, all
caught.

Behavioral risk worth naming: an unrecognized chain string now fails its
transaction closed rather than signing with a default. That is correct, and the
real fix is validating chain at POST /v1/transactions.
2026-08-06 15:59:30 -07:00
zeekayandHanzo Dev 99a1dab019 fix(deps): refresh re-tagged luxfi module checksums (age/mpc/pq)
The luxfi/{age,mpc,pq} tags were force-updated upstream, leaving the
recorded go.sum hashes pointing at pre-retag content. Refresh the
checksums at the SAME pinned versions (no version bumps) so the root
module and the e2e module build. All are GOPRIVATE-covered first-party
modules, so no sumdb authority is bypassed.

Unblocks: e2e/live_http_sign_test.go builds mpcd for the live 3-node
consensus ceremony.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-20 13:30:34 -07:00
zeekay 3d8d9c98d6 build: drop GOPRIVATE/GONOSUMDB + resolve poisoned deps via immutable proxy+sumdb
luxfi/* and hanzoai/* are PUBLIC Go modules. The GOPRIVATE / GONOSUMDB /
GOPROXY=direct env in Dockerfile + release.yml routed fetches around
proxy.golang.org + sum.golang.org, "working" only by skipping checksum
verification — which poisons go.sum with force-republished git bytes that no
longer match the immutable checksum db. Drop the bypass; resolve every
luxfi/hanzoai checksum from the public proxy + sumdb instead.

Poisoned deps resolved (strip luxfi/hanzoai go.sum lines, then go mod tidy):
- github.com/luxfi/edwards25519 v0.1.0 -> v0.1.1 (the agl/ed25519 replace
  target, in both go.mod and e2e/go.mod): v0.1.0 was force-republished, its zip
  hash diverges from the sum.golang.org pin and no copy verifies; v0.1.1 is the
  clean patch the proxy + sumdb agree on.
- github.com/luxfi/age v1.4.0, github.com/luxfi/zap v0.8.1: version healthy, but
  go.sum carried poisoned zip hashes — corrected to the sumdb canonical values.
- e2e/go.sum: also completed to the full canonical checksum set (hack-era builds
  with GOPROXY=direct skip go.sum completion).

Verified with GOWORK=off (matching the .dockerignore'd workspace + CI):
- go mod verify -> all modules verified (main + e2e)
- CGO_ENABLED=0 go build ./... clean (main + e2e)
- go test ./... -> 35 ok / 0 FAIL; signing, threshold, keygen, attestation, kms,
  ristretto255 curve, sr25519, and policy-gated sign-flow tests all pass. No
  crypto regression from the edwards25519 curve-lib bump.
2026-06-28 15:14:15 -07:00
zeekay 8c59d024b1 chore(mpc): commit go.sum/go.mod checksum churn from H-2 BLS-custody rip
Dep-checksum tail left uncommitted after 5c4d5cb removed bls_keygen_session.go.
- main go.sum: luxfi/zap v0.8.1 + luxfi/age v1.4.0 h1 refresh (upstream same-version
  tag re-cut; go mod verify = all modules verified, build green).
- e2e/go.mod + e2e/go.sum: go mod tidy churn (nats.go v1.50.0 -> v1.52.0 within v1,
  added indirects) for the e2e test module.
- go.work.sum: workspace checksum sync.

GOWORK=off go build ./... exit 0; go mod verify clean.
2026-06-26 21:18:02 -07:00
zeekay 255faab1cd fix(transport): propagate peer Ed25519 keys to consensus identity store
Consensus mode learned only its OWN Ed25519 public key: peer keys arrive in
ReadySignal.PublicKey during the transport identity handshake (sendIdentity) but
were discarded — never fed to the ConsensusIdentityStore that VerifyWireMessage
consults. Result: every inbound CGGMP21/FROST/signing wire message from a peer
was dropped ("public key not found for node …") and keygen/signing stalled to a
60s timeout. The HTTP /keygen and /sign endpoints could not work in any real
multi-node ensemble.

Fix (orthogonal, no import cycle): transport.Config.OnPeerIdentity observer is
invoked (notifyPeerIdentity, with self/empty/length guards + defensive copy)
whenever a peer key is learned — from the MsgMPCReady handshake in handleMessage
and handleIncoming. The acceptor now also replies with its own identity so the
key exchange is symmetric on the single tie-broken connection. main.go wires the
observer to ConsensusIdentityStore.AddPeerPublicKey. This is the consensus-mode
analog of the legacy NATS path's peers.json + *_identity.json seeding.

Proof: e2e/live_http_sign_test.go (//go:build integration) builds mpcd from the
repo root, starts a real 3-node 2-of-3 consensus ensemble, and over the live
HTTP API proves: distributed CGGMP21 keygen (real evm address), a real threshold
signature, idempotency cached-match, anti-oracle 409 on key reuse with new
content, 400 on empty key, and no plaintext key in any response or node log.
Unit coverage for the observer guards in pkg/transport/peer_identity_test.go.

Also fixes a pre-existing standalone_test.go bug: it set only ZAPDB_PASSWORD but
the env HSM provider reads MPC_PASSWORD — set both so the node starts.
2026-06-26 21:18:02 -07:00
zeekay b4be842240 chore: complete corona rename — strip residual corona identifiers (wire bytes preserved)
Bump e2e submodule's luxfi/threshold pin (v1.5.5 → v1.9.9) to drop the only
remaining corona puller in this workspace. Removes the residual
'github.com/luxfi/corona v0.2.0/go.mod' hash line from go.work.sum.
Align go.work go directive to 1.26.4 to match both workspace siblings
(../threshold and ./e2e). No source code in this repo imported corona;
this was workspace-graph hygiene only. R-LWE threshold primitive is now
referenced exclusively via luxfi/corona upstream.
2026-06-15 00:45:13 -07:00
zeekay 96a9a06b6f go: 1.26.3 → 1.26.4 (security: crypto/x509, mime, net/textproto) 2026-06-06 22:11:17 -07:00
zeekay da277ae96a fix: gofmt -s across repo (CI format check) 2026-06-02 11:39:20 -07:00
zeekay a393be55b2 go.mod: bump go directive to 1.26.3 (security advisory) 2026-05-12 20:55:00 -07:00
zeekay 3bb399cbdd mpc: deployment-mode flag + audit dispatcher for fund-private clusters
Adds the topology contract that lets a fund run mpcd as its own private
cluster — no Lux primary network registration, no shared M-Chain audit
ledger — using the same code path and the same operator surface.

Mode flag at cmd/mpcd/main.go:
  --network=primary|private              # registration target
  --audit-store=mchain|local-worm|...    # audit log destination
  --registrar-url, --registrar-auth      # primary-mode announcement
  --audit-worm-path, --audit-mchain-url  # backend selectors

pkg/audit:
  - Append-only Merkle hash chain. Tampering with any historical event
    invalidates every subsequent hash. Canonical bytes use fixed-order
    concatenation (json.Marshal map ordering is non-deterministic).
  - WORMDispatcher: NDJSON append, replay-on-start, fsync per Append.
  - MChainDispatcher: batched POST of (batchHash, fromSeq, toSeq) to a
    configurable anchor RPC.
  - compositeDispatcher: fan-out to multiple legs with per-leg sealing.
  - S3 Glacier Vault / Azure Immutable / GCS Object Lock dispatchers
    fail closed — wired into the flag surface but gated behind the
    cloud-audit build tag, no silent fallback.

pkg/identity/bootstrap.go: identity creation + (primary-mode only)
registrar announcement. Same on-disk format as the previous
loadOrGenerateIdentity (which is now removed); private mode is silent.

deployments/k8s-private: fund-only Kustomize overlay. Standalone
namespace, NetworkPolicy that blocks egress to anything outside the
fund's VPC, hardware-WORM PVC for the audit log, KMS-driven secret
template.

deployments/k8s-primary: env-var patch overlay on the existing lux-mpc
base. composite(local-worm,mchain) by default.

deployments/terraform/{aws,gcp,azure}: VPC + audit storage modules.
terraform validate passes; EKS/GKE/AKS + HSM modules are stubbed and
not yet plan-tested.

deployments/onprem/README.md: WORM storage + PKCS#11 HSM checklist.

Tests (14 total, all passing):
  pkg/audit:           6/6  (chain, WORM replay, MChain batching, composite,
                             cloud stubs fail loud, double-seal rejected)
  pkg/identity:        4/4  (private no-call, primary call, primary empty
                             URL no-op, primary registrar 503 surfaces)
  internal/privatemode: 4/4 (no registrar traffic, restart, tamper-detect,
                             cloud stubs fail loud)

Companion runbook in lux/papers/lux-private-mpc-deployment/
(LaTeX, 5 pages, tectonic-clean).
2025-12-28 16:20:40 -08:00
zeekay d658e47d2d mpc: per-node policy gate for non-bypassable signing
CanonicalIntent + LocalVerifier + SignGate. The MPC node signs only
if it independently verifies the policy bundle. Closes the bypassable
single point of failure where each node trusted the coordinator.

pkg/intent: CBOR-encoded intent, RFC 8949 Sec 4.2 deterministic
encoding. Approvals + node attestations excluded from digest.

pkg/policy/local_verifier: six-check gate (wallet, expiry, policy
hash, approval threshold, allowlist+limits, risk). Fails CLOSED.
Signed NodeAttestation for both approve and reject.

pkg/risk: Provider interface. InternalAllowlist (always available),
Chainalysis HTTP client + httptest coverage, TRM/Elliptic NOTIMPL
with documented endpoint shapes. CompositeProvider AND/OR.

pkg/mpc/sign_orchestrator: SignGate wraps verifier as
protocol-agnostic gate. AuthorizeSign returns ErrSignBlocked wrapping
the cause. AuthorizeAndDigest returns the digest the threshold
scheme must sign over.

e2e/policy_gate_test (tag policygate; pre-existing zapdb break) +
pkg/mpc/sign_orchestrator_e2e_test (runs in normal test suite):
3-node simulation proves non-bypassable.

56 tests, 4 packages, all passing.

go.sum: corrects luxfi/edwards25519 v0.1.0 /go.mod hash.
2025-12-28 16:07:25 -08:00
zeekay e5589e4a1a wallet: 9-tier wallet architecture as first-class concept
Add pkg/wallet implementing the 9-tier model (hot, warm, cold, gas,
bridge, contract_admin, validator, quarantine, disaster_recovery) with
canonical TierPolicy, in-memory Registry, domain-separation enforcement,
and per-wallet velocity/daily counters.

Domain separation rejects (cloud_provider, account) collisions and
HSM-provider collisions; cold + DR demand 3+ distinct cloud and HSM
vendors. Hot/warm/bridge/contract_admin demand 2+. Gas tier disables
the check (low value, allowlisted only).

Sign dispatch in pkg/api/handlers_wallet.go routes per tier:
quarantine rejects unconditionally; over-cap rejects with usage view;
airgapped tiers (cold, DR) return airgapped_queued; timelocked tiers
return timelocked with end-time; auto-approval tiers (hot, gas, bridge,
validator) commit counters and return auto_approved when amount is
under the large-amount escalation threshold; remainder return
approval_required with HumanApprovalsMin (or HumanApprovalsLarge above
threshold).

Routes mounted at /v1/wallet (no /api/ prefix per house rule).

Tests:
- pkg/wallet/tier_test.go: 16/16 pass
- e2e/tier_lifecycle_test.go: 9/9 pass
2025-12-28 15:54:10 -08:00
zeekay d5b900ad36 chore: exclude missing luxfi/genesis v1.5.21/22, sync edwards25519 replace 2025-12-28 10:03:12 -08:00
zeekay c6a281abf8 deps: luxfi/zapdb v4 → v1.0.0 (module path reset) 2025-12-28 05:25:05 -08:00
zeekay b72d63af75 fix: use badger API directly in e2e test instead of kvstore.Store
CheckKeyInAllNodes was assigning *badger.DB to kvstore.Store.DB which
requires database.Database — badger.DB is missing the Compact method.
The test only needs Get and Close, so use badger.View/txn.Get directly.
2025-12-28 02:06:25 -08:00
zeekay 995bc17af9 fix: rename env vars to MPC_PASSWORD, remove LUX_ prefix
- Default env var: MPC_PASSWORD (was LUX_MPC_PASSWORD)
- No fallback to ZAPDB_PASSWORD — single source
- Factory env_var config reads from config map only (not env)
- Cloud HSM providers unchanged (ZAPDB_ENCRYPTED_PASSWORD)
- All tests pass
2025-12-28 00:40:20 -08:00
zeekay 93449007f0 chore: sync local changes 2025-12-28 00:33:42 -08:00
zeekay d2e33e1d45 fix: goimports formatting with local-prefixes (matches golangci-lint) 2025-12-27 23:14:14 -08:00
zeekay 08cb29c9a3 fix: goimports formatting + CGO_ENABLED=0 for CI tests
- Run goimports -w on all 17 Go files with formatting issues
- Set CGO_ENABLED=0 on all CI steps (test, lint, build, CodeQL, SBOM)
- Remove -race flag from tests (requires CGO which pulls in blst.h)
- All 17 test packages pass with CGO_ENABLED=0
2025-12-27 23:01:00 -08:00
zeekay a8925893b5 security: add orgID multi-tenancy isolation, rename badger to zapdb
Multi-tenancy isolation (addresses Dragonfire review MPC audit):
- Add OrgID field to all event messages (keygen, sign, reshare)
- Scope session dedup keys as orgID-walletID-txID
- Namespace KVStore keys with org prefix (org:<orgID>:<key>)
- Backward-compatible fallback to legacy unscoped keys
- New org_kvstore.go with OrgScopedKey/GetKeyShareWithFallback

Naming cleanup:
- Rename all badger references to zapdb in code, config, tests
- config.yaml: badger_password → zapdb_password
- AppConfig: BadgerPassword → ZapDBPassword
- Fix MPCiumVersion typo → MPCVersion
2025-12-27 22:41:08 -08:00
zeekay 7620698a36 feat: sr25519 threshold signing for Polkadot/Substrate
MPC daemon now supports sr25519 (Schnorrkel) key generation and
threshold signing using FROST over the ristretto255 group.

New files:
- pkg/mpc/sr25519_keygen_session.go — threshold keygen
- pkg/mpc/sr25519_signing_session.go — threshold signing
- pkg/mpc/sr25519_config_marshal.go — CBOR serialization
- pkg/mpc/ristretto255.go — local curve implementation
- pkg/eventconsumer/signing_handler_sr25519.go — event routing

Wiring:
- KeyTypeSR25519 constant + session type
- Event consumer routes sr25519 keygen/signing events
- API accepts "sr25519" as valid protocol
- DOT/KSM network codes mapped to sr25519 key type
- Signing context configurable (default "substrate")

14 tests passing.
2025-12-27 21:41:32 -08:00
zeekay d2a7c0f0d0 chore: bump Go 1.26.0 → 1.26.1
Fixes 5 stdlib CVEs (html/template, os, net/url, crypto/x509 x2).
2025-12-27 13:05:01 -08:00
zeekay 7cfc8e9874 chore(e2e): use real semver tag v1.0.5, remove local path replace
e2e now references github.com/luxfi/mpc v1.0.5 (semantic tag).
Local resolution handled by go.work workspace, not replace directive.
Only remaining replace is agl/ed25519 → luxfi/edwards25519 (broken upstream).
2025-12-27 17:03:24 -08:00
zeekay ccb91d25e0 chore: go 1.26, add go.work, tidy modules
- Bump e2e module to go 1.26.0
- Add go.work workspace file (replaces need for local replaces)
- Remove dgraph-io/badger from e2e direct deps (unused directly)
- Tidy both modules
- Document agl/ed25519 redirect requirement (broken upstream)
2025-12-27 16:56:47 -08:00
zeekay d73d4876e1 refactor(kvstore): rename Badger→ZapDB types, clean up package API
- git mv badger.go→zapdb.go, badger_backup.go→zap_backup.go
- BadgerKVStore→Store, BadgerConfig→Config, NewBadgerKVStore→New
- BadgerBackupExecutor→Backup, NewBadgerBackupExecutor→NewBackup
- BadgerBackupMeta→BackupMeta, BadgerBackupVersionInfo→BackupVersion
- Config fields: DBPath→Path, EncryptionKey→Key, BackupEncryptionKey→BackupKey, BackupDir→Dir
- Backup fields: BackupEncryptionKey→Key, BackupDir→Dir
- Store fields: BackupExecutor→Exec, db→DB (exported, fixes e2e compile)
- Updated all callers: transport/factory, transport/state, backup/s3,
  cmd/mpcd/main, cmd/lux-mpc-cli/recovery, scripts/migration/add-key-type,
  e2e/base_test, pkg/kvstore/backup_test
2025-12-27 16:23:40 -08:00
zeekay 7fd512051e feat: replace BadgerDB with ZapDB, remove local replace directives, vendor mode CI/CD
- Replace github.com/dgraph-io/badger/v4 with github.com/luxfi/zapdb/v4 v4.9.2
  throughout pkg/kvstore/, pkg/transport/, cmd/mpcd/, e2e/, scripts/
- Rename BadgerPath/BadgerPassword → ZapDBPath/ZapDBPassword in FactoryConfig
- Remove local replace directives from go.mod:
  - hanzoai/orm: now uses published v0.3.2 (tagged on GitHub)
  - hanzoai/kv-go/v9: now uses published v9.17.2-hanzo.1
  - Only remaining replace: agl/ed25519 → luxfi/edwards25519
- Add vendor/ directory (GOWORK=off go mod vendor) for reproducible builds
- Update Dockerfile to golang:1.26-alpine + -mod=vendor build flag
- Update CI/CD to use self-hosted linux/amd64 runners (no cross-compilation)
- Add k8s/valkey.yaml for session KV store
- Update k8s manifests: api-kv support, ZapDB password env, fresh secrets
- Dashboard API and MPC nodes all tested running on cluster (3/3 nodes healthy)
2025-12-27 15:30:42 -08:00
zeekay 33c08f435d chore: sync dependencies and format code 2025-12-27 09:26:29 -08:00
zeekay 6c1e9138ca chore: update logger imports and dependencies 2025-12-27 09:00:00 -08:00
zeekay 8f3f43c612 chore: update LICENSE and go.mod 2025-12-31 22:21:37 -08:00
zeekay c3d5a91dc1 chore: upgrade threshold to v1.1.11 (LSS livelock fix) 2025-12-28 17:14:28 -08:00
zeekay 821bd09da5 chore: remove bridge stub, fix all TODOs with real implementations
- Remove lux-mpc-bridge command (bridge is in T-Chain, not MPC)
- Remove empty pkg/bridge directory
- Fix sign.go: remove stale TODO (ECDSA/EdDSA already supported)
- Fix pubsub.go: remove obsolete subscription TODO
- Fix benchmark_test.go: implement real protocol benchmarks
- Fix xrpl_test.go: implement full XRPL E2E tests
- Update goreleaser: remove bridge build target
- Update docker-compose: remove bridge-compat services

MPC is now a clean standalone threshold signature tool.
2025-12-25 10:44:16 -08:00
zeekay 8952776a9b fix: format imports with goimports
- Fix import formatting in all files
- Ensure consistent import grouping
- Resolve golangci-lint formatting issues
2025-08-17 00:00:28 -05:00
zeekay 98421eb533 fix: update dependencies and fix e2e test issues
- Fixed shell script compatibility for macOS
- Updated e2e module dependencies
- Simplified benchmark tests
- Fixed KMS client build
2025-08-16 13:12:25 -05:00
zeekay e89da4d563 Update examples 2025-08-07 10:55:10 -05:00
zeekay 83969bc35e feat: Complete migration to luxfi/threshold library
Major changes:
- Replaced Binance TSS library with luxfi/threshold (supports CGGMP21 & FROST)
- Added XRPL network support for secp256k1 signatures
- Implemented resharing functionality for both ECDSA and EdDSA
- Fixed protocol adapters for CGGMP21 and FROST
- Updated event consumer to use new node methods
- Fixed all build errors in core packages

Technical improvements:
- Simplified party.ID usage (now string-based)
- Added proper message types and encoding
- Updated session interfaces with required methods
- Fixed test suite to work with new library structure

All core package tests passing. Ready for mainnet deployment.
2025-08-02 20:49:39 -05:00
zeekay 0b8661ced9 Use new threshold lib 2025-08-02 15:49:26 -05:00
zeekay 4ec50522c9 Add benchmarks, tests, updated docs 2025-08-02 15:27:50 -05:00
CodyandGitHub c6e2c3c06f Add integration, unit testing CI pipelines (#48)
* Add comprehensive tests for multiple pkg

* Add db_path to config

* Add e2e testing pipeline

* Add github workflow

* Remove config.test.yaml

* Fix lint issue

* Increase timeout

* Add e2e testing for signing

* Skip signing if node is not in participant list

* Code cleanup

* Add e2e testing for signing and resharing

* Increase timeout

* Add test instructions

* Update github workflow branching

* Add vulnerabilties check in CI

* Fix ci
2025-07-06 14:26:03 +07:00