Files
mpc/cmd/mpcd
91cca33dba fix(address): left-pad compressed-pubkey coords in pubKeyToEVMAddress (case 33) (#20)
Follow-up to #19 (emit 33-byte compressed keygen pubkey). pubKeyToEVMAddress's
case-33 assembled the EVM preimage as append(x.Bytes(), y.Bytes()...). big.Int.
Bytes() drops leading zero bytes, so a coordinate < 2^248 (X or Y with a high
zero byte, ~1/256 of keys) produced xyBytes<64 → the len!=64 guard returned an
EMPTY address for an otherwise-valid key. Now FillBytes zero-pads each coordinate
into a fixed 32-byte slice (case 32 already did this; case 33 did not — it was
latent until #19 made keygen actually emit 33-byte keys).

Verified: pubkey 03·005056e0… (X has a leading 00 byte) → old path xyBytes=63 →
empty; fixed path xyBytes=64 → 0x7242EFb11FB1A669486F10649C9B73739731a5e6, byte-
identical to luxfi/crypto DecompressPubkey+PubkeyToAddress.

Co-authored-by: hanzo-dev <[email protected]>
2026-07-07 21:54:53 -07:00
..

mpcd — MPC Daemon

mpcd is a threshold-signature daemon. It speaks ZAP wire protocol over TLS 1.3 directly between peers — no NATS, no Consul, no PostgreSQL in the critical path. The daemon is the same binary in every deployment shape; the operating mode is selected entirely by flags.

Operating modes

There is exactly one start command (mpcd start) and the topology is determined by the --peer and --threshold flags:

Topology Flags Use
Standalone (1-of-1 self-loop) --threshold 1 (no --peer) Smoke tests, dev sandbox
Networked t-of-n cluster --threshold t --peer ... (n−1 peers) Production

In all cases mpcd runs the consensus-embedded transport (pkg/transport). The "consensus" here is local PoA membership over the node's own Ed25519 identities — it is intentionally not the Lux mainnet chain, because key shares must never leave the cluster.

Standalone (1-of-1)

mpcd start \
    --node-id solo \
    --listen :9999 \
    --api :9800 \
    --data /var/lib/mpcd \
    --threshold 1

Starts mpcd with itself as the only quorum member. /healthz will report signing_quorum: true immediately. Keygen and signing of fresh wallets require ≥2 parties — CGGMP21 and FROST cannot produce shares with a single party — so this mode exists only for boot-up smoke tests, e2e harness fixtures, and verifying TLS / membership / API plumbing. For real signing, run a multi-node cluster.

Networked (t-of-n)

# Node 0
mpcd start --node-id node0 --listen :9999 --api :9800 \
    --data /data/node0 --threshold 2 \
    --peer [email protected]:9999 --peer [email protected]:9999

# Node 1
mpcd start --node-id node1 --listen :9999 --api :9800 \
    --data /data/node1 --threshold 2 \
    --peer [email protected]:9999 --peer [email protected]:9999

# Node 2 — same pattern

The daemon expects the cluster size n = len(peers) + 1 and rejects start when threshold > n. See pkg/transport/registry.go::HasSigningQuorum for the quorum arithmetic.

Smoke test

go test -tags=integration ./cmd/mpcd/... -count=1 -timeout 90s

standalone_test.go builds mpcd, runs it with --threshold 1 on free ports and a fresh data directory, polls /healthz until quorum holds, and asserts the response shape. It does not exercise keygen — see e2e/ for that.