TON's wallet version is not a preference, it is FORCED. The account_id is a hash of the wallet contract's StateInit, so the same Ed25519 key yields a different address under v3R2, v4R2 and W5 — and luxfi/bridge's TON assembler is the only code in the estate that can SPEND TON. It builds v4R2 with subwallet 698983191. Emitting any other version would produce addresses that fund normally and that our own signer can never deploy a contract for: money in, unspendable. So this makes the identical call into the identical library rather than hand-rolling cell serialization. Non-bounceable (UQ…) because a bounceable message to a not-yet-deployed contract is returned to sender, and a deposit address is undeployed by definition until its first spend. Bridge makes the same choice. ⚠ SECOND LIVE WRONG-ADDRESS BUG, same shape as the TON/Solana one removed last week: handlers_wallets served addresses["xrp"] = *wallet.EVMAddress — a 20-byte hex EVM address labelled XRP. Both xrp and ton now derive from the stored public keys through pkg/address. Derived on READ rather than adding columns, so every wallet minted before today gets a correct address with no migration. Vectors are external on purpose. XRP matches three published XRPL fixtures (xrpl.js secp256k1 + ed25519, and the dev-portal address-encoding example). TON uses MAINNET as the oracle — library fixtures would be self-referential — by harvesting accounts whose code_hash is the v4R2 code hash and asking each contract for its own public key; one row is corroborated independently by @ton/ton's published spec address. The code cell is pinned to mainnet's hash and the data layout to seqno‖subwallet‖pubkey. Curve confusion fails closed both ways: TON refuses a secp256k1 key, XRP refuses an Ed25519 one, and TON refuses every torsion-order point Solana refuses. Known trap left alone deliberately: pubKeyToEVMAddress and pubKeyToBtcAddress still assume even-y for a 32-byte input, which is wrong for half of keys. Not reachable today because keygen serializes the 33-byte compressed point, but it is one refactor from being live. address.XRP refuses that input instead. Fixing the EVM/BTC paths is a higher-risk edit that deserves its own review.
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.