fix: gofmt -s across repo (CI format check)

This commit is contained in:
2026-06-02 11:39:20 -07:00
parent 94be305ce9
commit da277ae96a
50 changed files with 392 additions and 392 deletions
+1 -1
View File
@@ -11,8 +11,8 @@ import (
"testing"
"time"
"github.com/google/go-sev-guest/verify/trust"
test "github.com/google/go-sev-guest/testing"
"github.com/google/go-sev-guest/verify/trust"
)
// sevSnpAttestationMilan is a real AMD Milan SEV-SNP attestation report
+2 -2
View File
@@ -139,8 +139,8 @@ func (r *ComplianceRejection) Error() string { return "mpc: " + r.Reason }
// SignResult is the immediate response.
type SignResult struct {
SessionID string
Signature []byte
SessionID string
Signature []byte
// Status: pending | signed.
Status string
ApprovalID string
+6 -6
View File
@@ -98,12 +98,12 @@ func airgapFIPSCheckCommand() *cli.Command {
}
provider := strings.ToLower(strings.TrimSpace(c.Args().Get(0)))
validated := map[string]string{
"aws": "AWS KMS / CloudHSM, CMVP cert #4523 / #3380",
"gcp": "Google Cloud HSM (Marvell LiquidSecurity), CMVP cert #4399",
"azure": "Azure Key Vault Premium / Managed HSM, CMVP cert #4399 / #4153",
"yubihsm": "YubiHSM 2 (FIPS firmware), CMVP cert #4148",
"pkcs11": "validation depends on configured vendor library — verify CMVP listing",
"kmip": "validation depends on configured KMS server — verify CMVP listing",
"aws": "AWS KMS / CloudHSM, CMVP cert #4523 / #3380",
"gcp": "Google Cloud HSM (Marvell LiquidSecurity), CMVP cert #4399",
"azure": "Azure Key Vault Premium / Managed HSM, CMVP cert #4399 / #4153",
"yubihsm": "YubiHSM 2 (FIPS firmware), CMVP cert #4148",
"pkcs11": "validation depends on configured vendor library — verify CMVP listing",
"kmip": "validation depends on configured KMS server — verify CMVP listing",
}
if cert, ok := validated[provider]; ok {
fmt.Printf("OK: %s — %s\n", provider, cert)
+2 -3
View File
@@ -39,7 +39,6 @@ import (
mpcapi "github.com/luxfi/mpc/pkg/api"
"github.com/luxfi/mpc/pkg/backup"
"github.com/luxfi/mpc/pkg/zapauth"
"github.com/luxfi/mpc/pkg/db"
"github.com/luxfi/mpc/pkg/event"
"github.com/luxfi/mpc/pkg/eventconsumer"
@@ -50,6 +49,7 @@ import (
"github.com/luxfi/mpc/pkg/mpc"
"github.com/luxfi/mpc/pkg/transport"
"github.com/luxfi/mpc/pkg/types"
"github.com/luxfi/mpc/pkg/zapauth"
)
const (
@@ -540,7 +540,7 @@ func runNodeConsensus(ctx context.Context, c *cli.Command) error {
// threshold = minimum signers required (operator surface).
// Present alongside ready_count so an operator can see how
// close to losing quorum the ensemble is.
"version": Version,
"version": Version,
}
json.NewEncoder(w).Encode(resp)
}
@@ -1566,4 +1566,3 @@ func isThresholdLoopback(addr string) bool {
}
return ip.IsLoopback()
}
+5 -5
View File
@@ -13,11 +13,11 @@
// PoA membership (n=1), HTTP control plane, and graceful shutdown.
//
// What this test covers:
// 1. Build mpcd
// 2. Start it with --node-id=solo --threshold=1 and no peers
// 3. Wait for /healthz to report status=healthy + quorum=true
// 4. Confirm the readyCount, threshold, and version fields
// 5. Send SIGTERM and confirm clean exit
// 1. Build mpcd
// 2. Start it with --node-id=solo --threshold=1 and no peers
// 3. Wait for /healthz to report status=healthy + quorum=true
// 4. Confirm the readyCount, threshold, and version fields
// 5. Send SIGTERM and confirm clean exit
//
// Keygen/signing are NOT exercised here. CGGMP21 and FROST require ≥2
// parties to produce a signature share — a 1-of-1 ensemble is structurally
+14 -10
View File
@@ -56,21 +56,23 @@ type nodeCtx struct {
pubKey ed25519.PublicKey
privKey ed25519.PrivateKey
gate *mpc.SignGate
sink interface{ All() []intent.NodeAttestation } // memAttestationSink
sink interface {
All() []intent.NodeAttestation
} // memAttestationSink
}
// clusterRig is the test fixture: 3 MPC nodes, each with a copy of the
// same policy bundle + approval keyset, plus 3 executive approval keys.
type clusterRig struct {
nodes []*nodeCtx
signers []signerKey
bundle *policy.PolicyBundle
nodes []*nodeCtx
signers []signerKey
bundle *policy.PolicyBundle
}
type signerKey struct {
id string
pub ed25519.PublicKey
priv ed25519.PrivateKey
id string
pub ed25519.PublicKey
priv ed25519.PrivateKey
}
func mkSigner(t *testing.T, id string) signerKey {
@@ -411,9 +413,11 @@ func TestPolicyGate_FailureModesAreOrthogonal(t *testing.T) {
want: policy.ErrPolicyNotFound,
},
{
name: "expired",
mutate: func(_ *clusterRig, ci *intent.CanonicalIntent) { ci.ExpiresAt = time.Date(2026, 4, 27, 11, 0, 0, 0, time.UTC) },
want: policy.ErrExpiredIntent,
name: "expired",
mutate: func(_ *clusterRig, ci *intent.CanonicalIntent) {
ci.ExpiresAt = time.Date(2026, 4, 27, 11, 0, 0, 0, time.UTC)
},
want: policy.ErrExpiredIntent,
},
{
name: "destination off allowlist",
+2 -2
View File
@@ -72,7 +72,7 @@ func (s *Server) validateJWT(tokenStr string) (*JWTClaims, error) {
if claims.Issuer != "mpc.lux.network" {
return nil, fmt.Errorf("invalid token issuer")
}
if !slices.Contains([]string(claims.Audience),"mpc-api") {
if !slices.Contains([]string(claims.Audience), "mpc-api") {
return nil, fmt.Errorf("invalid token audience")
}
return claims, nil
@@ -95,7 +95,7 @@ func (s *Server) validateRefreshToken(tokenStr string) (*JWTClaims, error) {
if claims.Issuer != "mpc.lux.network" {
return nil, fmt.Errorf("invalid token issuer")
}
if !slices.Contains([]string(claims.Audience),"mpc-refresh") {
if !slices.Contains([]string(claims.Audience), "mpc-refresh") {
return nil, fmt.Errorf("invalid token audience")
}
return claims, nil
+7 -7
View File
@@ -38,10 +38,10 @@ type approvalSession struct {
CreatedAt time.Time
ExpiresAt time.Time
mu sync.Mutex
sigs []approval.ApprovalSignature
completed bool
rejected bool
mu sync.Mutex
sigs []approval.ApprovalSignature
completed bool
rejected bool
rejectReason string
}
@@ -393,9 +393,9 @@ func (s *Server) handleApprovalCast(w http.ResponseWriter, r *http.Request) {
}
writeJSON(w, http.StatusOK, map[string]interface{}{
"status": ifElseString(sess.completed, "approved", "pending"),
"collected": len(sess.sigs),
"threshold": sess.Threshold,
"status": ifElseString(sess.completed, "approved", "pending"),
"collected": len(sess.sigs),
"threshold": sess.Threshold,
})
}
+16 -16
View File
@@ -16,16 +16,16 @@ import (
// ValidatorKeySet holds MPC wallet references for a validator's BLS and Corona keys.
type ValidatorKeySet struct {
ValidatorID string `json:"validatorId"`
BLSWalletID string `json:"blsWalletId"`
ValidatorID string `json:"validatorId"`
BLSWalletID string `json:"blsWalletId"`
CoronaWalletID string `json:"coronaWalletId"`
BLSPublicKey string `json:"blsPublicKey"`
BLSPublicKey string `json:"blsPublicKey"`
CoronaPublicKey string `json:"coronaPublicKey"`
Threshold int `json:"threshold"`
Parties int `json:"parties"`
Status string `json:"status"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
Threshold int `json:"threshold"`
Parties int `json:"parties"`
Status string `json:"status"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
type kmsGenerateRequest struct {
@@ -109,16 +109,16 @@ func (s *Server) handleKMSGenerate(w http.ResponseWriter, r *http.Request) {
now := time.Now().UTC()
ks := &ValidatorKeySet{
ValidatorID: req.ValidatorID,
BLSWalletID: blsResult.WalletID,
ValidatorID: req.ValidatorID,
BLSWalletID: blsResult.WalletID,
CoronaWalletID: coronaResult.WalletID,
BLSPublicKey: blsResult.ECDSAPubKey,
BLSPublicKey: blsResult.ECDSAPubKey,
CoronaPublicKey: coronaResult.EDDSAPubKey,
Threshold: req.Threshold,
Parties: req.Parties,
Status: "active",
CreatedAt: now,
UpdatedAt: now,
Threshold: req.Threshold,
Parties: req.Parties,
Status: "active",
CreatedAt: now,
UpdatedAt: now,
}
if s.db != nil {
+7 -7
View File
@@ -629,13 +629,13 @@ func (s *Server) handleBiometricEnroll(w http.ResponseWriter, r *http.Request) {
}
var req struct {
CredentialID string `json:"credentialId"`
SignedResponse string `json:"signedResponse"`
AuthenticatorData string `json:"authenticatorData"`
ClientDataJSON string `json:"clientDataJSON"`
PublicKey string `json:"publicKey"`
Modality string `json:"modality"`
LivenessAttestation string `json:"livenessAttestation"` // base64 envelope
CredentialID string `json:"credentialId"`
SignedResponse string `json:"signedResponse"`
AuthenticatorData string `json:"authenticatorData"`
ClientDataJSON string `json:"clientDataJSON"`
PublicKey string `json:"publicKey"`
Modality string `json:"modality"`
LivenessAttestation string `json:"livenessAttestation"` // base64 envelope
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeError(w, http.StatusBadRequest, "invalid request body")
+9 -9
View File
@@ -26,15 +26,15 @@ var errSessionConflict = errors.New("session modified concurrently")
// sessionResponse is the JSON shape specified in mpc.yaml.
type sessionResponse struct {
SessionID string `json:"sessionId"`
WalletID string `json:"walletId"`
GrantedTo string `json:"grantedTo,omitempty"`
Scopes []string `json:"scopes,omitempty"`
ValueLimit *string `json:"valueLimit,omitempty"`
OperationLimit *int `json:"operationLimit,omitempty"`
Status string `json:"status"`
CreatedAt time.Time `json:"createdAt"`
ExpiresAt time.Time `json:"expiresAt"`
SessionID string `json:"sessionId"`
WalletID string `json:"walletId"`
GrantedTo string `json:"grantedTo,omitempty"`
Scopes []string `json:"scopes,omitempty"`
ValueLimit *string `json:"valueLimit,omitempty"`
OperationLimit *int `json:"operationLimit,omitempty"`
Status string `json:"status"`
CreatedAt time.Time `json:"createdAt"`
ExpiresAt time.Time `json:"expiresAt"`
RevokedAt *time.Time `json:"revokedAt,omitempty"`
}
+3 -3
View File
@@ -272,8 +272,8 @@ func (s *Server) handleCreateWalletBackup(w http.ResponseWriter, r *http.Request
}
var req struct {
Threshold int `json:"threshold"`
TotalShards int `json:"total_shards"`
Threshold int `json:"threshold"`
TotalShards int `json:"total_shards"`
Destinations []string `json:"destinations"`
UserWebAuthnPubKey string `json:"user_webauthn_pub_key,omitempty"` // P-256 public key for passkey-encrypted backup
}
@@ -419,7 +419,7 @@ func (s *Server) handleCreateWalletBackup(w http.ResponseWriter, r *http.Request
backup.TotalShards = req.TotalShards
backup.Status = "active"
backup.UserPasskeyFingerprint = userPasskeyPubKey
backup.EncryptedKeyShare = encrypted // AES-256-GCM ciphertext, decryptable ONLY with user's passkey
backup.EncryptedKeyShare = encrypted // AES-256-GCM ciphertext, decryptable ONLY with user's passkey
backup.EphemeralPublicKey = base64.StdEncoding.EncodeToString(ephPub.Bytes()) // needed for user-side ECDH
if err := backup.Create(); err != nil {
+10 -10
View File
@@ -58,16 +58,16 @@ const treasurySignPermission = "mpc.treasury.sign"
// --- Request / response shapes ---
type treasuryWalletResponse struct {
WalletID string `json:"walletId"`
Name string `json:"name"`
Address string `json:"address,omitempty"`
Chain string `json:"chain"`
Signers []treasurySignerView `json:"signers"`
Tiers []db.TreasuryTier `json:"tiers"`
RegulatorShard bool `json:"regulatorShard"`
Status string `json:"status"`
CreatedAt time.Time `json:"createdAt"`
Outstanding []outstandingOpView `json:"outstanding,omitempty"`
WalletID string `json:"walletId"`
Name string `json:"name"`
Address string `json:"address,omitempty"`
Chain string `json:"chain"`
Signers []treasurySignerView `json:"signers"`
Tiers []db.TreasuryTier `json:"tiers"`
RegulatorShard bool `json:"regulatorShard"`
Status string `json:"status"`
CreatedAt time.Time `json:"createdAt"`
Outstanding []outstandingOpView `json:"outstanding,omitempty"`
}
type treasurySignerView struct {
+36 -35
View File
@@ -78,18 +78,18 @@ type tieredWalletCreateRequest struct {
}
type tieredWalletResponse struct {
ID string `json:"id"`
OrgID string `json:"orgId"`
Tier string `json:"tier"`
Chain string `json:"chain"`
Address string `json:"address"`
GroupPubKey string `json:"groupPubKey,omitempty"` // hex
Threshold wallet.ThresholdSpec `json:"threshold"`
Nodes []wallet.NodeBinding `json:"nodes"`
Policy wallet.TierPolicy `json:"policy"`
PolicyID string `json:"policyId,omitempty"`
CreatedAt time.Time `json:"createdAt"`
Metadata map[string]string `json:"metadata,omitempty"`
ID string `json:"id"`
OrgID string `json:"orgId"`
Tier string `json:"tier"`
Chain string `json:"chain"`
Address string `json:"address"`
GroupPubKey string `json:"groupPubKey,omitempty"` // hex
Threshold wallet.ThresholdSpec `json:"threshold"`
Nodes []wallet.NodeBinding `json:"nodes"`
Policy wallet.TierPolicy `json:"policy"`
PolicyID string `json:"policyId,omitempty"`
CreatedAt time.Time `json:"createdAt"`
Metadata map[string]string `json:"metadata,omitempty"`
}
func toTieredWalletResponse(w wallet.Wallet) tieredWalletResponse {
@@ -226,16 +226,17 @@ func (s *Server) handleTieredWalletListByTier(w http.ResponseWriter, r *http.Req
// signDecision represents the outcome of the tier-aware sign dispatch.
// Status: "auto_approved" | "approval_required" | "airgapped_queued" |
// "timelocked" | "rejected"
//
// "timelocked" | "rejected"
type signDecision struct {
Status string `json:"status"`
WalletID string `json:"walletId"`
Tier string `json:"tier"`
Reason string `json:"reason,omitempty"`
RequiredApprovals int `json:"requiredApprovals,omitempty"`
TimelockEndsAt *time.Time `json:"timelockEndsAt,omitempty"`
Usage *wallet.UsageView `json:"usage,omitempty"`
NextStep string `json:"nextStep,omitempty"`
Status string `json:"status"`
WalletID string `json:"walletId"`
Tier string `json:"tier"`
Reason string `json:"reason,omitempty"`
RequiredApprovals int `json:"requiredApprovals,omitempty"`
TimelockEndsAt *time.Time `json:"timelockEndsAt,omitempty"`
Usage *wallet.UsageView `json:"usage,omitempty"`
NextStep string `json:"nextStep,omitempty"`
}
type tieredSignRequest struct {
@@ -250,20 +251,20 @@ type tieredSignRequest struct {
//
// The decision tree:
//
// 1. Quarantine: ALWAYS reject. No path to a signature.
// 2. Per-tx + daily + velocity limits: enforced first; over-cap rejects.
// 3. Airgapped tiers (cold, DR): caller must drive the airgap flow
// (pkg/airgap/session). Returns "airgapped_queued" with the next
// step. Sibling task #106 finalizes the producer hookup.
// 4. Timelocked tiers (cold 24h, contract_admin 48h, DR 7d, quarantine
// 72h): returns "timelocked" with TimelockEndsAt; the broadcast
// side picks it up after expiry. Sibling task ships the queue.
// 5. AutoApproval tiers (hot/gas/bridge/validator): allowlist check +
// amount under per-tx limit AND not over the large-amount escalation
// threshold → "auto_approved", counters are committed.
// 6. Otherwise: requires HumanApprovalsMin (or HumanApprovalsLarge if
// amount >= LargeAmount). Returns "approval_required". Sibling task
// ships the approval bundle verifier.
// 1. Quarantine: ALWAYS reject. No path to a signature.
// 2. Per-tx + daily + velocity limits: enforced first; over-cap rejects.
// 3. Airgapped tiers (cold, DR): caller must drive the airgap flow
// (pkg/airgap/session). Returns "airgapped_queued" with the next
// step. Sibling task #106 finalizes the producer hookup.
// 4. Timelocked tiers (cold 24h, contract_admin 48h, DR 7d, quarantine
// 72h): returns "timelocked" with TimelockEndsAt; the broadcast
// side picks it up after expiry. Sibling task ships the queue.
// 5. AutoApproval tiers (hot/gas/bridge/validator): allowlist check +
// amount under per-tx limit AND not over the large-amount escalation
// threshold → "auto_approved", counters are committed.
// 6. Otherwise: requires HumanApprovalsMin (or HumanApprovalsLarge if
// amount >= LargeAmount). Returns "approval_required". Sibling task
// ships the approval bundle verifier.
//
// KYT and CanonicalIntent verifier interface are referenced via the
// sibling LocalVerifier task — for now we surface the requirement in the
+2 -3
View File
@@ -77,9 +77,9 @@ func TestCrossTenant_OrgIDSanitization(t *testing.T) {
input string
contains string // must NOT appear in the scoped key
}{
{"a:b:c", "org:a:b:c:"}, // triple colon injection
{"a:b:c", "org:a:b:c:"}, // triple colon injection
{"../etc/passwd", "org:../etc/passwd"}, // path traversal (colons don't apply but still test)
{"org\x00evil", "org:org\x00evil:"}, // null byte
{"org\x00evil", "org:org\x00evil:"}, // null byte
}
for _, tc := range cases {
@@ -508,4 +508,3 @@ func bytes_repeat(b byte, n int) []byte {
}
return out
}
+3 -3
View File
@@ -72,9 +72,9 @@ func TestBackupShardRequiresPasskey(t *testing.T) {
func TestBackupShardRejectsEmptyPasskey(t *testing.T) {
// Empty or invalid passkey pubkeys must be rejected.
invalidKeys := []string{
"", // empty
"not-base64!@#$", // garbage
base64.StdEncoding.EncodeToString([]byte{0x04}), // too short
"", // empty
"not-base64!@#$", // garbage
base64.StdEncoding.EncodeToString([]byte{0x04}), // too short
base64.StdEncoding.EncodeToString(make([]byte, 65)), // starts with 0x00, not 0x04
}
for _, key := range invalidKeys {
+10 -10
View File
@@ -68,14 +68,14 @@ type HSMProvider interface {
}
type Server struct {
db *db.Database
mpc MPCBackend
hsm HSMProvider // optional: server-side HSM co-signing
tradeApproval *custody.TradeApprovalService
txTracker *txtracker.Tracker
jwtSecret []byte
oidcIssuers []string
webauthnRPID string
db *db.Database
mpc MPCBackend
hsm HSMProvider // optional: server-side HSM co-signing
tradeApproval *custody.TradeApprovalService
txTracker *txtracker.Tracker
jwtSecret []byte
oidcIssuers []string
webauthnRPID string
webauthnOrigins map[string]bool
// PubKey is the Ed25519 verifying key used to validate
// (or any other configured PAD-2 liveness provider) attestations
@@ -88,8 +88,8 @@ type Server struct {
// intended only for the extended-envelope rollout window.
// Configured via MPC_LIVENESS_BINDING=strict|lax (default strict).
livenessBindingMode webauthn.BindingMode
router chi.Router
replayGuard *replayGuard
router chi.Router
replayGuard *replayGuard
// approval is the executive-approval registry consumed by /v1/approval/*.
// Optional — when nil, those endpoints return 503. Wired via SetApproval.
+4 -4
View File
@@ -146,7 +146,7 @@ func TestKMSZAP_Auth_RequiredHappyPath(t *testing.T) {
// Now a sign call should succeed.
respOp, signBody := callOp(t, client, peer, OpKMSSign, kmsZapSignRequest{
VaultID: "v1", WalletID: "w1", Payload: []byte("x"),
VaultID: "v1", WalletID: "w1", Payload: []byte("x"),
})
if respOp != OpKMSSign {
t.Fatalf("sign opcode: got 0x%04x want 0x%04x", respOp, OpKMSSign)
@@ -171,7 +171,7 @@ func TestKMSZAP_Auth_RequiredRejectsMissing(t *testing.T) {
defer client.Stop()
op, body := callOp(t, client, peer, OpKMSSign, kmsZapSignRequest{
VaultID: "v1", WalletID: "w1", Payload: []byte("x"),
VaultID: "v1", WalletID: "w1", Payload: []byte("x"),
})
if op != OpKMSSign {
t.Fatalf("opcode echo: got 0x%04x want 0x%04x", op, OpKMSSign)
@@ -204,7 +204,7 @@ func TestKMSZAP_Auth_RequiredRejectsBadToken(t *testing.T) {
}
op, signBody := callOp(t, client, peer, OpKMSSign, kmsZapSignRequest{
VaultID: "v1", WalletID: "w1", Payload: []byte("x"),
VaultID: "v1", WalletID: "w1", Payload: []byte("x"),
})
if op != OpKMSSign {
t.Fatalf("opcode echo: got 0x%04x want 0x%04x", op, OpKMSSign)
@@ -225,7 +225,7 @@ func TestKMSZAP_Auth_AdvisoryAcceptsMissing(t *testing.T) {
defer client.Stop()
op, body := callOp(t, client, peer, OpKMSSign, kmsZapSignRequest{
VaultID: "v1", WalletID: "w1", Payload: []byte("x"),
VaultID: "v1", WalletID: "w1", Payload: []byte("x"),
})
if op != OpKMSSign {
t.Fatalf("opcode echo: got 0x%04x want 0x%04x", op, OpKMSSign)
+11 -11
View File
@@ -13,17 +13,17 @@ import (
//
// Supported provider types:
//
// ledger-enterprise — Ledger Enterprise (Vault) REST, ECDSA-P256 attestations
// ledger-device — USB Ledger Nano via APDU, secp256k1 / Ed25519
// webauthn — FIDO2 / passkey, ES256
// aws-kms — AWS KMS asymmetric ECDSA via luxfi/hsm
// gcp-kms — Google Cloud KMS via luxfi/hsm
// azure-keyvault — Azure Key Vault via luxfi/hsm
// zymbit — Zymbit SCM via luxfi/hsm
// yubihsm — Yubico YubiHSM 2 via luxfi/hsm
// mldsa — Post-quantum ML-DSA-65 via luxfi/hsm
// safe-multisig — Gnosis Safe execTransaction, on-chain approval
// local-dev — Software Ed25519 (test only; refuses production)
// ledger-enterprise — Ledger Enterprise (Vault) REST, ECDSA-P256 attestations
// ledger-device — USB Ledger Nano via APDU, secp256k1 / Ed25519
// webauthn — FIDO2 / passkey, ES256
// aws-kms — AWS KMS asymmetric ECDSA via luxfi/hsm
// gcp-kms — Google Cloud KMS via luxfi/hsm
// azure-keyvault — Azure Key Vault via luxfi/hsm
// zymbit — Zymbit SCM via luxfi/hsm
// yubihsm — Yubico YubiHSM 2 via luxfi/hsm
// mldsa — Post-quantum ML-DSA-65 via luxfi/hsm
// safe-multisig — Gnosis Safe execTransaction, on-chain approval
// local-dev — Software Ed25519 (test only; refuses production)
//
// Production safety: providers that hold a private key only acceptable in
// non-prod environments (currently only "local-dev") refuse to start when
+1 -1
View File
@@ -21,7 +21,7 @@ import (
//
// Configuration:
//
// region — AWS region (e.g. "us-east-1"). Defaults to AWS_REGION env.
// region — AWS region (e.g. "us-east-1"). Defaults to AWS_REGION env.
//
// Approver enrollment is explicit: callers must register each approver's
// (approverID -> KMS key ARN, P-256 public key, optional attestation)
+2 -2
View File
@@ -20,8 +20,8 @@ import (
//
// Configuration:
//
// vault_url — full Vault URL (e.g. "https://approvals.vault.azure.net").
// Optional; falls back to AZURE_VAULT_URL.
// vault_url — full Vault URL (e.g. "https://approvals.vault.azure.net").
// Optional; falls back to AZURE_VAULT_URL.
//
// Approver enrollment by Vault key name + cached SPKI public key.
type AzureKeyVaultProvider struct {
+1 -1
View File
@@ -18,7 +18,7 @@ import (
// GCPKMSProvider produces ECDSA-P256 approval signatures via Google Cloud
// KMS asymmetric keys. Mirrors AWSKMSProvider; key resource name format:
//
// projects/{project}/locations/{location}/keyRings/{keyRing}/cryptoKeys/{key}/cryptoKeyVersions/{version}
// projects/{project}/locations/{location}/keyRings/{keyRing}/cryptoKeys/{key}/cryptoKeyVersions/{version}
type GCPKMSProvider struct {
signer luxhsm.Signer
+12 -13
View File
@@ -36,18 +36,18 @@ import (
//
// Configuration:
//
// ledgerctl_path — path to ledgerctl (default "ledgerctl" on PATH)
// app — Ledger app name. Default "ethereum". Set to
// "lux" for Lux native paths, "bitcoin" for BTC, etc.
// bip32_path — derivation path. Default m/44'/9000'/0'/0/0 (Lux
// native). For EVM-compat use m/44'/60'/0'/0/0;
// ledger.BIP44PathForName(...) builds this.
// chain_id — EIP-155 chain id forwarded to ledgerctl when app
// is "ethereum". Optional.
// sign_action — override the verb passed to ledgerctl; defaults
// to "{app}-sign-hash".
// pubkey_action — override the verb for fetching the device pubkey;
// defaults to "{app}-get-pubkey".
// ledgerctl_path — path to ledgerctl (default "ledgerctl" on PATH)
// app — Ledger app name. Default "ethereum". Set to
// "lux" for Lux native paths, "bitcoin" for BTC, etc.
// bip32_path — derivation path. Default m/44'/9000'/0'/0/0 (Lux
// native). For EVM-compat use m/44'/60'/0'/0/0;
// ledger.BIP44PathForName(...) builds this.
// chain_id — EIP-155 chain id forwarded to ledgerctl when app
// is "ethereum". Optional.
// sign_action — override the verb passed to ledgerctl; defaults
// to "{app}-sign-hash".
// pubkey_action — override the verb for fetching the device pubkey;
// defaults to "{app}-get-pubkey".
//
// Verification: pubkeys returned by the device are parsed as compressed
// SEC1 secp256k1 (33 bytes). Approvals sign SHA-256(intent.Bytes())
@@ -377,4 +377,3 @@ func asn1MarshalRS(r, s *big.Int) ([]byte, error) {
// Reuse the helper from ecdsa_helpers.go via the local ecdsaSig type.
return marshalECDSASig(ecdsaSig{R: r, S: s})
}
+10 -10
View File
@@ -26,18 +26,18 @@ import (
//
// Ledger Enterprise REST endpoints used:
//
// POST /accounts/{accountId}/transactions — propose tx
// GET /accounts/{accountId}/transactions/{txId} — poll status
// POST /accounts/{accountId}/transactions/{txId}/approve — approve via PSD
// POST /accounts/{accountId}/transactions — propose tx
// GET /accounts/{accountId}/transactions/{txId} — poll status
// POST /accounts/{accountId}/transactions/{txId}/approve — approve via PSD
//
// Public docs: https://docs.ledger.com/enterprise
//
// Configuration:
//
// endpoint — Vault REST base URL (e.g. "https://vault.ledger-enterprise.example/api")
// api_key — service-account API key (Bearer token)
// workspace — workspace ID
// timeout — HTTP request timeout (default 30s); approval polling timeout (default 30m)
// endpoint — Vault REST base URL (e.g. "https://vault.ledger-enterprise.example/api")
// api_key — service-account API key (Bearer token)
// workspace — workspace ID
// timeout — HTTP request timeout (default 30s); approval polling timeout (default 30m)
//
// Approver enrollment maps approverID -> Vault account ID + cached secp256k1
// public key (Ledger Enterprise typically uses secp256k1 for transaction
@@ -169,14 +169,14 @@ func (p *LedgerEnterpriseProvider) GetPublicIdentity(_ context.Context, approver
type leTransactionRequest struct {
Workspace string `json:"workspace"`
Type string `json:"type"` // "approval-attestation"
Digest string `json:"digest"` // base64 of intent digest
Type string `json:"type"` // "approval-attestation"
Digest string `json:"digest"` // base64 of intent digest
Algorithm string `json:"signing_algorithm"`
}
type leTransactionResponse struct {
TxID string `json:"transaction_id"`
Status string `json:"status"` // "pending", "approved", "rejected", "failed"
Status string `json:"status"` // "pending", "approved", "rejected", "failed"
Sig string `json:"signature,omitempty"` // base64 DER signature when approved
Error string `json:"error,omitempty"`
}
+2 -2
View File
@@ -29,8 +29,8 @@ type MLDSAProvider struct {
}
type mldsaEntry struct {
PrivKey *mldsa.PrivateKey
PubKey *mldsa.PublicKey
PrivKey *mldsa.PrivateKey
PubKey *mldsa.PublicKey
PubBytes []byte
}
+9 -9
View File
@@ -16,13 +16,13 @@ import (
//
// Layout of an approval bundle for this provider:
//
// [chainID (8B BE)] [safeAddress (20B)] [safeTxHash (32B)] [execTxHash (32B)]
// [chainID (8B BE)] [safeAddress (20B)] [safeTxHash (32B)] [execTxHash (32B)]
//
// Verification: a service caller checks
//
// 1. safeTxHash == Safe.getTransactionHash(intent.Bytes() / EIP-712 typed data)
// 2. execTxHash is mined (≥ 1 confirmation) on the chain
// 3. execTxHash receipt logs include `ExecutionSuccess(safeTxHash)`
// 1. safeTxHash == Safe.getTransactionHash(intent.Bytes() / EIP-712 typed data)
// 2. execTxHash is mined (≥ 1 confirmation) on the chain
// 3. execTxHash receipt logs include `ExecutionSuccess(safeTxHash)`
//
// This package only validates the bundle's *shape* — on-chain confirmation
// is the caller's responsibility (it requires an EVM RPC client we don't
@@ -30,8 +30,8 @@ import (
//
// Configuration:
//
// chain_id — decimal chain ID (e.g. "1" mainnet, "42161" arbitrum)
// safe_address — 0x-prefixed Safe address (20 bytes)
// chain_id — decimal chain ID (e.g. "1" mainnet, "42161" arbitrum)
// safe_address — 0x-prefixed Safe address (20 bytes)
//
// Approver enrollment: register approverID -> Safe address. One Safe can
// represent any number of logical approvers (each with their own owner key)
@@ -52,9 +52,9 @@ type safeEntry struct {
}
type pendingSafe struct {
intent CanonicalIntent
expiresAt time.Time
resultCh chan safeResult
intent CanonicalIntent
expiresAt time.Time
resultCh chan safeResult
}
type safeResult struct {
+13 -13
View File
@@ -28,10 +28,10 @@ import (
//
// Configuration:
//
// rpid — Relying Party ID (e.g. "approvals.fund.com"). Required.
// origins — comma-separated allowed origins (e.g. "https://approvals.fund.com").
// In production, MUST be HTTPS.
// timeout — challenge TTL (default 5m). Override with "300s" / "5m".
// rpid — Relying Party ID (e.g. "approvals.fund.com"). Required.
// origins — comma-separated allowed origins (e.g. "https://approvals.fund.com").
// In production, MUST be HTTPS.
// timeout — challenge TTL (default 5m). Override with "300s" / "5m".
//
// HTTPS enforcement: NewWebAuthn requires HTTPS origins unless MPC_ENV=dev.
type WebAuthnProvider struct {
@@ -39,22 +39,22 @@ type WebAuthnProvider struct {
allowedOrigins map[string]bool
timeout time.Duration
mu sync.Mutex
identity map[string]webauthnEntry
pending map[string]*pendingWebAuthn
mu sync.Mutex
identity map[string]webauthnEntry
pending map[string]*pendingWebAuthn
}
type webauthnEntry struct {
CredentialID string // base64url-encoded WebAuthn credential ID
PubKey *ecdsa.PublicKey
CredentialID string // base64url-encoded WebAuthn credential ID
PubKey *ecdsa.PublicKey
PubKeyUncompressed []byte // 65-byte 0x04||X||Y
}
type pendingWebAuthn struct {
intent CanonicalIntent
challenge []byte // 32 random bytes; binds to intent.Digest()
expiresAt time.Time
resultCh chan webauthnResult
intent CanonicalIntent
challenge []byte // 32 random bytes; binds to intent.Digest()
expiresAt time.Time
resultCh chan webauthnResult
}
type webauthnResult struct {
+1 -1
View File
@@ -22,7 +22,7 @@ import (
//
// Configuration:
//
// api_addr — Zymbit REST API URL (default "http://localhost:6789").
// api_addr — Zymbit REST API URL (default "http://localhost:6789").
//
// Approver enrollment maps approverID -> Zymbit slot ID + cached SPKI public
// key. Slots must be provisioned out-of-band on the Zymbit device.
+8 -8
View File
@@ -12,10 +12,10 @@
//
// One way to wire approval:
//
// intent := intent.New(...)
// bundle, err := orchestrator.CollectApprovals(ctx, intent, approvers, threshold)
// // intent.SetApprovals(bundle); MPC signing only proceeds when
// // bundle has >= threshold valid signatures from required approvers.
// intent := intent.New(...)
// bundle, err := orchestrator.CollectApprovals(ctx, intent, approvers, threshold)
// // intent.SetApprovals(bundle); MPC signing only proceeds when
// // bundle has >= threshold valid signatures from required approvers.
package approval
import (
@@ -103,10 +103,10 @@ type ApprovalSignature struct {
// Use the named constants — never raw strings — so a typo at the call site
// becomes a compile error.
const (
AlgorithmECDSAP256 = "ECDSA-P256" // generic ECDSA secp256r1, SHA-256
AlgorithmECDSAsecp256k1 = "ECDSA-secp256k1" // Ethereum / Bitcoin signatures
AlgorithmECDSAP256 = "ECDSA-P256" // generic ECDSA secp256r1, SHA-256
AlgorithmECDSAsecp256k1 = "ECDSA-secp256k1" // Ethereum / Bitcoin signatures
AlgorithmEd25519 = "Ed25519"
AlgorithmMLDSA65 = "ML-DSA-65" // FIPS 204, NIST level 3
AlgorithmMLDSA65 = "ML-DSA-65" // FIPS 204, NIST level 3
AlgorithmSafeContract = "Safe-execTransaction" // on-chain Safe approval
AlgorithmWebAuthnES256 = "WebAuthn-ES256" // CTAP2 ES256 (P-256)
AlgorithmWebAuthnES256 = "WebAuthn-ES256" // CTAP2 ES256 (P-256)
)
+24 -23
View File
@@ -61,28 +61,28 @@ func (t TEEKind) String() string {
// not retained here; this struct holds only the post-verification
// projection that downstream gates need.
type CPUQuote struct {
Kind TEEKind // SEV-SNP or TDX
Verified bool // verifier confirmed the signature chain
MeasurementHash [32]byte // launch measurement (MRTD or LAUNCH_MEASUREMENT)
ReportData [32]byte // freshness binding (e.g. nonce hash)
VerifiedAt time.Time // when the verifier signed off
VendorCertChain [][]byte // AMD or Intel vendor cert chain (DER)
Kind TEEKind // SEV-SNP or TDX
Verified bool // verifier confirmed the signature chain
MeasurementHash [32]byte // launch measurement (MRTD or LAUNCH_MEASUREMENT)
ReportData [32]byte // freshness binding (e.g. nonce hash)
VerifiedAt time.Time // when the verifier signed off
VendorCertChain [][]byte // AMD or Intel vendor cert chain (DER)
}
// GPUEvidence is the verified output of an NVIDIA GPU attestation.
// Populated by GPUReport.Parse + NRASClient.Verify + RIM.Match.
type GPUEvidence struct {
UUID string // GPU UUID, e.g. "GPU-1234..."
DriverVersion string // e.g. "535.104.05"
VBIOSVersion string // e.g. "96.00.74.00.01"
Architecture string // "Hopper" | "Blackwell"
Verified bool // NRAS returned a valid token
NRASTokenHash [32]byte // sha256 of the NRAS token bytes
MeasurementRoot [32]byte // root over the measurement list
RIMMatched bool // every measurement matched the RIM
Nonce [32]byte // freshness nonce echoed in report
VerifiedAt time.Time // when NRAS signed the token
NVSwitchVerified bool // multi-GPU NVSwitch attested (optional)
UUID string // GPU UUID, e.g. "GPU-1234..."
DriverVersion string // e.g. "535.104.05"
VBIOSVersion string // e.g. "96.00.74.00.01"
Architecture string // "Hopper" | "Blackwell"
Verified bool // NRAS returned a valid token
NRASTokenHash [32]byte // sha256 of the NRAS token bytes
MeasurementRoot [32]byte // root over the measurement list
RIMMatched bool // every measurement matched the RIM
Nonce [32]byte // freshness nonce echoed in report
VerifiedAt time.Time // when NRAS signed the token
NVSwitchVerified bool // multi-GPU NVSwitch attested (optional)
}
// VerifiedBundle binds a CPU TEE quote to a GPU evidence record
@@ -111,11 +111,11 @@ type VerifiedResult struct {
// Errors returned by Verify, in the order they are checked.
var (
ErrBundleMissingWorker = errors.New("nvidia: composite missing worker ID")
ErrBundleNoCPUQuote = errors.New("nvidia: composite missing CPU quote")
ErrBundleBadCPU = errors.New("nvidia: CPU quote not verified")
ErrBundleBadGPU = errors.New("nvidia: GPU evidence not verified")
ErrBundleRIMMismatch = errors.New("nvidia: RIM mismatch on GPU measurements")
ErrBundleMissingWorker = errors.New("nvidia: composite missing worker ID")
ErrBundleNoCPUQuote = errors.New("nvidia: composite missing CPU quote")
ErrBundleBadCPU = errors.New("nvidia: CPU quote not verified")
ErrBundleBadGPU = errors.New("nvidia: GPU evidence not verified")
ErrBundleRIMMismatch = errors.New("nvidia: RIM mismatch on GPU measurements")
ErrBundleBindingMissing = errors.New("nvidia: CPU report_data does not bind GPU nonce")
ErrBundleStale = errors.New("nvidia: composite older than freshness window")
)
@@ -179,7 +179,8 @@ func (c *VerifiedBundle) Verify(now time.Time, freshness time.Duration) (Verifie
// use this when constructing the CPU quote so the composite verifies.
//
// Layout: sha256( "luxfi/mpc/composite\x00" || workerID || "\x00" ||
// gpuUUID || "\x00" || gpuNonce ).
//
// gpuUUID || "\x00" || gpuNonce ).
//
// The leading domain string + null separators prevent cross-domain
// reuse of the same hash (a SEV-SNP report bound this way cannot be
+3 -3
View File
@@ -136,9 +136,9 @@ func TestBindCPUReportData_StableAcrossInputs(t *testing.T) {
func TestTEEKind_String(t *testing.T) {
cases := map[TEEKind]string{
TEENone: "none",
TEESEVSNP: "sev_snp",
TEETDX: "tdx",
TEENone: "none",
TEESEVSNP: "sev_snp",
TEETDX: "tdx",
TEEKind(99): "invalid",
}
for k, want := range cases {
+17 -17
View File
@@ -20,13 +20,13 @@ import (
// Errors returned by the parser.
var (
ErrReportEmpty = errors.New("nvidia/report: empty input")
ErrReportBadJSON = errors.New("nvidia/report: bad JSON envelope")
ErrReportBadVersion = errors.New("nvidia/report: unsupported evidence_version")
ErrReportMissingNonce = errors.New("nvidia/report: missing nonce")
ErrReportBadNonce = errors.New("nvidia/report: nonce must be 32 bytes hex")
ErrReportMissingArch = errors.New("nvidia/report: missing architecture")
ErrReportUnknownArch = errors.New("nvidia/report: unknown architecture")
ErrReportEmpty = errors.New("nvidia/report: empty input")
ErrReportBadJSON = errors.New("nvidia/report: bad JSON envelope")
ErrReportBadVersion = errors.New("nvidia/report: unsupported evidence_version")
ErrReportMissingNonce = errors.New("nvidia/report: missing nonce")
ErrReportBadNonce = errors.New("nvidia/report: nonce must be 32 bytes hex")
ErrReportMissingArch = errors.New("nvidia/report: missing architecture")
ErrReportUnknownArch = errors.New("nvidia/report: unknown architecture")
ErrReportNoMeasurements = errors.New("nvidia/report: no measurements present")
)
@@ -57,16 +57,16 @@ type Measurement struct {
// rawReport is the on-the-wire envelope. Field names are the
// canonical lower-snake spelling used by the NVIDIA evidence schema.
type rawReport struct {
EvidenceVersion string `json:"evidence_version"`
GPUUUID string `json:"gpu_uuid"`
Architecture string `json:"architecture"`
DriverVersion string `json:"driver_version"`
VBIOSVersion string `json:"vbios_version"`
NonceHex string `json:"nonce"`
Measurements []Measurement `json:"measurements"`
CertChain []string `json:"cert_chain"` // PEM blocks
Quote string `json:"attestation_quote"` // base64 binary
NVSwitchPresent bool `json:"nvswitch_present"`
EvidenceVersion string `json:"evidence_version"`
GPUUUID string `json:"gpu_uuid"`
Architecture string `json:"architecture"`
DriverVersion string `json:"driver_version"`
VBIOSVersion string `json:"vbios_version"`
NonceHex string `json:"nonce"`
Measurements []Measurement `json:"measurements"`
CertChain []string `json:"cert_chain"` // PEM blocks
Quote string `json:"attestation_quote"` // base64 binary
NVSwitchPresent bool `json:"nvswitch_present"`
}
// GPUReport is the parsed, validated GPU evidence report — every field
+23 -24
View File
@@ -49,11 +49,11 @@ import (
// Errors returned by the NRAS client.
var (
ErrNRASNoEndpoint = errors.New("nvidia/nras: endpoint not configured")
ErrNRASNoEvidence = errors.New("nvidia/nras: empty evidence")
ErrNRASBadStatus = errors.New("nvidia/nras: non-2xx response")
ErrNRASBadResponse = errors.New("nvidia/nras: malformed response body")
ErrNRASEmptyToken = errors.New("nvidia/nras: token field missing")
ErrNRASNoEndpoint = errors.New("nvidia/nras: endpoint not configured")
ErrNRASNoEvidence = errors.New("nvidia/nras: empty evidence")
ErrNRASBadStatus = errors.New("nvidia/nras: non-2xx response")
ErrNRASBadResponse = errors.New("nvidia/nras: malformed response body")
ErrNRASEmptyToken = errors.New("nvidia/nras: token field missing")
ErrNRASNonceMismatch = errors.New("nvidia/nras: token nonce does not match request")
ErrNRASTokenExpired = errors.New("nvidia/nras: token already expired")
ErrNRASBadJWT = errors.New("nvidia/nras: token not a valid JWS compact serialization")
@@ -71,9 +71,9 @@ var (
// VerifyResult. The HTTP client is injectable so tests use httptest
// directly.
type NRASClient struct {
Endpoint string // e.g. "https://nras.attestation.nvidia.com"
HTTPClient *http.Client // nil = http.DefaultClient with 15s timeout
TrustRoots []TrustRoot // public keys that may sign NRAS tokens
Endpoint string // e.g. "https://nras.attestation.nvidia.com"
HTTPClient *http.Client // nil = http.DefaultClient with 15s timeout
TrustRoots []TrustRoot // public keys that may sign NRAS tokens
Now func() time.Time
}
@@ -97,13 +97,13 @@ type VerifyOptions struct {
// VerifyResult captures the post-verification facts. Callers fold
// these into GPUEvidence.
type VerifyResult struct {
Token string // raw JWS compact form
TokenHash [32]byte // sha256 of the token bytes
Nonce [32]byte // echoed from the request — must match GPU report
ExpiresAt time.Time // honored if non-zero
IssuedAt time.Time // RFC3339 from the response
SignerKeyID string // which trust root verified the JWS
Architecture string // mirrored back from the report; for audit
Token string // raw JWS compact form
TokenHash [32]byte // sha256 of the token bytes
Nonce [32]byte // echoed from the request — must match GPU report
ExpiresAt time.Time // honored if non-zero
IssuedAt time.Time // RFC3339 from the response
SignerKeyID string // which trust root verified the JWS
Architecture string // mirrored back from the report; for audit
}
// Verify uploads evidence and returns the parsed result.
@@ -249,14 +249,14 @@ func (c *NRASClient) Verify(ctx context.Context, report []byte, nonce [32]byte,
// by CompositeAttestation.
func FoldIntoEvidence(r *VerifyResult, report *GPUReport, rim *RIM) GPUEvidence {
out := GPUEvidence{
UUID: report.UUID,
DriverVersion: report.DriverVersion,
VBIOSVersion: report.VBIOSVersion,
Architecture: report.Architecture,
Verified: true,
NRASTokenHash: r.TokenHash,
Nonce: r.Nonce,
VerifiedAt: r.IssuedAt,
UUID: report.UUID,
DriverVersion: report.DriverVersion,
VBIOSVersion: report.VBIOSVersion,
Architecture: report.Architecture,
Verified: true,
NRASTokenHash: r.TokenHash,
Nonce: r.Nonce,
VerifiedAt: r.IssuedAt,
NVSwitchVerified: report.NVSwitchPresent,
}
if rim != nil {
@@ -424,4 +424,3 @@ func truncate(s string, n int) string {
}
return s[:n] + "..."
}
+8 -8
View File
@@ -2,14 +2,14 @@
// CVE-2015-9235 family). The eight cases below pin down the alg ↔
// key-type matrix at the dispatch layer:
//
// 1. EdDSA + ed25519.PublicKey → PASS
// 2. ES256 + *ecdsa.PublicKey on P-256 → PASS
// 3. PS256 + *rsa.PublicKey → PASS
// 4. EdDSA paired with RSA pub → ErrAlgKeyTypeMismatch
// 5. ES256 paired with ed25519 pub → ErrAlgKeyTypeMismatch
// 6. ES256 paired with ecdsa P-384 → ErrAlgKeyTypeMismatch
// 7. alg=none → ErrAlgNoneRefused
// 8. alg=Unknown → ErrUnknownAlg
// 1. EdDSA + ed25519.PublicKey → PASS
// 2. ES256 + *ecdsa.PublicKey on P-256 → PASS
// 3. PS256 + *rsa.PublicKey → PASS
// 4. EdDSA paired with RSA pub → ErrAlgKeyTypeMismatch
// 5. ES256 paired with ed25519 pub → ErrAlgKeyTypeMismatch
// 6. ES256 paired with ecdsa P-384 → ErrAlgKeyTypeMismatch
// 7. alg=none → ErrAlgNoneRefused
// 8. alg=Unknown → ErrUnknownAlg
//
// Cases 4-6 are the substitution attack: a forged JWS header that
// names a different alg than the trust-root's key actually supports.
-1
View File
@@ -277,4 +277,3 @@ func sortStrings(s []string) {
s[j+1] = v
}
}
+5 -5
View File
@@ -113,11 +113,11 @@ func (d *MChainDispatcher) flushLocked(ctx context.Context) error {
batchHash := hex.EncodeToString(h.Sum(nil))
body, err := json.Marshal(map[string]any{
"batchHash": batchHash,
"fromSeq": from,
"toSeq": to,
"prevAnchor": d.anchor,
"events": d.pend,
"batchHash": batchHash,
"fromSeq": from,
"toSeq": to,
"prevAnchor": d.anchor,
"events": d.pend,
})
if err != nil {
return fmt.Errorf("audit/mchain: marshal: %w", err)
+3 -3
View File
@@ -92,7 +92,7 @@ func TestGasStation_AboveFloorSkipped(t *testing.T) {
_ = g.Set(GasRule{
OrgID: "org1", Chain: "ethereum", ReserveWallet: "reserve",
TargetWallets: []string{"hot1"},
Floor: big.NewInt(100), Target: big.NewInt(500), CapPerTick: big.NewInt(10000),
Floor: big.NewInt(100), Target: big.NewInt(500), CapPerTick: big.NewInt(10000),
Enabled: true,
})
results, err := g.Tick(context.Background())
@@ -113,7 +113,7 @@ func TestGasStation_CapPerTickHonored(t *testing.T) {
_ = g.Set(GasRule{
OrgID: "org1", Chain: "ethereum", ReserveWallet: "reserve",
TargetWallets: []string{"hot1"},
Floor: big.NewInt(100), Target: big.NewInt(10000), CapPerTick: big.NewInt(50),
Floor: big.NewInt(100), Target: big.NewInt(10000), CapPerTick: big.NewInt(50),
Enabled: true,
})
results, _ := g.Tick(context.Background())
@@ -136,7 +136,7 @@ func TestGasStation_BalanceError(t *testing.T) {
_ = g.Set(GasRule{
OrgID: "org1", Chain: "ethereum", ReserveWallet: "reserve",
TargetWallets: []string{"hot1"},
Floor: big.NewInt(100), Target: big.NewInt(500), CapPerTick: big.NewInt(10000),
Floor: big.NewInt(100), Target: big.NewInt(500), CapPerTick: big.NewInt(10000),
Enabled: true,
})
results, _ := g.Tick(context.Background())
+4 -4
View File
@@ -86,10 +86,10 @@ type BTCBumpInput struct {
// BTCBumpPlan is the new fee + the inputs (unchanged) that the caller
// must sign into a replacement transaction.
type BTCBumpPlan struct {
NewFeeSat *big.Int `json:"newFeeSat"`
NewFeeRateSatVB int64 `json:"newFeeRateSatVB"`
Inputs []string `json:"inputs"`
BumpFactorBps int64 `json:"bumpFactorBps"`
NewFeeSat *big.Int `json:"newFeeSat"`
NewFeeRateSatVB int64 `json:"newFeeRateSatVB"`
Inputs []string `json:"inputs"`
BumpFactorBps int64 `json:"bumpFactorBps"`
}
// BTCBump computes an RBF-compliant replacement fee.
+7 -7
View File
@@ -70,13 +70,13 @@ func (r GasRule) Validate() error {
// TopupReceipt records what a Tick did for one address.
type TopupReceipt struct {
Address string `json:"address"`
BeforeWei string `json:"beforeWei"`
AfterWei string `json:"afterWei,omitempty"`
SentWei string `json:"sentWei"`
TxHash string `json:"txHash"`
SkipReason string `json:"skipReason,omitempty"`
OccurredAt time.Time `json:"occurredAt"`
Address string `json:"address"`
BeforeWei string `json:"beforeWei"`
AfterWei string `json:"afterWei,omitempty"`
SentWei string `json:"sentWei"`
TxHash string `json:"txHash"`
SkipReason string `json:"skipReason,omitempty"`
OccurredAt time.Time `json:"occurredAt"`
}
// TickResult is the per-rule outcome of one GasStation tick.
+19 -19
View File
@@ -30,17 +30,17 @@ import (
// for small amounts; for large transfers the absolute fee dominates and
// providers should report it precisely.
type Quote struct {
Provider string `json:"provider"`
Route string `json:"route"` // human-readable route description
Asset string `json:"asset"`
Amount string `json:"amount"` // base-10 in smallest unit
Dest string `json:"dest"`
Chain string `json:"chain"` // destination chain (CAIP-2)
FeeBps int64 `json:"feeBps"` // total cost in basis points
FeeAbs string `json:"feeAbs"` // absolute fee in smallest unit (informational)
ETA time.Duration `json:"eta"` // estimated time to settle
ValidUntil time.Time `json:"validUntil"`
Provider_ Provider `json:"-"` // back-reference for execution
Provider string `json:"provider"`
Route string `json:"route"` // human-readable route description
Asset string `json:"asset"`
Amount string `json:"amount"` // base-10 in smallest unit
Dest string `json:"dest"`
Chain string `json:"chain"` // destination chain (CAIP-2)
FeeBps int64 `json:"feeBps"` // total cost in basis points
FeeAbs string `json:"feeAbs"` // absolute fee in smallest unit (informational)
ETA time.Duration `json:"eta"` // estimated time to settle
ValidUntil time.Time `json:"validUntil"`
Provider_ Provider `json:"-"` // back-reference for execution
}
// TransferRequest is what SmartTransfer.Quote consumes.
@@ -176,9 +176,9 @@ func (s *SmartTransfer) Best(ctx context.Context, req TransferRequest) (Quote, e
// callers feed the resulting Quote into their existing sign + broadcast
// path.
type OnChainProvider struct {
chain string
bps int64
eta time.Duration
chain string
bps int64
eta time.Duration
}
// NewOnChainProvider constructs a provider for one chain. bps is the
@@ -217,11 +217,11 @@ func (p *OnChainProvider) QuoteTransfer(_ context.Context, req TransferRequest)
// BridgeProvider quotes a cross-chain transfer. RouteName is used in the
// quote's Route field for operator visibility.
type BridgeProvider struct {
name string
bps int64
srcChain string
dstChain string
eta time.Duration
name string
bps int64
srcChain string
dstChain string
eta time.Duration
}
// NewBridgeProvider constructs a bridge provider. The (src,dst) pair is
+4 -4
View File
@@ -30,7 +30,7 @@ type BiometricProof struct {
type DeviceRegistration struct {
DeviceID string `json:"device_id"`
DeviceType string `json:"device_type"` // ios, android
PublicKey []byte `json:"public_key"` // Secure Enclave P-256 uncompressed public key
PublicKey []byte `json:"public_key"` // Secure Enclave P-256 uncompressed public key
BiometricType string `json:"biometric_type"` // face_id, touch_id, fingerprint
WalletID string `json:"wallet_id"`
ParticipantID string `json:"participant_id"` // MPC participant slot (e.g., "user")
@@ -45,9 +45,9 @@ const maxProofAge = 30 * time.Second
// nonceGuard tracks used nonces to prevent replay attacks.
// Nonces expire after 2x the proof age to bound memory usage.
type nonceGuard struct {
mu sync.Mutex
used map[string]time.Time
ttl time.Duration
mu sync.Mutex
used map[string]time.Time
ttl time.Duration
}
func newNonceGuard() *nonceGuard {
+2 -2
View File
@@ -26,7 +26,7 @@ type TradeApprovalNotification struct {
DeviceType string `json:"device_type"` // ios, android
TradeID string `json:"trade_id"`
Symbol string `json:"symbol"`
Side string `json:"side"` // buy, sell
Side string `json:"side"` // buy, sell
Quantity string `json:"quantity"`
Price string `json:"price"`
TotalValue string `json:"total_value"`
@@ -98,7 +98,7 @@ func (f *FCMNotifier) SendTradeApproval(ctx context.Context, req TradeApprovalNo
},
"apns": map[string]interface{}{
"headers": map[string]string{
"apns-priority": "10",
"apns-priority": "10",
"apns-push-type": "alert",
},
},
+33 -33
View File
@@ -406,13 +406,13 @@ func init() { orm.Register[Settlement]("settlement") }
// (e.g., iCloud Keychain + Platform HSM) so that no single party can decrypt alone.
type WalletBackup struct {
orm.Model[WalletBackup]
OrgID string `json:"orgId"`
WalletID string `json:"walletId"`
BackupID string `json:"backupId"` // unique backup identifier
Threshold int `json:"threshold"` // T shards required to reconstruct
TotalShards int `json:"totalShards"`
EncryptedKeyShare []byte `json:"encryptedKeyShare,omitempty"` // AES-256-GCM(ECDH(user_passkey, ephemeral)) encrypted key share
EphemeralPublicKey string `json:"ephemeralPublicKey,omitempty"` // P-256 ephemeral pubkey for user-side ECDH decryption
OrgID string `json:"orgId"`
WalletID string `json:"walletId"`
BackupID string `json:"backupId"` // unique backup identifier
Threshold int `json:"threshold"` // T shards required to reconstruct
TotalShards int `json:"totalShards"`
EncryptedKeyShare []byte `json:"encryptedKeyShare,omitempty"` // AES-256-GCM(ECDH(user_passkey, ephemeral)) encrypted key share
EphemeralPublicKey string `json:"ephemeralPublicKey,omitempty"` // P-256 ephemeral pubkey for user-side ECDH decryption
Shards []BackupShard `json:"shards,omitempty"`
Status string `json:"status"` // active, recovered, revoked
UserPasskeyFingerprint string `json:"userPasskeyFingerprint,omitempty"` // P-256 pubkey that encrypts the backup shard — company CANNOT decrypt
@@ -440,8 +440,8 @@ type DeviceEnrollment struct {
DeviceID string `json:"deviceId"`
DeviceType string `json:"deviceType"` // ios, android
BiometricType string `json:"biometricType"` // face_id, touch_id, fingerprint
PublicKey string `json:"publicKey"` // Base64-encoded P-256 Secure Enclave public key
ParticipantID string `json:"participantId"` // MPC participant slot (e.g., "user")
PublicKey string `json:"publicKey"` // Base64-encoded P-256 Secure Enclave public key
ParticipantID string `json:"participantId"` // MPC participant slot (e.g., "user")
Challenge string `json:"challenge,omitempty"`
Attestation string `json:"attestation,omitempty"` // Device attestation object
PushToken string `json:"pushToken,omitempty"` // FCM or APNS push notification token
@@ -462,25 +462,25 @@ func init() { orm.Register[DeviceEnrollment]("device-enrollment") }
// pending_approval → expired
type PendingTrade struct {
orm.Model[PendingTrade]
OrgID string `json:"orgId"`
UserID string `json:"userId"`
WalletID string `json:"walletId"`
DeviceID string `json:"deviceId,omitempty"`
Symbol string `json:"symbol"`
Side string `json:"side"` // buy, sell
Quantity string `json:"quantity"`
Price string `json:"price"`
TotalValue string `json:"totalValue"`
TradeID string `json:"tradeId,omitempty"` // ATS trade record ID
OrderID string `json:"orderId,omitempty"` // ATS order record ID
MessageHash string `json:"messageHash"` // Settlement tx data to sign (hex)
IntentID *string `json:"intentId,omitempty"` // MPC intent created after approval
TxHash *string `json:"txHash,omitempty"` // On-chain tx hash after broadcast
Status string `json:"status"`
Reason *string `json:"reason,omitempty"` // Rejection reason
ExpiresAt time.Time `json:"expiresAt"` // 5 minute approval window
ApprovedAt *time.Time `json:"approvedAt,omitempty"`
SignedAt *time.Time `json:"signedAt,omitempty"`
OrgID string `json:"orgId"`
UserID string `json:"userId"`
WalletID string `json:"walletId"`
DeviceID string `json:"deviceId,omitempty"`
Symbol string `json:"symbol"`
Side string `json:"side"` // buy, sell
Quantity string `json:"quantity"`
Price string `json:"price"`
TotalValue string `json:"totalValue"`
TradeID string `json:"tradeId,omitempty"` // ATS trade record ID
OrderID string `json:"orderId,omitempty"` // ATS order record ID
MessageHash string `json:"messageHash"` // Settlement tx data to sign (hex)
IntentID *string `json:"intentId,omitempty"` // MPC intent created after approval
TxHash *string `json:"txHash,omitempty"` // On-chain tx hash after broadcast
Status string `json:"status"`
Reason *string `json:"reason,omitempty"` // Rejection reason
ExpiresAt time.Time `json:"expiresAt"` // 5 minute approval window
ApprovedAt *time.Time `json:"approvedAt,omitempty"`
SignedAt *time.Time `json:"signedAt,omitempty"`
StatusHistory []StatusTransition `json:"statusHistory,omitempty"`
}
@@ -557,9 +557,9 @@ const (
// `KeyRef` is either an IAM user/service-principal ID (for human signers)
// or a KMS key path (for HSM signers, always `providers/{OrgID}/...`).
type TreasurySigner struct {
Role string `json:"role"` // compliance_officer | treasurer | platform_hsm | backup_hsm | regulator
KeyRef string `json:"keyRef"` // user ID for humans, KMS key path for HSMs
DisplayName string `json:"displayName,omitempty"`
Role string `json:"role"` // compliance_officer | treasurer | platform_hsm | backup_hsm | regulator
KeyRef string `json:"keyRef"` // user ID for humans, KMS key path for HSMs
DisplayName string `json:"displayName,omitempty"`
RotatedAt *time.Time `json:"rotatedAt,omitempty"`
}
@@ -568,8 +568,8 @@ type TreasurySigner struct {
// value "inf" matches arbitrarily large operations. `Threshold` is the
// number of signer approvals required when the op's value falls in this tier.
type TreasuryTier struct {
MaxValue string `json:"maxValue"` // "100000", "1000000", "inf"
Threshold int `json:"threshold"` // e.g. 3, 4, 5
MaxValue string `json:"maxValue"` // "100000", "1000000", "inf"
Threshold int `json:"threshold"` // e.g. 3, 4, 5
RequireRegulator bool `json:"requireRegulator,omitempty"`
}
+2 -2
View File
@@ -199,8 +199,8 @@ func (g *GCPProvider) kmsSign(ctx context.Context, message []byte) ([]byte, erro
crc := crc32.Checksum(message, crc32.MakeTable(crc32.Castagnoli))
result, err := g.kmsClient.AsymmetricSign(ctx, &kmspb.AsymmetricSignRequest{
Name: g.keyVersionName(),
Data: message,
Name: g.keyVersionName(),
Data: message,
DataCrc32C: &kmspb.Int64Value{Value: int64(crc)},
})
if err != nil {
+5 -5
View File
@@ -56,9 +56,9 @@ type Config struct {
// FileConfig configures the local filesystem provider.
type FileConfig struct {
BasePath string `yaml:"base_path" mapstructure:"base_path"` // directory containing key files
Encrypted bool `yaml:"encrypted" mapstructure:"encrypted"` // whether keys are age-encrypted
HexEncoded bool `yaml:"hex_encoded" mapstructure:"hex_encoded"` // whether key files contain hex (default true)
BasePath string `yaml:"base_path" mapstructure:"base_path"` // directory containing key files
Encrypted bool `yaml:"encrypted" mapstructure:"encrypted"` // whether keys are age-encrypted
HexEncoded bool `yaml:"hex_encoded" mapstructure:"hex_encoded"` // whether key files contain hex (default true)
}
// AWSConfig configures the AWS KMS / CloudHSM provider.
@@ -73,7 +73,7 @@ type AWSConfig struct {
// GCPConfig configures the Google Cloud KMS provider.
type GCPConfig struct {
Project string `yaml:"project" mapstructure:"project"`
Location string `yaml:"location" mapstructure:"location"` // e.g. "global", "us-east1"
Location string `yaml:"location" mapstructure:"location"` // e.g. "global", "us-east1"
KeyRing string `yaml:"key_ring" mapstructure:"key_ring"`
KeyName string `yaml:"key_name" mapstructure:"key_name"`
HSMLevel string `yaml:"hsm_level" mapstructure:"hsm_level"` // "SOFTWARE" or "HSM" (default HSM)
@@ -81,7 +81,7 @@ type GCPConfig struct {
// AzureConfig configures the Azure Key Vault / Managed HSM provider.
type AzureConfig struct {
VaultURL string `yaml:"vault_url" mapstructure:"vault_url"` // e.g. "https://mpc-hsm.managedhsm.azure.net"
VaultURL string `yaml:"vault_url" mapstructure:"vault_url"` // e.g. "https://mpc-hsm.managedhsm.azure.net"
KeyName string `yaml:"key_name" mapstructure:"key_name"`
TenantID string `yaml:"tenant_id" mapstructure:"tenant_id"`
ClientID string `yaml:"client_id" mapstructure:"client_id"`
+1 -1
View File
@@ -187,7 +187,7 @@ func announce(ctx context.Context, cfg Config, id *Identity) error {
return nil
}
body, err := json.Marshal(map[string]any{
"node_id": id.NodeID,
"node_id": id.NodeID,
"public_key": hex.EncodeToString(id.PublicKey),
"ts": time.Now().UTC().Format(time.RFC3339Nano),
})
+17 -17
View File
@@ -54,11 +54,11 @@ type ConsensusKV struct {
pendingCh chan struct{}
// Node identity (dual keys)
nodeID string
edPrivKey ed25519.PrivateKey // Classical signing
edPubKey ed25519.PublicKey
pqPrivKey *mldsa.PrivateKey // Post-quantum signing (ML-DSA-65)
pqPubKey *mldsa.PublicKey
nodeID string
edPrivKey ed25519.PrivateKey // Classical signing
edPubKey ed25519.PublicKey
pqPrivKey *mldsa.PrivateKey // Post-quantum signing (ML-DSA-65)
pqPubKey *mldsa.PublicKey
// Optional KMS/HSM signer (overrides local keys)
signer Signer
@@ -66,7 +66,7 @@ type ConsensusKV struct {
// Validator set
validators map[string]*ValidatorKeys // nodeID -> dual keys
threshold int // votes for finality
threshold int // votes for finality
// Chain state
blockHeight uint64
@@ -112,8 +112,8 @@ type ConsensusBlock struct {
Timestamp time.Time `json:"timestamp"`
Mutations []KVMutation `json:"mutations"`
ProposerID string `json:"proposer_id"`
EdSig []byte `json:"ed_sig"` // Ed25519 proposer signature
PQSig []byte `json:"pq_sig"` // ML-DSA-65 proposer signature
EdSig []byte `json:"ed_sig"` // Ed25519 proposer signature
PQSig []byte `json:"pq_sig"` // ML-DSA-65 proposer signature
}
// BlockHash computes the deterministic hash of a block (excludes signatures).
@@ -166,12 +166,12 @@ type Signer interface {
// ConsensusKVConfig configures the private blockchain consensus.
type ConsensusKVConfig struct {
NodeID string
EdPrivateKey ed25519.PrivateKey // Classical identity key (nil if using Signer)
PQPrivateKey *mldsa.PrivateKey // Post-quantum identity key (nil = auto-generate)
Signer Signer // Optional KMS/HSM signer (overrides local keys)
SignerKeyID string // Key ID for Signer
Validators map[string]*ValidatorKeys // All cluster nodes
Threshold int // Votes for finality (default: floor(n/2)+1)
EdPrivateKey ed25519.PrivateKey // Classical identity key (nil if using Signer)
PQPrivateKey *mldsa.PrivateKey // Post-quantum identity key (nil = auto-generate)
Signer Signer // Optional KMS/HSM signer (overrides local keys)
SignerKeyID string // Key ID for Signer
Validators map[string]*ValidatorKeys // All cluster nodes
Threshold int // Votes for finality (default: floor(n/2)+1)
NATSConn *nats.Conn
ChainID string // Default "mpc"
ProposalInterval time.Duration // Default 50ms
@@ -743,10 +743,10 @@ func (c *ConsensusKV) LoadSnapshot(snap map[string][]byte, height uint64) error
return nil
}
func (c *ConsensusKV) Height() uint64 { return c.blockHeight }
func (c *ConsensusKV) Height() uint64 { return c.blockHeight }
func (c *ConsensusKV) LastBlockHash() [32]byte { return c.lastBlockHash }
func (c *ConsensusKV) ValidatorCount() int { return len(c.validators) }
func (c *ConsensusKV) Threshold() int { return c.threshold }
func (c *ConsensusKV) ValidatorCount() int { return len(c.validators) }
func (c *ConsensusKV) Threshold() int { return c.threshold }
// PQPublicKey returns this node's ML-DSA-65 public key bytes (for registration).
func (c *ConsensusKV) PQPublicKey() []byte {
+1 -1
View File
@@ -310,7 +310,7 @@ func TestConsensusKV_PQKeyGeneration(t *testing.T) {
priv, err := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
require.NoError(t, err)
assert.Equal(t, mldsa.MLDSA65PublicKeySize, len(priv.PublicKey.Bytes())) // 1,952 bytes
assert.Equal(t, mldsa.MLDSA65PublicKeySize, len(priv.PublicKey.Bytes())) // 1,952 bytes
assert.Equal(t, mldsa.MLDSA65PrivateKeySize, len(priv.Bytes())) // 4,032 bytes
// Sign and verify
-1
View File
@@ -445,4 +445,3 @@ func (s *SQLiteMeta) Audit(entry AuditEntry) error {
func (s *SQLiteMeta) Close() error {
return s.db.Close()
}
+5 -5
View File
@@ -57,19 +57,19 @@ type CanonicalIntent struct {
SessionID string `cbor:"session_id"`
WalletID string `cbor:"wallet_id"`
WalletTier WalletTier `cbor:"wallet_tier"`
Chain string `cbor:"chain"` // CAIP-2 e.g. "eip155:1"
Chain string `cbor:"chain"` // CAIP-2 e.g. "eip155:1"
Asset string `cbor:"asset"`
From string `cbor:"from"`
To string `cbor:"to"`
Amount string `cbor:"amount"` // arbitrary-precision decimal as string
Amount string `cbor:"amount"` // arbitrary-precision decimal as string
MaxFee string `cbor:"max_fee"`
Nonce string `cbor:"nonce"`
CalldataHash [32]byte `cbor:"calldata_hash"` // keccak256 / sha256 of contract calldata
CalldataHash [32]byte `cbor:"calldata_hash"` // keccak256 / sha256 of contract calldata
HumanSummary string `cbor:"human_summary"`
PolicyID string `cbor:"policy_id"`
PolicyHash [32]byte `cbor:"policy_hash"` // hash of the active policy bundle this intent was approved against
PolicyHash [32]byte `cbor:"policy_hash"` // hash of the active policy bundle this intent was approved against
RiskVerdictID string `cbor:"risk_verdict_id"`
SimulationHash [32]byte `cbor:"simulation_hash"` // hash of the EVM simulation result
SimulationHash [32]byte `cbor:"simulation_hash"` // hash of the EVM simulation result
ExpiresAt time.Time `cbor:"expires_at"`
Approvals []ApprovalSignature `cbor:"approvals"` // executive sigs over the digest; excluded from digest
NodeAttestations []NodeAttestation `cbor:"node_attestations"` // per-node verifier decisions; excluded from digest