build(mpc): GOEXPERIMENT=jsonv2 alongside runtimesecret per SCALE_STANDARD (#13)

* build(mpc): GOEXPERIMENT=jsonv2 alongside runtimesecret per SCALE_STANDARD

Per SCALE_STANDARD.md §2 (~/work/hanzo/hips/docs/SCALE_STANDARD.md) —
every Go production Dockerfile that emits JSON to a client builds with
GOEXPERIMENT=jsonv2. Verified on hanzoai/zip's json_bench_test.go:
- Edge POST roundtrip: -12% time, -23% allocs
- Marshal-only: -22% time
- Unmarshal-only: -19% time, -25% allocs

GOEXPERIMENT is comma-separated, so jsonv2 composes cleanly with the
pre-existing runtimesecret experiment used by the MPC data plane.

* ci: switch workflows to lux-build (luxfi-scoped ARC pool)

Cross-org runs-on dispatch failure: hanzo-build-linux-amd64 scale set is
registered to github.com/hanzoai and silently rejects luxfi/* jobs.
Switch to lux-build (githubConfigUrl=github.com/luxfi, max 20).

* test/zapauth: deterministic invalid-signature test (was flaky)

TestZapAuthInvalidSignature flipped one base64 char of the signature
segment. ECDSA accepts a wrong sig with probability ≈ 1/2^6 per
single-char flip (alphabet is 64), making the test ~1.5% flake rate
in CI.

Fix: replace the entire signature segment with all-A's. Still
decodable as base64url but cryptographically guaranteed-invalid
bytes — Verify rejection is now deterministic.

Verified: ran -count=10 ./pkg/zapauth/ — all pass.

* deps: bump accel/crypto/corona/threshold/fhe/lens/math to current tags

* api/db: decomplect — KeccakAddress on Wallet / TreasuryWallet / KeygenResult

Wave 5 of the workspace-wide eth* naming purge.

pkg/db/models.go — Wallet + TreasuryWallet:
- New canonical KeccakAddress *string field with json `keccakAddress`
- EthAddress kept with json `ethAddress` as `// Deprecated:` alias
- NormalizeAddresses() method on both structs mirrors the two fields
  via the package-private mirrorAddrPtrPair helper

pkg/api/server.go — KeygenResult (backend response):
- New canonical KeccakAddress string field with json `keccak_address`
- EthAddress kept with json `eth_address` as `// Deprecated:` alias
- NormalizeAddresses() method mirrors both fields

pkg/api/handlers_wallets.go, handlers_treasury.go, handlers_bridge.go,
handlers_mpc.go — all sites that constructed wallet rows from MPC
backend results now:
  1. Call result.NormalizeAddresses() on the backend response so both
     KeccakAddress / EthAddress carry the same value before copy
  2. Copy both fields onto the persisted Wallet / TreasuryWallet row
  3. Call wal.NormalizeAddresses() to ensure DB row has both
  4. Read from the canonical KeccakAddress at response-construction
     sites (toWalletResponse, treasury response, bridge response)

The "ethereum"/"lux"/"xrp" labels in addresses map (handleGetWalletAddresses)
are per-chain DISPLAY labels keyed by chain name a client knows;
the values are the Keccak-derived 20-byte address. Map keys NOT
decomplected here — they're per-chain UI labels, not internal naming.

Effect on the wire:
- API consumers reading wallet.ethAddress see the same value as
  wallet.keccakAddress
- New consumers can switch to keccakAddress today
- ORM persistence stores both fields (eventually consistent via
  NormalizeAddresses calls at handler boundaries)

Tests: pkg/api unit tests pass (1.8s); pkg/db has no test files.
Deprecation alias retention means downstream KMS / dashboards
keep working at every step of the migration.

Per CLAUDE.md x.x.x+1.

* strip KeccakAddress + EthAddress dual-field bridge (no aliases)

Forward-only completion of the workspace strip-aliases cascade. The
canonical JSON field is evmAddress, the Go field is EVMAddress. No
NormalizeAddresses helpers, no mirrorAddrPtrPair, no deprecated
KeccakAddress, no deprecated EthAddress.

Touched:
  pkg/api/server.go            KeygenResult drops KeccakAddress +
                               EthAddress + NormalizeAddresses
  pkg/db/models.go             Wallet drops KeccakAddress + EthAddress
                               TreasuryWallet drops KeccakAddress +
                               EthAddress
                               Drops NormalizeAddresses + mirrorAddrPtrPair
  pkg/api/handlers_wallets.go  rip mirror calls; only EVMAddress
                               populated on Wallet rows
  pkg/api/handlers_treasury.go rip mirror calls on TreasuryWallet
  pkg/api/handlers_mpc.go      rip NormalizeAddresses calls; rename
                               filter("ethAddress=") → ("evmAddress=")
  pkg/api/handlers_bridge.go   local var keccakAddress → evmAddr
  pkg/api/handlers_smart_wallets.go wallet.EthAddress → wallet.EVMAddress
  pkg/api/treasury_test.go     mock KeygenResult EthAddress → EVMAddress
  pkg/api/zap_kms_server_test.go same
  cmd/mpcd/main.go             pubKeyToEthAddress → pubKeyToEVMAddress,
                               \"eth_address\" → \"evm_address\",
                               local var ethAddr → evmAddr, KeygenResult
                               EthAddress → EVMAddress field

Deps: bump luxfi/metric v1.5.1 → v1.5.7 to pick up ExponentialBuckets
(via transitive luxfi/threshold dep).

The value IS \"EVM-runtime account address\" — that's the data model.
The Keccak256(secp256k1 pubkey) derivation is HOW.
This commit is contained in:
2026-05-29 21:05:40 -07:00
committed by GitHub
parent e6d97f63d1
commit b25d9422d0
18 changed files with 125 additions and 89 deletions
+1 -1
View File
@@ -294,7 +294,7 @@ jobs:
echo "Syft: $(jq '.artifacts | length' sbom.syft.json)"
build:
runs-on: hanzo-build-linux-amd64
runs-on: lux-build
needs: [test, lint, codeql-analysis, sbom]
env:
GOEXPERIMENT: runtimesecret
+3 -3
View File
@@ -17,7 +17,7 @@ jobs:
arch: [amd64, arm64]
# No GH-hosted arm runners. arm64 builds via Docker buildx with QEMU on
# the amd64 self-hosted runner (QEMU is acceptable for Docker builds).
runs-on: hanzo-build-linux-amd64
runs-on: lux-build
steps:
- uses: actions/checkout@v4
- name: Set up QEMU
@@ -43,7 +43,7 @@ jobs:
manifest:
needs: build
runs-on: hanzo-build-linux-amd64
runs-on: lux-build
steps:
- uses: docker/login-action@v3
with:
@@ -62,7 +62,7 @@ jobs:
notify:
needs: manifest
if: startsWith(github.ref, 'refs/tags/v')
runs-on: hanzo-build-linux-amd64
runs-on: lux-build
steps:
- uses: peter-evans/repository-dispatch@v3
with:
+2 -2
View File
@@ -15,7 +15,7 @@ jobs:
arch: [amd64, arm64]
# All builds run on the amd64 self-hosted runner. arm64 cross-compiles
# via GOARCH; CGO_ENABLED=0 keeps it pure-Go (no cross-compiler needed).
runs-on: hanzo-build-linux-amd64
runs-on: lux-build
env:
GONOSUMCHECK: '*'
GONOSUMDB: '*'
@@ -38,7 +38,7 @@ jobs:
release:
needs: build
runs-on: hanzo-build-linux-amd64
runs-on: lux-build
permissions:
contents: write
steps:
+6 -1
View File
@@ -31,7 +31,12 @@ RUN go mod download
COPY . .
COPY --from=ui /ui/dist ./ui/dist/
ENV GOEXPERIMENT=runtimesecret
# Per SCALE_STANDARD.md §2 (https://github.com/hanzoai/hips/blob/main/docs/SCALE_STANDARD.md)
# — every Go production Dockerfile that emits JSON to a client builds
# with GOEXPERIMENT=jsonv2 (composed with runtimesecret for the MPC
# data-plane secrecy guarantees). Verified -12% time / -23% allocs on
# the edge POST roundtrip vs encoding/json v1.
ENV GOEXPERIMENT=runtimesecret,jsonv2
RUN CGO_ENABLED=1 GOOS=linux go build -ldflags="-s -w -linkmode external -extldflags '-static'" -tags 'sqlite_omit_load_extension' -o mpcd ./cmd/mpcd
RUN CGO_ENABLED=1 GOOS=linux go build -ldflags="-s -w -linkmode external -extldflags '-static'" -tags 'sqlite_omit_load_extension' -o mpc ./cmd/mpc
+7 -7
View File
@@ -662,7 +662,7 @@ func runNodeConsensus(ctx context.Context, c *cli.Command) error {
resp["ecdsa_pub_key"] = hex.EncodeToString(result.ECDSAPubKey)
resp["eddsa_pub_key"] = hex.EncodeToString(result.EDDSAPubKey)
if len(result.ECDSAPubKey) >= 32 {
resp["eth_address"] = pubKeyToEthAddress(result.ECDSAPubKey)
resp["evm_address"] = pubKeyToEVMAddress(result.ECDSAPubKey)
resp["btc_address"] = pubKeyToBtcAddress(result.ECDSAPubKey)
}
if len(result.EDDSAPubKey) == 32 {
@@ -973,11 +973,11 @@ func (b *ConsensusMPCBackend) TriggerKeygen(orgID, walletID string) (*mpcapi.Key
if result.ResultType != event.ResultTypeSuccess {
return nil, fmt.Errorf("keygen failed: %s", result.ErrorReason)
}
ethAddr := ""
evmAddr := ""
btcAddr := ""
solAddr := ""
if len(result.ECDSAPubKey) >= 32 {
ethAddr = pubKeyToEthAddress(result.ECDSAPubKey)
evmAddr = pubKeyToEVMAddress(result.ECDSAPubKey)
btcAddr = pubKeyToBtcAddress(result.ECDSAPubKey)
}
if len(result.EDDSAPubKey) == 32 {
@@ -987,7 +987,7 @@ func (b *ConsensusMPCBackend) TriggerKeygen(orgID, walletID string) (*mpcapi.Key
WalletID: result.WalletID,
ECDSAPubKey: hex.EncodeToString(result.ECDSAPubKey),
EDDSAPubKey: hex.EncodeToString(result.EDDSAPubKey),
EthAddress: ethAddr,
EVMAddress: evmAddr,
BtcAddress: btcAddr,
SolAddress: solAddr,
}, nil
@@ -1412,9 +1412,9 @@ func (q *ConsensusMessageQueue) Close() {
// Nothing to close in consensus mode
}
// pubKeyToEthAddress derives an Ethereum address from an ECDSA public key.
// pubKeyToEVMAddress derives an Ethereum address from an ECDSA public key.
// Accepts compressed (33 bytes), uncompressed (65 bytes), or raw x-coordinate (32 bytes).
func pubKeyToEthAddress(pubKey []byte) string {
func pubKeyToEVMAddress(pubKey []byte) string {
var xyBytes []byte // 64 bytes: X(32) || Y(32)
switch len(pubKey) {
case 65:
@@ -1450,7 +1450,7 @@ func pubKeyToEthAddress(pubKey []byte) string {
// Try as hex string
decoded, err := hex.DecodeString(string(pubKey))
if err == nil && len(decoded) > 0 {
return pubKeyToEthAddress(decoded)
return pubKeyToEVMAddress(decoded)
}
return ""
}
+9 -9
View File
@@ -30,14 +30,14 @@ require (
github.com/hanzoai/dbx v1.16.0
github.com/hanzoai/orm v0.5.2
github.com/hashicorp/consul/api v1.33.7
github.com/luxfi/crypto v1.19.0
github.com/luxfi/crypto v1.19.10
github.com/luxfi/database v1.18.3
github.com/luxfi/fhe v1.7.7
github.com/luxfi/fhe v1.8.2
github.com/luxfi/hsm v1.1.3
github.com/luxfi/lattice/v7 v7.1.0
github.com/luxfi/log v1.4.1
github.com/luxfi/metric v1.5.1
github.com/luxfi/threshold v1.6.12
github.com/luxfi/metric v1.5.7
github.com/luxfi/threshold v1.8.2
github.com/luxfi/zap v0.2.0
github.com/minio/minio-go/v7 v7.0.100
github.com/mitchellh/mapstructure v1.5.0
@@ -69,7 +69,6 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260311194731-d5b7577c683d // indirect
github.com/agl/ed25519 v0.0.0-20200225211852-fd4d107ace12 // indirect
github.com/ansel1/merry v1.8.1 // indirect
github.com/ansel1/merry/v2 v2.2.2 // indirect
@@ -144,7 +143,7 @@ require (
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/klauspost/crc32 v1.3.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/luxfi/accel v1.0.7 // indirect
github.com/luxfi/accel v1.1.2 // indirect
github.com/luxfi/age v1.4.0 // indirect
github.com/luxfi/atomic v1.0.0 // indirect
github.com/luxfi/cache v1.2.1 // indirect
@@ -153,14 +152,15 @@ require (
github.com/luxfi/concurrent v0.0.3 // indirect
github.com/luxfi/constants v1.4.7 // indirect
github.com/luxfi/container v0.0.4 // indirect
github.com/luxfi/corona v0.4.0 // indirect
github.com/luxfi/corona v0.4.1 // indirect
github.com/luxfi/geth v1.16.79 // indirect
github.com/luxfi/ids v1.2.9 // indirect
github.com/luxfi/lens v0.1.3 // indirect
github.com/luxfi/math v1.4.0 // indirect
github.com/luxfi/lens v0.1.4 // indirect
github.com/luxfi/math v1.4.1 // indirect
github.com/luxfi/math/big v0.1.0 // indirect
github.com/luxfi/mdns v0.1.0 // indirect
github.com/luxfi/mock v0.1.1 // indirect
github.com/luxfi/pq v1.0.3 // indirect
github.com/luxfi/sampler v1.0.0 // indirect
github.com/luxfi/utils v1.1.4 // indirect
github.com/luxfi/zapdb v1.8.0 // indirect
+20 -18
View File
@@ -442,8 +442,6 @@ github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260311194731-d5b7577c683d h1:EA+kZ8mxGb1W/ewiIBMzb/1gg5BiW1Fvr3r4qCUBJEg=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260311194731-d5b7577c683d/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -879,8 +877,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/luxfi/accel v1.0.7 h1:ksHieAp50umwqxqgyHk9WiOmXM54kia3IEDb5H7FsM8=
github.com/luxfi/accel v1.0.7/go.mod h1:iZD3oxffiMEIT/KvzD8bgwC/cBn4AYlMW3QJpbRa4RE=
github.com/luxfi/accel v1.1.2 h1:V3vZOWTcOmR5mm7I27jRvXmpEPyb3oRf7lLnL6R+pFI=
github.com/luxfi/accel v1.1.2/go.mod h1:K00BcnLzEYMPHwCFq8Tf/450ApmTs9xBVvYOnofJCkc=
github.com/luxfi/age v1.4.0 h1:tU5q65RQSQdaVq64Z/DVUhiPQMoMPQT6pr41LsvG0AQ=
github.com/luxfi/age v1.4.0/go.mod h1:iAYAxgvrXxcy746+Ovh/eWWDuF9teJLNcCSSOX9RYW0=
github.com/luxfi/atomic v1.0.0 h1:xUV60MuzRvXngaQ1sM0yVC2v4TRoLlUGkkH7M9PS4yw=
@@ -897,14 +895,16 @@ github.com/luxfi/constants v1.4.7 h1:e/Qs+DQP3pugle3Zncq6fZCxKgqqtbyD/z7Gm4ZjsYg
github.com/luxfi/constants v1.4.7/go.mod h1:hOszZ2NDQ8gMZKncfcZ67PXkb5OIbnwAzXC3oFbQwW0=
github.com/luxfi/container v0.0.4 h1:BXhF82WyfqVP5mjlNcr7tP0Fcnvl0Ap1rkiu+rq5XuM=
github.com/luxfi/container v0.0.4/go.mod h1:Z3SpmMF5d4t77MM0nHYXURpn+EMVaeu1fhbd/3BGaek=
github.com/luxfi/corona v0.4.0 h1:vGD3bhT5I9vEA3XEReRu1sEMkRvUL3WosEcJgIR9eTc=
github.com/luxfi/corona v0.4.0/go.mod h1:4aD7+ZqnlZ2aVuU/DBQ5aspIagv5ux45LW2sJ4+siY8=
github.com/luxfi/crypto v1.19.0 h1:VtH6kvZrCEjCnkHPkhExDU+GJ0ZulYX4rnA+lXJdJHw=
github.com/luxfi/crypto v1.19.0/go.mod h1:ee525i8Recbpb0jVTDZYZBr1MmvJ27OITJHZ/nlNMBw=
github.com/luxfi/corona v0.4.1 h1:DGMdZMrENl6vmJf9CgiQMD8c5ORSTMymL0z9KgcZstk=
github.com/luxfi/corona v0.4.1/go.mod h1:4aD7+ZqnlZ2aVuU/DBQ5aspIagv5ux45LW2sJ4+siY8=
github.com/luxfi/crypto v1.19.10 h1:2tdnBafuysfTkMJyLdz6+W181YmJJO1vndOatKmr5JQ=
github.com/luxfi/crypto v1.19.10/go.mod h1:88cJ59mF/e199BYZK/p8UHmo4bSBPSVNOiHeY6AhMBw=
github.com/luxfi/database v1.18.3 h1:gg+xwhKUxXa7fDoOD8IS91E71QqoEtcDCl2nfS61Jgg=
github.com/luxfi/database v1.18.3/go.mod h1:sv0pYCGKlK1aNJTICxFUDpVWCJTigoLlshHmV/1pg7c=
github.com/luxfi/fhe v1.7.7 h1:RP26i3CxvNy2y/3LKiJPgd9uR5MrlG/MvQnv5rkQFhk=
github.com/luxfi/fhe v1.7.7/go.mod h1:FHfGNgKL5ZLcnKVKFVNLDJOBbQyQm5DGpVrMnY8ASJo=
github.com/luxfi/edwards25519 v0.1.0 h1:YPoT831TZMslvNyy/KuTtpHi4BGZWgpwak06RCaYLzo=
github.com/luxfi/edwards25519 v0.1.0/go.mod h1:MBoV+bPEz1tSMADPMSUaYdZ+agY3syG8B6z7RmJBH0A=
github.com/luxfi/fhe v1.8.2 h1:QllnObNFbi6D4mvFI6uQkepW8HgLtdy4RMR1TKYAInA=
github.com/luxfi/fhe v1.8.2/go.mod h1:16yxwhcnCez/rNcd/C9JjH9IjbEz73X+0tvlsONyLeA=
github.com/luxfi/geth v1.16.79 h1:MtP8ZUuSVZDjmZDa1kTJCl0PpG8+wGbQft652GO6a3A=
github.com/luxfi/geth v1.16.79/go.mod h1:6xNi4sHoh0v1kBf0TOjmFhBg5+4umbSqrwiFZsDanZ0=
github.com/luxfi/hsm v1.1.3 h1:ke36qmz5zqapBrnN3ij7gU9fFPaPBrnzcdrKX4lV/rk=
@@ -913,24 +913,28 @@ github.com/luxfi/ids v1.2.9 h1:+yjdhXW99drnd2Zlp1u/p8k3G23W3/1btJQ4ogHawUI=
github.com/luxfi/ids v1.2.9/go.mod h1:khJOEdOPxd22yn0jcVrnbX1ADa0GHn5Y74gvCzN5BYc=
github.com/luxfi/lattice/v7 v7.1.0 h1:mr3HvN6olNTS2LT/xAW/JBhTqfvpsGmsopDMeR7BSJs=
github.com/luxfi/lattice/v7 v7.1.0/go.mod h1:IaaUN+3ysnBG4BA8ILRYG0j80+qtYDP4C5lkaDb2pDE=
github.com/luxfi/lens v0.1.3 h1:JTk/AjhHdKaiJ6Z7ZNazqnRfbgRijUPIxHamKW7b/N8=
github.com/luxfi/lens v0.1.3/go.mod h1:XHpPOSgkT0ZFwfwp0t70Tvs5b6rjGyo1SNDEIDtfrqw=
github.com/luxfi/lens v0.1.4 h1:goGjGDXx2BNdjzXDunL5QT8elK2ZyCcc0z8TAbtWYrg=
github.com/luxfi/lens v0.1.4/go.mod h1:mL+G8IK+9L41d78/2FYRgfhEzAjcr5+VEXB8SGuHbus=
github.com/luxfi/log v1.4.1 h1:rIfFRodb9jrD/w7KayaUk0Oc+37PaQQdKEEMJCjR8gw=
github.com/luxfi/log v1.4.1/go.mod h1:64IE3xRMJcpkQwnPUfJw3pDj7wU0kRS7BZ9wM7R72jk=
github.com/luxfi/math v1.4.0 h1:/sb7Grw3hfO+5INWAWdB95jTvCeXg8fSQxsxDzcFtd4=
github.com/luxfi/math v1.4.0/go.mod h1:iW0FOCC8qF2mPE+MakG780CAHA83848lb1L04thA1Pg=
github.com/luxfi/math v1.4.1 h1:1t9bCCsEqnl9yIKrShlbs80DBKyYTWdnzkVfBqEeO7Q=
github.com/luxfi/math v1.4.1/go.mod h1:QvbRxauQyE1w4lvbcLSe6c8yeJz2Zj1Bq1rayGgs2tA=
github.com/luxfi/math/big v0.1.0 h1:Vz4c0RsZVPdIKPsHPgAJChH/R3p15WHRUz7LkLf+NIQ=
github.com/luxfi/math/big v0.1.0/go.mod h1:BuxSu22RbO93xBLk5Eam5nldFponoJ73xDFz4uJ3Huk=
github.com/luxfi/mdns v0.1.0 h1:VB3mQcETc9j5SY1S6lAgFtuGr/rjWuDgPYnxS+OKWMQ=
github.com/luxfi/mdns v0.1.0/go.mod h1:/3dheKVjUk2yiS/ocH1IDzeLXOIe+kpVsErIGDOZdiQ=
github.com/luxfi/metric v1.5.1 h1:6tVarXMnNR3Xzud8FYUHjtXabTll3HI/OEqG0tgLAcI=
github.com/luxfi/metric v1.5.1/go.mod h1:PkD4D4JoGuyKtfUkqPNYkrg9xKrJeNVZFdW/5XvAe/A=
github.com/luxfi/metric v1.5.7 h1:LoSPEUpak2SLcynF+LT2cXjl9ECp4nY+Lia9zudmDv4=
github.com/luxfi/metric v1.5.7/go.mod h1:CMguEhyuLi4YUWyXimJ+UHply99BDFrL0pxedB7rBqM=
github.com/luxfi/mock v0.1.1 h1:0HEtIjg1J6CWz+IUyP6rsGqNWTcmxjFnSQIhaDuARwY=
github.com/luxfi/mock v0.1.1/go.mod h1:jo35akl3Vtd8LbzDts8VJ0jmSVycrd1/eBi6g6t5hKU=
github.com/luxfi/pq v1.0.3 h1:pFlQm1+5FuKTDUh2y/23bXWkN4I2Rc5iuxJypwDFFMs=
github.com/luxfi/pq v1.0.3/go.mod h1:8bppZcRElfrVt0n3nYCZW3iX1TvhvzNbdjNdK1irgIE=
github.com/luxfi/sampler v1.0.0 h1:k8Sf6otW83w4pQp0jXLA+g3J/joB7w7SqXQsWmNTOV0=
github.com/luxfi/sampler v1.0.0/go.mod h1:f96/ozlj9vFfZj+akLtrHn4VpulQahwB+MQQhpeIekk=
github.com/luxfi/threshold v1.6.12 h1:07PKnw9V1ue1aORMuq4ZsG0kw4eWZPLemog7TUsOW+Q=
github.com/luxfi/threshold v1.6.12/go.mod h1:WcqGG/9f3ciLcxIAX3thlgVVRsr6cvCiPq/OLYe8+ts=
github.com/luxfi/threshold v1.8.2 h1:GxkOm4R8BWTKyVn0teo8hLii5zuif/YrR06VcIfenGU=
github.com/luxfi/threshold v1.8.2/go.mod h1:RRT64ObcAyD7JweqVnGDVzmu6KeKGadFV3WiJaxvX+Y=
github.com/luxfi/utils v1.1.4 h1:8OY0jXCkpp6OotN1Y++6DATJkvtEwzq2k0p56imJlAk=
github.com/luxfi/utils v1.1.4/go.mod h1:c3yz1RjzrB+cs5GZm+q1T3/2cCKElO9vxm9yRRtgSEM=
github.com/luxfi/zap v0.2.0 h1:RzvOkp3EoN5UCkpnqfObHLM1sEHy7YcxXKuermhE/VA=
@@ -1827,5 +1831,3 @@ modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
github.com/luxfi/edwards25519 v0.1.0 h1:YPoT831TZMslvNyy/KuTtpHi4BGZWgpwak06RCaYLzo=
github.com/luxfi/edwards25519 v0.1.0/go.mod h1:MBoV+bPEz1tSMADPMSUaYdZ+agY3syG8B6z7RmJBH0A=
+10
View File
@@ -160,6 +160,7 @@ github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251230134950-44c893854e3f/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260215031811-a0ab0b218a81/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260311194731-d5b7577c683d/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
github.com/Sereal/Sereal/Go/sereal v0.0.0-20231009093132-b9187f1a92c6/go.mod h1:JwrycNnC8+sZPDyzM3MQ86LvaGzSpfxg885KOOwFRW4=
@@ -374,6 +375,7 @@ github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI=
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
github.com/google/renameio/v2 v2.0.1/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
@@ -393,6 +395,7 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4/go.mod h1:6Nz966r3vQYCqIzWsuEl
github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s=
github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o=
github.com/guptarohit/asciigraph v0.5.5/go.mod h1:dYl5wwK4gNsnFf9Zp+l06rFiDZ5YtXM6x7SRWZ3KGag=
github.com/hanzoai/dbx v1.13.0/go.mod h1:WIRkg1f2Mc+vXo2gpWk+hgPsJPFZBNUFCkSFJ4M5s4k=
github.com/hanzoai/kms/sdk/go v1.0.0/go.mod h1:kPPvEuE3K+fcOY+S6hiNO8nffxDNChvC1Uc4fR5Y+xk=
github.com/hanzoai/tygoja v0.0.2/go.mod h1:o+poSPLJ5S3SMtdz7gJHaL/6P6tjGBZcfQMl0SUIBr4=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
@@ -475,6 +478,8 @@ github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNa
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/luxfi/accel v1.0.0/go.mod h1:KLcUuJlaO5/knBTKZ2YW1NCHDSIHOz9N++0x854v/9Y=
github.com/luxfi/accel v1.0.1/go.mod h1:j5NCg8eKiQwhbRuJqa5G1oozcKp8HxJWMnrNA/CocWk=
github.com/luxfi/accel v1.0.7/go.mod h1:iZD3oxffiMEIT/KvzD8bgwC/cBn4AYlMW3QJpbRa4RE=
github.com/luxfi/accel v1.1.0/go.mod h1:K00BcnLzEYMPHwCFq8Tf/450ApmTs9xBVvYOnofJCkc=
github.com/luxfi/address v1.0.1/go.mod h1:5j3Eh66v9zvv1GbNdZwt+23krV8JlSDaRzmWZU8ZRM0=
github.com/luxfi/ai v0.0.0-20251225021023-3f15131f2bd1/go.mod h1:4+FVvnCo1QqM3NaVUte0da4zgH2IblVLmUxKQi0BwE0=
github.com/luxfi/api v1.0.1/go.mod h1:N2Ymyqo3SoB6/DmPXRV6W377k2qNqZujSP/B8fbIB9U=
@@ -504,6 +509,7 @@ github.com/luxfi/crypto v1.17.38/go.mod h1:G2t1GQvPsrwnzwyVEj0LQDuX2AWZVI5kEAPyV
github.com/luxfi/crypto v1.17.39/go.mod h1:mChLWmW4CLR1wAN6CeJTveCzUv0DTzGQnYgq01x3W0U=
github.com/luxfi/crypto v1.17.43/go.mod h1:1M6uQ8I/7dzQwf5RdnKtqOWr0+iXRW0D9WfnMV+Cb1s=
github.com/luxfi/crypto v1.17.45/go.mod h1:GnAkhQ7HNs3X0Tzx5nOONS3kl0yRmWHbDcRO5ffILsg=
github.com/luxfi/crypto v1.19.0/go.mod h1:ee525i8Recbpb0jVTDZYZBr1MmvJ27OITJHZ/nlNMBw=
github.com/luxfi/crypto/ipa v1.2.4/go.mod h1:43J6f6rcfUMrZt4cQectMOZb6Ps+fAEj8ZTPC3Kk+gE=
github.com/luxfi/czmq/v4 v4.2.2/go.mod h1:WFY9ldUbeU900P8bcIRr7Jim23veOhJL5A5zSpTogdU=
github.com/luxfi/database v1.17.35/go.mod h1:dYxaF30KGJQzuxA3VH7+WGU5/dhwRyzLgzSk9F3wje0=
@@ -537,6 +543,7 @@ github.com/luxfi/log v1.2.1/go.mod h1:0vJzb4Hl9fvgwWDMKcbCD/SYH3bO0iSmgZMcYdfdl1
github.com/luxfi/log v1.4.0/go.mod h1:64IE3xRMJcpkQwnPUfJw3pDj7wU0kRS7BZ9wM7R72jk=
github.com/luxfi/lpm v1.0.5/go.mod h1:dS/NS6muB9zlHWbO/VeCMYCe53YwIjXDHg/jif7TejI=
github.com/luxfi/math v1.2.2/go.mod h1:C8STnF2H+D6rqBPt248CiWY2TGuJgdtv/+4UqrT15iM=
github.com/luxfi/math v1.4.0/go.mod h1:iW0FOCC8qF2mPE+MakG780CAHA83848lb1L04thA1Pg=
github.com/luxfi/math/safe v0.0.1/go.mod h1:EejrmOJHh03YAD8+Zww8cPcMR1K3Q2I7w1dX4sMloeo=
github.com/luxfi/metric v1.4.9/go.mod h1:JlAAc4Jm5apoU2DHEgt57UHD7+8AjMdpP2rQJ/zaACY=
github.com/luxfi/metric v1.4.10/go.mod h1:3OIQ+e6mciesm3cik3Q4CSOAvTlBy4JA0ZoKzCB9APo=
@@ -1016,5 +1023,8 @@ gopkg.in/vmihailenco/msgpack.v2 v2.9.2/go.mod h1:/3Dn1Npt9+MYyLpYYXjInO/5jvMLamn
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
lukechampine.com/blake3 v1.4.0/go.mod h1:MQJNQCTnR+kwOP/JEZSxj3MaQjp80FOFSNMMHXcSeX0=
modernc.org/libc v1.70.0/go.mod h1:OVmxFGP1CI/Z4L3E0Q3Mf1PDE0BucwMkcXjjLntvHJo=
modernc.org/sqlite v1.48.0/go.mod h1:hWjRO6Tj/5Ik8ieqxQybiEOUXy0NJFNp2tpvVpKlvig=
modernc.org/sqlite v1.48.1/go.mod h1:hWjRO6Tj/5Ik8ieqxQybiEOUXy0NJFNp2tpvVpKlvig=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA=
+4 -4
View File
@@ -98,9 +98,9 @@ func (s *Server) handleBridgeSign(w http.ResponseWriter, r *http.Request) {
}
mpcWalletID := bridgeWallet.WalletID
ethAddress := ""
if bridgeWallet.EthAddress != nil {
ethAddress = *bridgeWallet.EthAddress
evmAddr := ""
if bridgeWallet.EVMAddress != nil {
evmAddr = *bridgeWallet.EVMAddress
}
// Hash the transaction ID (keccak256)
@@ -150,7 +150,7 @@ func (s *Server) handleBridgeSign(w http.ResponseWriter, r *http.Request) {
Data: &bridgeSignData{
FromTokenAddress: req.ToTokenAddress,
Signature: sig,
MPCSigner: ethAddress,
MPCSigner: evmAddr,
HashedTxID: hashedTxID,
ToTokenAddrHash: toTokenAddrHash,
Vault: false,
+11 -11
View File
@@ -85,8 +85,8 @@ type walletResponse struct {
func toWalletResponse(w *db.Wallet, isDefault bool) walletResponse {
addr := ""
if w.EthAddress != nil {
addr = *w.EthAddress
if w.EVMAddress != nil {
addr = *w.EVMAddress
} else if w.BtcAddress != nil {
addr = *w.BtcAddress
} else if w.SolAddress != nil {
@@ -178,7 +178,7 @@ func (s *Server) handleMpcCreateWallet(w http.ResponseWriter, r *http.Request) {
wal.Protocol = req.Protocol
wal.ECDSAPubkey = nilIfEmpty(result.ECDSAPubKey)
wal.EDDSAPubkey = nilIfEmpty(result.EDDSAPubKey)
wal.EthAddress = nilIfEmpty(result.EthAddress)
wal.EVMAddress = nilIfEmpty(result.EVMAddress)
wal.BtcAddress = nilIfEmpty(result.BtcAddress)
wal.SolAddress = nilIfEmpty(result.SolAddress)
if status != nil {
@@ -372,8 +372,8 @@ func (s *Server) handleMpcWalletBalances(w http.ResponseWriter, r *http.Request)
items := make([]map[string]interface{}, 0, len(wallets))
for _, wal := range wallets {
addr := ""
if wal.EthAddress != nil {
addr = *wal.EthAddress
if wal.EVMAddress != nil {
addr = *wal.EVMAddress
}
items = append(items, map[string]interface{}{
"walletId": wal.Id(),
@@ -395,11 +395,11 @@ func (s *Server) handleMpcBalancesByAddress(w http.ResponseWriter, r *http.Reque
return
}
// Try ETH, BTC, SOL address fields in turn. Any match scoped to caller's
// org resolves the address; otherwise 404 (never leak "address unknown
// globally" vs "address not in your org").
// Try EVM, BTC, SOL address fields in turn. Any match scoped to
// caller's org resolves the address; otherwise 404 (never leak
// "address unknown globally" vs "address not in your org").
q := orm.TypedQuery[db.Wallet](s.db.ORM).Filter("orgId=", orgID).Limit(1)
wallets, _ := q.Filter("ethAddress=", addr).GetAll(r.Context())
wallets, _ := q.Filter("evmAddress=", addr).GetAll(r.Context())
if len(wallets) == 0 {
wallets, _ = orm.TypedQuery[db.Wallet](s.db.ORM).
Filter("orgId=", orgID).Filter("btcAddress=", addr).Limit(1).GetAll(r.Context())
@@ -450,8 +450,8 @@ func (s *Server) handleMpcCryptoWallet(w http.ResponseWriter, r *http.Request) {
}
network = "solana"
default:
if wal.EthAddress != nil {
addr = *wal.EthAddress
if wal.EVMAddress != nil {
addr = *wal.EVMAddress
}
network = "ethereum"
}
+2 -2
View File
@@ -40,8 +40,8 @@ func (s *Server) handleDeploySmartWallet(w http.ResponseWriter, r *http.Request)
return
}
mpcEOA := ""
if wallet.EthAddress != nil {
mpcEOA = *wallet.EthAddress
if wallet.EVMAddress != nil {
mpcEOA = *wallet.EVMAddress
}
if mpcEOA == "" {
writeError(w, http.StatusBadRequest, "wallet has no Ethereum address (keygen not complete)")
+3 -3
View File
@@ -177,7 +177,7 @@ func (s *Server) handleCreateTreasuryWallet(w http.ResponseWriter, r *http.Reque
tw.OrgID = orgID
tw.Name = req.Name
tw.WalletID = result.WalletID
tw.EthAddress = nilIfEmpty(result.EthAddress)
tw.EVMAddress = nilIfEmpty(result.EVMAddress)
tw.Chain = req.Chain
tw.Signers = signers
tw.Tiers = tiers
@@ -484,8 +484,8 @@ func toTreasuryWalletResponse(tw *db.TreasuryWallet, outstanding []outstandingOp
})
}
addr := ""
if tw.EthAddress != nil {
addr = *tw.EthAddress
if tw.EVMAddress != nil {
addr = *tw.EVMAddress
}
return treasuryWalletResponse{
WalletID: tw.Id(),
+10 -5
View File
@@ -96,7 +96,7 @@ func (s *Server) handleCreateWallet(w http.ResponseWriter, r *http.Request) {
wal.Protocol = req.Protocol
wal.ECDSAPubkey = nilIfEmpty(result.ECDSAPubKey)
wal.EDDSAPubkey = nilIfEmpty(result.EDDSAPubKey)
wal.EthAddress = nilIfEmpty(result.EthAddress)
wal.EVMAddress = nilIfEmpty(result.EVMAddress)
wal.BtcAddress = nilIfEmpty(result.BtcAddress)
wal.SolAddress = nilIfEmpty(result.SolAddress)
wal.Threshold = status.Threshold
@@ -137,10 +137,15 @@ func (s *Server) handleGetWalletAddresses(w http.ResponseWriter, r *http.Request
}
addresses := map[string]interface{}{}
if wallet.EthAddress != nil {
addresses["ethereum"] = *wallet.EthAddress
addresses["lux"] = *wallet.EthAddress // EVM-compatible
addresses["xrp"] = *wallet.EthAddress // secp256k1-derived (simplified)
if wallet.EVMAddress != nil {
// "ethereum"/"lux"/"xrp" are user-facing display labels keyed by
// chain name; the value is the 20-byte EVM-runtime account
// address (the form consumed by every EVM-compatible chain plus
// xrp's secp256k1-derived form). Map labels are display names,
// not internal naming.
addresses["ethereum"] = *wallet.EVMAddress
addresses["lux"] = *wallet.EVMAddress // EVM-compatible
addresses["xrp"] = *wallet.EVMAddress // secp256k1-derived (simplified)
}
if wallet.BtcAddress != nil {
addresses["bitcoin"] = *wallet.BtcAddress
+7 -1
View File
@@ -30,11 +30,17 @@ type MPCBackend interface {
GetClusterStatus() *ClusterStatus
}
// KeygenResult is the backend response from a keygen operation.
//
// EVMAddress is the canonical 20-byte EVM-runtime account address —
// the format consumed by every EVM-compatible chain. The derivation
// hashes the secp256k1 pubkey with Keccak256 — that's HOW. The value
// IS "EVM-runtime account address" — that's WHAT.
type KeygenResult struct {
WalletID string `json:"wallet_id"`
ECDSAPubKey string `json:"ecdsa_pub_key"`
EDDSAPubKey string `json:"eddsa_pub_key"`
EthAddress string `json:"eth_address"`
EVMAddress string `json:"evm_address,omitempty"`
BtcAddress string `json:"btc_address,omitempty"`
SolAddress string `json:"sol_address,omitempty"`
}
+1 -1
View File
@@ -43,7 +43,7 @@ func newTreasuryServer(t *testing.T) *Server {
t.Cleanup(func() { d.Close() })
return &Server{
db: d,
mpc: &mockMPCBackend{keygenResult: &KeygenResult{WalletID: "wallet-t-1", EthAddress: "0xabc"}},
mpc: &mockMPCBackend{keygenResult: &KeygenResult{WalletID: "wallet-t-1", EVMAddress: "0xabc"}},
}
}
+1 -1
View File
@@ -33,7 +33,7 @@ func (s *stubBackend) TriggerKeygen(orgID, walletID string) (*KeygenResult, erro
WalletID: walletID,
ECDSAPubKey: "0202020202",
EDDSAPubKey: "0303030303",
EthAddress: "0x0000000000000000000000000000000000000001",
EVMAddress: "0x0000000000000000000000000000000000000001",
}, nil
}
func (s *stubBackend) TriggerSign(orgID, walletID string, payload []byte) (*SignResult, error) {
+21 -12
View File
@@ -54,6 +54,11 @@ type Vault struct {
func init() { orm.Register[Vault]("vault") }
// Wallet holds an MPC key share group.
//
// EVMAddress is the canonical 20-byte EVM-runtime account address —
// the format consumed by every EVM-compatible chain. The derivation
// hashes the secp256k1 pubkey with Keccak256 — that's HOW. The value
// IS "EVM-runtime account address" — that's WHAT.
type Wallet struct {
orm.Model[Wallet]
VaultID string `json:"vaultId"`
@@ -64,7 +69,7 @@ type Wallet struct {
Protocol string `json:"protocol,omitempty"` // cggmp21, frost, lss
ECDSAPubkey *string `json:"ecdsaPubkey,omitempty"`
EDDSAPubkey *string `json:"eddsaPubkey,omitempty"`
EthAddress *string `json:"ethAddress,omitempty"`
EVMAddress *string `json:"evmAddress,omitempty"`
BtcAddress *string `json:"btcAddress,omitempty"`
SolAddress *string `json:"solAddress,omitempty"`
Threshold int `json:"threshold"`
@@ -569,19 +574,23 @@ type TreasuryTier struct {
}
// TreasuryWallet is an org-scoped 3-of-5 governance MPC wallet.
//
// EVMAddress is the canonical 20-byte EVM-runtime account address —
// the format consumed by every EVM-compatible chain. See Wallet
// docstring for the naming rationale.
type TreasuryWallet struct {
orm.Model[TreasuryWallet]
OrgID string `json:"orgId"`
Name string `json:"name"`
WalletID string `json:"walletId"` // underlying MPC wallet id
EthAddress *string `json:"ethAddress,omitempty"`
Chain string `json:"chain"` // "evm" default
Signers []TreasurySigner `json:"signers"`
Tiers []TreasuryTier `json:"tiers"` // sorted by threshold ascending on create
RegulatorShard bool `json:"regulatorShard"`
PolicyID string `json:"policyId,omitempty"` // backing Policy row with kind="treasury"
Status string `json:"status"` // active, frozen, archived
CreatedBy *string `json:"createdBy,omitempty"`
OrgID string `json:"orgId"`
Name string `json:"name"`
WalletID string `json:"walletId"` // underlying MPC wallet id
EVMAddress *string `json:"evmAddress,omitempty"`
Chain string `json:"chain"` // "evm" default
Signers []TreasurySigner `json:"signers"`
Tiers []TreasuryTier `json:"tiers"` // sorted by threshold ascending on create
RegulatorShard bool `json:"regulatorShard"`
PolicyID string `json:"policyId,omitempty"` // backing Policy row with kind="treasury"
Status string `json:"status"` // active, frozen, archived
CreatedBy *string `json:"createdBy,omitempty"`
}
func init() { orm.Register[TreasuryWallet]("treasury-wallet") }
+7 -8
View File
@@ -137,15 +137,14 @@ func TestZapAuthInvalidSignature(t *testing.T) {
"sub": "kms-pod-1",
"exp": float64(time.Now().Add(time.Hour).Unix()),
})
// Flip the last char of the signature segment so the ECDSA verify
// fails. Stay within base64 alphabet to keep parts decodable.
// Replace the entire signature segment with all-A's (decodable
// base64url but cryptographically guaranteed-invalid bytes). A
// single-char flip can be probabilistically valid for ECDSA ≈
// 1/2^6, which made this test flake in CI. Wiping the whole sig
// makes the verify failure deterministic.
parts := strings.Split(tok, ".")
last := parts[2]
flipped := last[:len(last)-1] + "a"
if flipped == last {
flipped = last[:len(last)-1] + "b"
}
bad := parts[0] + "." + parts[1] + "." + flipped
badSig := strings.Repeat("A", len(parts[2]))
bad := parts[0] + "." + parts[1] + "." + badSig
if _, err := v.Verify(context.Background(), bad); err == nil {
t.Fatalf("Verify: expected error on tampered signature")
}