mirror of
https://github.com/luxfi/kms.git
synced 2026-07-27 05:54:18 +00:00
The KMS ZapClient and the mpcd KMS-facing ZAP server (luxfi/mpc
pkg/api/zap_kms_server.go) had drifted out of lockstep despite the
"MUST stay in lockstep" comment — each was only unit-tested against its
own stub, never against the other's real bytes. Three drifts:
1. Sign request: KMS sent {key_type, wallet_id, message}; mpcd requires
{vault_id, wallet_id, payload} and hard-fails "vault_id and wallet_id
required". SignRequest now carries VaultID (Manager supplies it from
MPC_VAULT_ID) and Payload (json "payload").
2. Keygen response: mpcd returns snake_case (wallet_id/ecdsa_pub_key/
evm_address); KMS KeygenResult was camelCase → decoded to an empty
struct. Tags realigned to snake_case (the ZAP path is the only live
decode; the REST Client is unused).
3. False-green: ZapClient.call() json-decoded the daemon's {"error":...}
frame into a zero-value SignResult and returned it with nil error —
the KMS looked MPC-backed but returned an empty signature. call() now
surfaces a non-empty top-level "error" as a real error (fail closed).
Adds pkg/mpc/wire_contract_test.go — a cross-repo contract test that
transcribes the mpcd request/response shapes and fails CI if the KMS
side drifts again (the guard that was missing). Existing tests updated
to the corrected snake_case wire.
This is a library fix in luxfi/kms — it fixes the wire for all three
orgs (Lux/Zoo/Hanzo) once. ed25519/Corona keygen over this wire remains
a follow-up (mpcd's KMS-ZAP keygen carries no key_type/protocol).
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
78 lines
2.3 KiB
Go
78 lines
2.3 KiB
Go
// Copyright (C) 2024-2026 Lux Industries Inc.
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package mpc
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// TestWallet_MarshalJSON_EmitsEVMAddress pins the canonical wire
|
|
// shape: only evmAddress, no legacy aliases.
|
|
func TestWallet_MarshalJSON_EmitsEVMAddress(t *testing.T) {
|
|
addr := "0xABCDef0123456789aBcdEf0123456789ABCDeF01"
|
|
w := Wallet{
|
|
ID: "w1",
|
|
KeyType: "ecdsa",
|
|
EVMAddress: &addr,
|
|
}
|
|
out, err := json.Marshal(&w)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
s := string(out)
|
|
if !strings.Contains(s, `"evmAddress":"`+addr+`"`) {
|
|
t.Fatalf("missing evmAddress in output: %s", s)
|
|
}
|
|
if strings.Contains(s, "keccakAddress") || strings.Contains(s, "ethAddress") {
|
|
t.Fatalf("legacy address alias leaked into output: %s", s)
|
|
}
|
|
}
|
|
|
|
// TestWallet_UnmarshalJSON_AcceptsEVMAddress pins the canonical wire
|
|
// shape on the read side.
|
|
func TestWallet_UnmarshalJSON_AcceptsEVMAddress(t *testing.T) {
|
|
addr := "0xABCDef0123456789aBcdEf0123456789ABCDeF01"
|
|
src := `{"id":"w1","keyType":"ecdsa","evmAddress":"` + addr + `"}`
|
|
var w Wallet
|
|
if err := json.Unmarshal([]byte(src), &w); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if w.EVMAddress == nil || *w.EVMAddress != addr {
|
|
t.Fatalf("EVMAddress not populated: got %+v", w.EVMAddress)
|
|
}
|
|
}
|
|
|
|
// TestKeygenResult_EVMAddress mirrors the Wallet contract.
|
|
func TestKeygenResult_EVMAddress(t *testing.T) {
|
|
addr := "0xdeadbeefcafebabedeadbeefcafebabedeadbeef"
|
|
|
|
// Marshal: only evmAddress emitted.
|
|
k := KeygenResult{ID: "k1", EVMAddress: &addr}
|
|
out, err := json.Marshal(&k)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
s := string(out)
|
|
// snake_case matches the mpcd ZAP keygen response (evm_address). camelCase
|
|
// was the wire drift that silently decoded to empty — see wire_contract_test.go.
|
|
if !strings.Contains(s, `"evm_address":"`+addr+`"`) {
|
|
t.Fatalf("missing evm_address: %s", s)
|
|
}
|
|
if strings.Contains(s, "keccakAddress") || strings.Contains(s, "ethAddress") || strings.Contains(s, "evmAddress") {
|
|
t.Fatalf("legacy alias leaked: %s", s)
|
|
}
|
|
|
|
// Unmarshal: evm_address populates EVMAddress.
|
|
src := `{"id":"k2","evm_address":"` + addr + `"}`
|
|
var k2 KeygenResult
|
|
if err := json.Unmarshal([]byte(src), &k2); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if k2.EVMAddress == nil || *k2.EVMAddress != addr {
|
|
t.Fatalf("EVMAddress not populated: %v", k2.EVMAddress)
|
|
}
|
|
}
|