From 2dfd8b5a8fea1621578b4140b462f07891a818ce Mon Sep 17 00:00:00 2001 From: Prad Nukala Date: Sat, 10 Jan 2026 14:32:21 -0500 Subject: [PATCH] feat(enclave): add MPC enclave generation support --- cmd/enclave/main.go | 571 +++++---------------------- internal/keybase/actions.go | 17 +- internal/keybase/actions_account.go | 5 +- internal/keybase/actions_keyshare.go | 206 ---------- internal/keybase/conn.go | 268 ------------- internal/keybase/models.go | 110 +++++- internal/keybase/querier.go | 26 +- internal/keybase/query.sql.go | 455 +++++++++------------ internal/migrations/query.sql | 55 +-- internal/migrations/schema.sql | 82 ++-- internal/types/generate.go | 34 +- 11 files changed, 487 insertions(+), 1342 deletions(-) delete mode 100644 internal/keybase/actions_keyshare.go delete mode 100644 internal/keybase/conn.go diff --git a/cmd/enclave/main.go b/cmd/enclave/main.go index 3e75062..b252678 100644 --- a/cmd/enclave/main.go +++ b/cmd/enclave/main.go @@ -5,17 +5,19 @@ package main import ( "context" "encoding/base64" - "encoding/hex" "encoding/json" "errors" "fmt" "strings" + "enclave/internal/crypto/bip44" + "enclave/internal/crypto/mpc" "enclave/internal/keybase" "enclave/internal/state" "enclave/internal/types" "github.com/extism/go-pdk" + "github.com/sonr-io/crypto/core/protocol" ) func main() { state.Default() } @@ -51,7 +53,7 @@ func ping() int32 { //go:wasmexport generate func generate() int32 { - pdk.Log(pdk.LogInfo, "generate: starting database initialization") + pdk.Log(pdk.LogInfo, "generate: starting database initialization with MPC") var input types.GenerateInput if err := pdk.InputJSON(&input); err != nil { @@ -70,9 +72,9 @@ func generate() int32 { return 1 } - result, err := initializeDatabase(credentialBytes, input.KeyShare) + result, err := initializeWithMPC(credentialBytes) if err != nil { - pdk.SetError(fmt.Errorf("generate: failed to initialize database: %w", err)) + pdk.SetError(fmt.Errorf("generate: failed to initialize: %w", err)) return 1 } @@ -86,10 +88,11 @@ func generate() int32 { } output := types.GenerateOutput{ - DID: result.DID, - Database: dbBytes, - KeyShareID: result.KeyShareID, - Account: result.Account, + DID: result.DID, + Database: dbBytes, + EnclaveID: result.EnclaveID, + PublicKey: result.PublicKey, + Accounts: result.Accounts, } if err := pdk.OutputJSON(output); err != nil { @@ -97,7 +100,7 @@ func generate() int32 { return 1 } - pdk.Log(pdk.LogInfo, fmt.Sprintf("generate: created DID %s", result.DID)) + pdk.Log(pdk.LogInfo, fmt.Sprintf("generate: created DID %s with enclave %s", result.DID, result.EnclaveID)) return 0 } @@ -244,12 +247,13 @@ func query() int32 { } type initResult struct { - DID string - KeyShareID string - Account *types.AccountInfo + DID string + EnclaveID string + PublicKey string + Accounts []types.AccountInfo } -func initializeDatabase(credentialBytes []byte, keyShareInput *types.KeyShareInput) (*initResult, error) { +func initializeWithMPC(credentialBytes []byte) (*initResult, error) { kb, err := keybase.Open() if err != nil { return nil, fmt.Errorf("open database: %w", err) @@ -261,88 +265,95 @@ func initializeDatabase(credentialBytes []byte, keyShareInput *types.KeyShareInp return nil, fmt.Errorf("initialize: %w", err) } - result := &initResult{DID: did} - - if keyShareInput != nil { - keyShareID, account, err := createInitialKeyShare(ctx, keyShareInput) - if err != nil { - pdk.Log(pdk.LogWarn, fmt.Sprintf("initializeDatabase: failed to create keyshare: %s", err)) - } else { - result.KeyShareID = keyShareID - result.Account = account - pdk.Log(pdk.LogInfo, fmt.Sprintf("initializeDatabase: created keyshare %s", keyShareID)) - } + pdk.Log(pdk.LogInfo, "initializeWithMPC: generating MPC enclave") + enclave, err := mpc.NewEnclave() + if err != nil { + return nil, fmt.Errorf("generate MPC enclave: %w", err) } - pdk.Log(pdk.LogDebug, "initializeDatabase: created schema and initial records") - return result, nil -} + enclaveData := enclave.GetData() + enclaveID := fmt.Sprintf("enc_%x", credentialBytes[:8]) + + valShareStr, err := protocol.EncodeMessage(enclaveData.ValShare) + if err != nil { + return nil, fmt.Errorf("encode val share: %w", err) + } + userShareStr, err := protocol.EncodeMessage(enclaveData.UserShare) + if err != nil { + return nil, fmt.Errorf("encode user share: %w", err) + } -func createInitialKeyShare(ctx context.Context, input *types.KeyShareInput) (string, *types.AccountInfo, error) { am, err := keybase.NewActionManager() if err != nil { - return "", nil, fmt.Errorf("action manager: %w", err) + return nil, fmt.Errorf("action manager: %w", err) } - ks, err := am.CreateKeyShare(ctx, keybase.NewKeyShareInput{ - KeyID: input.KeyID, - PartyIndex: input.PartyIndex, - Threshold: input.Threshold, - TotalParties: input.TotalParties, - Curve: input.Curve, - ShareData: input.ShareData, - PublicKey: input.PublicKey, - ChainCode: input.ChainCode, - DerivationPath: input.DerivationPath, + enc, err := am.CreateEnclave(ctx, keybase.NewEnclaveInput{ + EnclaveID: enclaveID, + PublicKeyHex: enclaveData.PubHex, + PublicKey: enclaveData.PubBytes, + ValShare: []byte(valShareStr), + UserShare: []byte(userShareStr), + Nonce: enclaveData.Nonce, + Curve: string(enclaveData.Curve), }) if err != nil { - return "", nil, fmt.Errorf("create keyshare: %w", err) + return nil, fmt.Errorf("store enclave: %w", err) } - account, err := createInitialAccount(ctx, am, ks.ID, input.PublicKey) + pdk.Log(pdk.LogInfo, fmt.Sprintf("initializeWithMPC: stored enclave %s", enclaveID)) + + accounts, err := createDefaultAccounts(ctx, am, enc.ID, enclaveData.PubBytes) if err != nil { - pdk.Log(pdk.LogWarn, fmt.Sprintf("createInitialKeyShare: failed to create account: %s", err)) - return ks.ShareID, nil, nil + pdk.Log(pdk.LogWarn, fmt.Sprintf("initializeWithMPC: failed to create accounts: %s", err)) + accounts = []types.AccountInfo{} } - return ks.ShareID, account, nil -} - -func createInitialAccount(ctx context.Context, am *keybase.ActionManager, keyShareID int64, publicKey string) (*types.AccountInfo, error) { - address := deriveCosmosAddress(publicKey) - if address == "" { - return nil, fmt.Errorf("failed to derive address from public key") - } - - acc, err := am.CreateAccount(ctx, keybase.NewAccountInput{ - KeyShareID: keyShareID, - Address: address, - ChainID: "sonr-testnet-1", - CoinType: 118, - AccountIndex: 0, - AddressIndex: 0, - Label: "Default Account", - }) - if err != nil { - return nil, fmt.Errorf("create account: %w", err) - } - - return &types.AccountInfo{ - Address: acc.Address, - ChainID: acc.ChainID, - CoinType: acc.CoinType, + return &initResult{ + DID: did, + EnclaveID: enclaveID, + PublicKey: enclaveData.PubHex, + Accounts: accounts, }, nil } -func deriveCosmosAddress(publicKeyHex string) string { - if publicKeyHex == "" { - return "" +func createDefaultAccounts(ctx context.Context, am *keybase.ActionManager, enclaveID int64, pubKeyBytes []byte) ([]types.AccountInfo, error) { + chains := []string{"bitcoin", "ethereum", "sonr"} + derivedAccounts, err := bip44.DeriveAccounts(pubKeyBytes, chains) + if err != nil { + return nil, fmt.Errorf("derive accounts: %w", err) } - pubBytes, err := hex.DecodeString(publicKeyHex) - if err != nil || len(pubBytes) < 20 { - return "" + + accounts := make([]types.AccountInfo, 0, len(derivedAccounts)) + for i, derived := range derivedAccounts { + isDefault := int64(0) + if i == 0 { + isDefault = 1 + } + + acc, err := am.CreateAccount(ctx, keybase.NewAccountInput{ + EnclaveID: enclaveID, + Address: derived.Address, + ChainID: derived.ChainID, + CoinType: int64(derived.CoinType), + AccountIndex: int64(derived.AccountIndex), + AddressIndex: int64(derived.AddressIndex), + Label: derived.ChainID, + IsDefault: isDefault, + }) + if err != nil { + pdk.Log(pdk.LogWarn, fmt.Sprintf("createDefaultAccounts: failed for %s: %s", derived.ChainID, err)) + continue + } + + accounts = append(accounts, types.AccountInfo{ + Address: acc.Address, + ChainID: acc.ChainID, + CoinType: acc.CoinType, + }) } - return fmt.Sprintf("snr1%x", pubBytes[:20]) + + return accounts, nil } func serializeDatabase() ([]byte, error) { @@ -523,61 +534,16 @@ func matchResource(pattern, resource string) bool { } func executeAction(params *types.FilterParams) (json.RawMessage, error) { - switch params.Resource { - case "accounts": - return executeAccountAction(params) - case "credentials": - return executeCredentialAction(params) - case "sessions": - return executeSessionAction(params) - case "grants": - return executeGrantAction(params) - case "key_shares": - return executeKeyShareAction(params) - case "ucans": - return executeUCANAction(params) - case "delegations": - return executeDelegationAction(params) - case "verification_methods": - return executeVerificationMethodAction(params) - case "services": - return executeServiceAction(params) - default: - return nil, fmt.Errorf("unknown resource: %s", params.Resource) - } -} - -func executeAccountAction(params *types.FilterParams) (json.RawMessage, error) { - am, err := keybase.NewActionManager() - if err != nil { - return nil, fmt.Errorf("action manager: %w", err) + if params.Resource == "accounts" { + switch params.Action { + case "balances": + return fetchAccountBalances(params.Subject) + case "sign": + return json.Marshal(map[string]string{"signature": "placeholder"}) + } } - ctx := context.Background() - - switch params.Action { - case "list": - accounts, err := am.ListAccounts(ctx) - if err != nil { - return nil, fmt.Errorf("list accounts: %w", err) - } - return json.Marshal(accounts) - case "get": - if params.Subject == "" { - return nil, errors.New("subject (address) required for get action") - } - account, err := am.GetAccountByAddress(ctx, params.Subject) - if err != nil { - return nil, fmt.Errorf("get account: %w", err) - } - return json.Marshal(account) - case "balances": - return fetchAccountBalances(params.Subject) - case "sign": - return json.Marshal(map[string]string{"signature": "placeholder"}) - default: - return nil, fmt.Errorf("unknown action for accounts: %s", params.Action) - } + return keybase.Exec(context.Background(), params.Resource, params.Action, params.Subject) } func fetchAccountBalances(address string) (json.RawMessage, error) { @@ -614,274 +580,6 @@ func fetchAccountBalances(address string) (json.RawMessage, error) { return body, nil } -func executeCredentialAction(params *types.FilterParams) (json.RawMessage, error) { - am, err := keybase.NewActionManager() - if err != nil { - return nil, fmt.Errorf("action manager: %w", err) - } - - ctx := context.Background() - - switch params.Action { - case "list": - credentials, err := am.ListCredentials(ctx) - if err != nil { - return nil, fmt.Errorf("list credentials: %w", err) - } - return json.Marshal(credentials) - case "get": - if params.Subject == "" { - return nil, errors.New("subject (credential_id) required for get action") - } - credential, err := am.GetCredentialByID(ctx, params.Subject) - if err != nil { - return nil, fmt.Errorf("get credential: %w", err) - } - return json.Marshal(credential) - default: - return nil, fmt.Errorf("unknown action for credentials: %s", params.Action) - } -} - -func executeSessionAction(params *types.FilterParams) (json.RawMessage, error) { - am, err := keybase.NewActionManager() - if err != nil { - return nil, fmt.Errorf("action manager: %w", err) - } - - ctx := context.Background() - - switch params.Action { - case "list": - sessions, err := am.ListSessions(ctx) - if err != nil { - return nil, fmt.Errorf("list sessions: %w", err) - } - return json.Marshal(sessions) - case "revoke": - if params.Subject == "" { - return nil, errors.New("subject (session_id) required for revoke action") - } - if err := am.RevokeSession(ctx, params.Subject); err != nil { - return nil, fmt.Errorf("revoke session: %w", err) - } - return json.Marshal(map[string]bool{"revoked": true}) - default: - return nil, fmt.Errorf("unknown action for sessions: %s", params.Action) - } -} - -func executeGrantAction(params *types.FilterParams) (json.RawMessage, error) { - am, err := keybase.NewActionManager() - if err != nil { - return nil, fmt.Errorf("action manager: %w", err) - } - - ctx := context.Background() - - switch params.Action { - case "list": - grants, err := am.ListGrants(ctx) - if err != nil { - return nil, fmt.Errorf("list grants: %w", err) - } - return json.Marshal(grants) - case "revoke": - if params.Subject == "" { - return nil, errors.New("subject (grant_id) required for revoke action") - } - var grantID int64 - if _, err := fmt.Sscanf(params.Subject, "%d", &grantID); err != nil { - return nil, fmt.Errorf("invalid grant_id: %w", err) - } - if err := am.RevokeGrant(ctx, grantID); err != nil { - return nil, fmt.Errorf("revoke grant: %w", err) - } - return json.Marshal(map[string]bool{"revoked": true}) - default: - return nil, fmt.Errorf("unknown action for grants: %s", params.Action) - } -} - -func executeKeyShareAction(params *types.FilterParams) (json.RawMessage, error) { - am, err := keybase.NewActionManager() - if err != nil { - return nil, fmt.Errorf("action manager: %w", err) - } - - ctx := context.Background() - - switch params.Action { - case "list": - shares, err := am.ListKeyShares(ctx) - if err != nil { - return nil, fmt.Errorf("list key shares: %w", err) - } - return json.Marshal(shares) - case "get": - if params.Subject == "" { - return nil, errors.New("subject (share_id) required for get action") - } - share, err := am.GetKeyShareByID(ctx, params.Subject) - if err != nil { - return nil, fmt.Errorf("get key share: %w", err) - } - return json.Marshal(share) - case "rotate": - if params.Subject == "" { - return nil, errors.New("subject (share_id) required for rotate action") - } - if err := am.RotateKeyShare(ctx, params.Subject); err != nil { - return nil, fmt.Errorf("rotate key share: %w", err) - } - return json.Marshal(map[string]bool{"rotated": true}) - case "archive": - if params.Subject == "" { - return nil, errors.New("subject (share_id) required for archive action") - } - if err := am.ArchiveKeyShare(ctx, params.Subject); err != nil { - return nil, fmt.Errorf("archive key share: %w", err) - } - return json.Marshal(map[string]bool{"archived": true}) - case "delete": - if params.Subject == "" { - return nil, errors.New("subject (share_id) required for delete action") - } - if err := am.DeleteKeyShare(ctx, params.Subject); err != nil { - return nil, fmt.Errorf("delete key share: %w", err) - } - return json.Marshal(map[string]bool{"deleted": true}) - default: - return nil, fmt.Errorf("unknown action for key_shares: %s", params.Action) - } -} - -func executeUCANAction(params *types.FilterParams) (json.RawMessage, error) { - am, err := keybase.NewActionManager() - if err != nil { - return nil, fmt.Errorf("action manager: %w", err) - } - - ctx := context.Background() - - switch params.Action { - case "list": - delegations, err := am.ListDelegations(ctx) - if err != nil { - return nil, fmt.Errorf("list delegations: %w", err) - } - return json.Marshal(delegations) - case "get": - if params.Subject == "" { - return nil, errors.New("subject (cid) required for get action") - } - delegation, err := am.GetDelegationByCID(ctx, params.Subject) - if err != nil { - return nil, fmt.Errorf("get delegation: %w", err) - } - return json.Marshal(delegation) - case "revoke": - if params.Subject == "" { - return nil, errors.New("subject (cid) required for revoke action") - } - if err := am.RevokeDelegation(ctx, keybase.RevokeDelegationParams{ - DelegationCID: params.Subject, - RevokedBy: state.GetDID(), - Reason: "user revoked", - }); err != nil { - return nil, fmt.Errorf("revoke delegation: %w", err) - } - return json.Marshal(map[string]bool{"revoked": true}) - case "verify": - if params.Subject == "" { - return nil, errors.New("subject (cid) required for verify action") - } - revoked, err := am.IsDelegationRevoked(ctx, params.Subject) - if err != nil { - return nil, fmt.Errorf("check delegation: %w", err) - } - return json.Marshal(map[string]bool{"valid": !revoked, "revoked": revoked}) - case "cleanup": - if err := am.CleanExpiredDelegations(ctx); err != nil { - return nil, fmt.Errorf("cleanup delegations: %w", err) - } - return json.Marshal(map[string]bool{"cleaned": true}) - default: - return nil, fmt.Errorf("unknown action for ucans: %s", params.Action) - } -} - -func executeDelegationAction(params *types.FilterParams) (json.RawMessage, error) { - am, err := keybase.NewActionManager() - if err != nil { - return nil, fmt.Errorf("action manager: %w", err) - } - - ctx := context.Background() - - switch params.Action { - case "list": - if params.Subject == "" { - return nil, errors.New("subject (issuer DID) required for list action") - } - delegations, err := am.ListDelegationsByIssuer(ctx, params.Subject) - if err != nil { - return nil, fmt.Errorf("list delegations: %w", err) - } - return json.Marshal(delegations) - case "list_received": - if params.Subject == "" { - return nil, errors.New("subject (audience DID) required for list_received action") - } - delegations, err := am.ListDelegationsByAudience(ctx, params.Subject) - if err != nil { - return nil, fmt.Errorf("list received delegations: %w", err) - } - return json.Marshal(delegations) - case "list_command": - if params.Subject == "" { - return nil, errors.New("subject (command) required for list_command action") - } - delegations, err := am.ListDelegationsForCommand(ctx, params.Subject) - if err != nil { - return nil, fmt.Errorf("list delegations for command: %w", err) - } - return json.Marshal(delegations) - case "get": - if params.Subject == "" { - return nil, errors.New("subject (cid) required for get action") - } - delegation, err := am.GetDelegationByCID(ctx, params.Subject) - if err != nil { - return nil, fmt.Errorf("get delegation: %w", err) - } - return json.Marshal(delegation) - case "revoke": - if params.Subject == "" { - return nil, errors.New("subject (cid) required for revoke action") - } - if err := am.RevokeDelegation(ctx, keybase.RevokeDelegationParams{ - DelegationCID: params.Subject, - RevokedBy: state.GetDID(), - Reason: "user revoked", - }); err != nil { - return nil, fmt.Errorf("revoke delegation: %w", err) - } - return json.Marshal(map[string]bool{"revoked": true}) - case "verify": - if params.Subject == "" { - return nil, errors.New("subject (cid) required for verify action") - } - revoked, err := am.IsDelegationRevoked(ctx, params.Subject) - if err != nil { - return nil, fmt.Errorf("check delegation: %w", err) - } - return json.Marshal(map[string]bool{"valid": !revoked, "revoked": revoked}) - default: - return nil, fmt.Errorf("unknown action for delegations: %s", params.Action) - } -} - func resolveDID(did string) (*types.QueryOutput, error) { am, err := keybase.NewActionManager() if err != nil { @@ -939,82 +637,3 @@ func resolveDID(did string) (*types.QueryOutput, error) { Credentials: credentials, }, nil } - -func executeVerificationMethodAction(params *types.FilterParams) (json.RawMessage, error) { - am, err := keybase.NewActionManager() - if err != nil { - return nil, fmt.Errorf("action manager: %w", err) - } - - ctx := context.Background() - - switch params.Action { - case "list": - vms, err := am.ListVerificationMethodsFull(ctx) - if err != nil { - return nil, fmt.Errorf("list verification methods: %w", err) - } - return json.Marshal(vms) - case "get": - if params.Subject == "" { - return nil, errors.New("subject (method_id) required for get action") - } - vm, err := am.GetVerificationMethod(ctx, params.Subject) - if err != nil { - return nil, fmt.Errorf("get verification method: %w", err) - } - return json.Marshal(vm) - case "delete": - if params.Subject == "" { - return nil, errors.New("subject (method_id) required for delete action") - } - if err := am.DeleteVerificationMethod(ctx, params.Subject); err != nil { - return nil, fmt.Errorf("delete verification method: %w", err) - } - return json.Marshal(map[string]bool{"deleted": true}) - default: - return nil, fmt.Errorf("unknown action for verification_methods: %s", params.Action) - } -} - -func executeServiceAction(params *types.FilterParams) (json.RawMessage, error) { - am, err := keybase.NewActionManager() - if err != nil { - return nil, fmt.Errorf("action manager: %w", err) - } - - ctx := context.Background() - - switch params.Action { - case "list": - services, err := am.ListVerifiedServices(ctx) - if err != nil { - return nil, fmt.Errorf("list verified services: %w", err) - } - return json.Marshal(services) - case "get": - if params.Subject == "" { - return nil, errors.New("subject (origin) required for get action") - } - svc, err := am.GetServiceByOrigin(ctx, params.Subject) - if err != nil { - return nil, fmt.Errorf("get service: %w", err) - } - return json.Marshal(svc) - case "get_by_id": - if params.Subject == "" { - return nil, errors.New("subject (service_id) required for get_by_id action") - } - var serviceID int64 - if _, err := fmt.Sscanf(params.Subject, "%d", &serviceID); err != nil { - return nil, fmt.Errorf("invalid service_id: %w", err) - } - svc, err := am.GetServiceByID(ctx, serviceID) - if err != nil { - return nil, fmt.Errorf("get service by ID: %w", err) - } - return json.Marshal(svc) - default: - return nil, fmt.Errorf("unknown action for services: %s", params.Action) - } -} diff --git a/internal/keybase/actions.go b/internal/keybase/actions.go index 54b6dd9..4bddbe3 100644 --- a/internal/keybase/actions.go +++ b/internal/keybase/actions.go @@ -72,7 +72,7 @@ func (am *ActionManager) ListAccounts(ctx context.Context) ([]AccountResult, err AddressIndex: row.AddressIndex, Label: label, IsDefault: row.IsDefault == 1, - PublicKey: row.SharePublicKey, + PublicKey: row.PublicKeyHex, Curve: row.Curve, CreatedAt: row.CreatedAt, } @@ -245,12 +245,19 @@ func (am *ActionManager) ListSessions(ctx context.Context) ([]SessionResult, err authenticator = *sess.Authenticator } + var deviceInfo json.RawMessage + if sess.DeviceInfo != nil { + deviceInfo = json.RawMessage(*sess.DeviceInfo) + } else { + deviceInfo = json.RawMessage(`{}`) + } + results[i] = SessionResult{ ID: sess.ID, SessionID: sess.SessionID, DeviceName: sess.DeviceName, Authenticator: authenticator, - DeviceInfo: sess.DeviceInfo, + DeviceInfo: deviceInfo, IsCurrent: sess.IsCurrent == 1, LastActivity: sess.LastActivity, ExpiresAt: sess.ExpiresAt, @@ -386,8 +393,8 @@ func (am *ActionManager) ListGrants(ctx context.Context) ([]GrantResult, error) ServiceName: g.ServiceName, ServiceOrigin: g.ServiceOrigin, ServiceLogo: serviceLogo, - Scopes: g.Scopes, - Accounts: g.Accounts, + Scopes: json.RawMessage(g.Scopes), + Accounts: json.RawMessage(g.Accounts), Status: g.Status, GrantedAt: g.GrantedAt, LastUsed: lastUsed, @@ -481,7 +488,7 @@ func (am *ActionManager) ResolveDID(ctx context.Context, did string) (*DIDDocume AddressIndex: row.AddressIndex, Label: label, IsDefault: row.IsDefault == 1, - PublicKey: row.SharePublicKey, + PublicKey: row.PublicKeyHex, Curve: row.Curve, CreatedAt: row.CreatedAt, } diff --git a/internal/keybase/actions_account.go b/internal/keybase/actions_account.go index 8c139c7..4265bfb 100644 --- a/internal/keybase/actions_account.go +++ b/internal/keybase/actions_account.go @@ -6,13 +6,14 @@ import ( ) type NewAccountInput struct { - KeyShareID int64 `json:"key_share_id"` + EnclaveID int64 `json:"enclave_id"` Address string `json:"address"` ChainID string `json:"chain_id"` CoinType int64 `json:"coin_type"` AccountIndex int64 `json:"account_index"` AddressIndex int64 `json:"address_index"` Label string `json:"label,omitempty"` + IsDefault int64 `json:"is_default,omitempty"` } func (am *ActionManager) CreateAccount(ctx context.Context, params NewAccountInput) (*AccountResult, error) { @@ -30,7 +31,7 @@ func (am *ActionManager) CreateAccount(ctx context.Context, params NewAccountInp acc, err := am.kb.queries.CreateAccount(ctx, CreateAccountParams{ DidID: am.kb.didID, - KeyShareID: params.KeyShareID, + EnclaveID: params.EnclaveID, Address: params.Address, ChainID: params.ChainID, CoinType: params.CoinType, diff --git a/internal/keybase/actions_keyshare.go b/internal/keybase/actions_keyshare.go deleted file mode 100644 index 974df80..0000000 --- a/internal/keybase/actions_keyshare.go +++ /dev/null @@ -1,206 +0,0 @@ -package keybase - -import ( - "context" - "crypto/rand" - "encoding/hex" - "fmt" -) - -type KeyShareResult struct { - ID int64 `json:"id"` - ShareID string `json:"share_id"` - KeyID string `json:"key_id"` - PartyIndex int64 `json:"party_index"` - Threshold int64 `json:"threshold"` - TotalParties int64 `json:"total_parties"` - Curve string `json:"curve"` - PublicKey string `json:"public_key"` - ChainCode string `json:"chain_code,omitempty"` - DerivationPath string `json:"derivation_path,omitempty"` - Status string `json:"status"` - CreatedAt string `json:"created_at"` - RotatedAt string `json:"rotated_at,omitempty"` -} - -type NewKeyShareInput struct { - KeyID string `json:"key_id"` - PartyIndex int64 `json:"party_index"` - Threshold int64 `json:"threshold"` - TotalParties int64 `json:"total_parties"` - Curve string `json:"curve"` - ShareData string `json:"share_data"` - PublicKey string `json:"public_key"` - ChainCode string `json:"chain_code,omitempty"` - DerivationPath string `json:"derivation_path,omitempty"` -} - -func (am *ActionManager) CreateKeyShare(ctx context.Context, params NewKeyShareInput) (*KeyShareResult, error) { - am.kb.mu.Lock() - defer am.kb.mu.Unlock() - - if am.kb.didID == 0 { - return nil, fmt.Errorf("DID not initialized") - } - - shareID := generateShareID() - - var chainCode, derivationPath *string - if params.ChainCode != "" { - chainCode = ¶ms.ChainCode - } - if params.DerivationPath != "" { - derivationPath = ¶ms.DerivationPath - } - - ks, err := am.kb.queries.CreateKeyShare(ctx, CreateKeyShareParams{ - DidID: am.kb.didID, - ShareID: shareID, - KeyID: params.KeyID, - PartyIndex: params.PartyIndex, - Threshold: params.Threshold, - TotalParties: params.TotalParties, - Curve: params.Curve, - ShareData: params.ShareData, - PublicKey: params.PublicKey, - ChainCode: chainCode, - DerivationPath: derivationPath, - }) - if err != nil { - return nil, fmt.Errorf("create key share: %w", err) - } - - return keyShareToResult(&ks), nil -} - -func (am *ActionManager) ListKeyShares(ctx context.Context) ([]KeyShareResult, error) { - am.kb.mu.RLock() - defer am.kb.mu.RUnlock() - - if am.kb.didID == 0 { - return []KeyShareResult{}, nil - } - - shares, err := am.kb.queries.ListKeySharesByDID(ctx, am.kb.didID) - if err != nil { - return nil, fmt.Errorf("list key shares: %w", err) - } - - results := make([]KeyShareResult, len(shares)) - for i, ks := range shares { - results[i] = *keyShareToResult(&ks) - } - - return results, nil -} - -func (am *ActionManager) GetKeyShareByID(ctx context.Context, shareID string) (*KeyShareResult, error) { - am.kb.mu.RLock() - defer am.kb.mu.RUnlock() - - ks, err := am.kb.queries.GetKeyShareByID(ctx, shareID) - if err != nil { - return nil, fmt.Errorf("get key share: %w", err) - } - - return keyShareToResult(&ks), nil -} - -func (am *ActionManager) GetKeyShareByKeyID(ctx context.Context, keyID string) (*KeyShareResult, error) { - am.kb.mu.RLock() - defer am.kb.mu.RUnlock() - - if am.kb.didID == 0 { - return nil, fmt.Errorf("DID not initialized") - } - - ks, err := am.kb.queries.GetKeyShareByKeyID(ctx, GetKeyShareByKeyIDParams{ - DidID: am.kb.didID, - KeyID: keyID, - }) - if err != nil { - return nil, fmt.Errorf("get key share by key ID: %w", err) - } - - return keyShareToResult(&ks), nil -} - -func (am *ActionManager) RotateKeyShare(ctx context.Context, shareID string) error { - am.kb.mu.Lock() - defer am.kb.mu.Unlock() - - ks, err := am.kb.queries.GetKeyShareByID(ctx, shareID) - if err != nil { - return fmt.Errorf("get key share: %w", err) - } - - return am.kb.queries.RotateKeyShare(ctx, ks.ID) -} - -func (am *ActionManager) ArchiveKeyShare(ctx context.Context, shareID string) error { - am.kb.mu.Lock() - defer am.kb.mu.Unlock() - - ks, err := am.kb.queries.GetKeyShareByID(ctx, shareID) - if err != nil { - return fmt.Errorf("get key share: %w", err) - } - - return am.kb.queries.ArchiveKeyShare(ctx, ks.ID) -} - -func (am *ActionManager) DeleteKeyShare(ctx context.Context, shareID string) error { - am.kb.mu.Lock() - defer am.kb.mu.Unlock() - - if am.kb.didID == 0 { - return fmt.Errorf("DID not initialized") - } - - ks, err := am.kb.queries.GetKeyShareByID(ctx, shareID) - if err != nil { - return fmt.Errorf("get key share: %w", err) - } - - return am.kb.queries.DeleteKeyShare(ctx, DeleteKeyShareParams{ - ID: ks.ID, - DidID: am.kb.didID, - }) -} - -func generateShareID() string { - b := make([]byte, 16) - rand.Read(b) - return "ks_" + hex.EncodeToString(b) -} - -func keyShareToResult(ks *KeyShare) *KeyShareResult { - chainCode := "" - if ks.ChainCode != nil { - chainCode = *ks.ChainCode - } - derivationPath := "" - if ks.DerivationPath != nil { - derivationPath = *ks.DerivationPath - } - rotatedAt := "" - if ks.RotatedAt != nil { - rotatedAt = *ks.RotatedAt - } - - return &KeyShareResult{ - ID: ks.ID, - ShareID: ks.ShareID, - KeyID: ks.KeyID, - PartyIndex: ks.PartyIndex, - Threshold: ks.Threshold, - TotalParties: ks.TotalParties, - Curve: ks.Curve, - PublicKey: ks.PublicKey, - ChainCode: chainCode, - DerivationPath: derivationPath, - Status: ks.Status, - CreatedAt: ks.CreatedAt, - RotatedAt: rotatedAt, - } -} diff --git a/internal/keybase/conn.go b/internal/keybase/conn.go deleted file mode 100644 index 43ed1c2..0000000 --- a/internal/keybase/conn.go +++ /dev/null @@ -1,268 +0,0 @@ -package keybase - -import ( - "context" - "database/sql" - "encoding/json" - "fmt" - "sync" - - "enclave/internal/migrations" - - "github.com/ncruces/go-sqlite3" - "github.com/ncruces/go-sqlite3/driver" - _ "github.com/ncruces/go-sqlite3/embed" - "github.com/ncruces/go-sqlite3/ext/hash" - "github.com/ncruces/go-sqlite3/ext/serdes" - "github.com/ncruces/go-sqlite3/ext/uuid" -) - -// Keybase encapsulates the encrypted key storage database. -type Keybase struct { - db *sql.DB - conn *sqlite3.Conn // raw connection for serdes - queries *Queries - did string - didID int64 - mu sync.RWMutex -} - -var ( - instance *Keybase - initMu sync.Mutex -) - -// Open creates or returns the singleton Keybase instance with an in-memory database. -func Open() (*Keybase, error) { - initMu.Lock() - defer initMu.Unlock() - - if instance != nil { - return instance, nil - } - - var rawConn *sqlite3.Conn - initCallback := func(conn *sqlite3.Conn) error { - rawConn = conn - if err := hash.Register(conn); err != nil { - return fmt.Errorf("register hash extension: %w", err) - } - if err := uuid.Register(conn); err != nil { - return fmt.Errorf("register uuid extension: %w", err) - } - return nil - } - - db, err := driver.Open(":memory:", initCallback) - if err != nil { - return nil, fmt.Errorf("keybase: open database: %w", err) - } - - if _, err := db.Exec(migrations.SchemaSQL); err != nil { - db.Close() - return nil, fmt.Errorf("keybase: init schema: %w", err) - } - - instance = &Keybase{ - db: db, - conn: rawConn, - queries: New(db), - } - - return instance, nil -} - -// Get returns the existing Keybase instance or nil if not initialized. -func Get() *Keybase { - initMu.Lock() - defer initMu.Unlock() - return instance -} - -// MustGet returns the existing Keybase instance or panics if not initialized. -func MustGet() *Keybase { - kb := Get() - if kb == nil { - panic("keybase: not initialized") - } - return kb -} - -// Close closes the database connection and clears the singleton. -func Close() error { - initMu.Lock() - defer initMu.Unlock() - - if instance == nil { - return nil - } - - err := instance.db.Close() - instance = nil - return err -} - -// Reset clears the singleton instance (useful for testing). -func Reset() { - initMu.Lock() - defer initMu.Unlock() - - if instance != nil { - instance.db.Close() - instance = nil - } -} - -// DB returns the underlying sql.DB connection. -func (k *Keybase) DB() *sql.DB { - k.mu.RLock() - defer k.mu.RUnlock() - return k.db -} - -// Queries returns the SQLC-generated query interface. -func (k *Keybase) Queries() *Queries { - k.mu.RLock() - defer k.mu.RUnlock() - return k.queries -} - -// DID returns the current DID identifier. -func (k *Keybase) DID() string { - k.mu.RLock() - defer k.mu.RUnlock() - return k.did -} - -// DIDID returns the database ID of the current DID. -func (k *Keybase) DIDID() int64 { - k.mu.RLock() - defer k.mu.RUnlock() - return k.didID -} - -// IsInitialized returns true if a DID has been set. -func (k *Keybase) IsInitialized() bool { - k.mu.RLock() - defer k.mu.RUnlock() - return k.did != "" -} - -// SetDID sets the current DID context. -func (k *Keybase) SetDID(did string, didID int64) { - k.mu.Lock() - defer k.mu.Unlock() - k.did = did - k.didID = didID -} - -// Initialize creates a new DID document from a WebAuthn credential. -func (k *Keybase) Initialize(ctx context.Context, credentialBytes []byte) (string, error) { - k.mu.Lock() - defer k.mu.Unlock() - - did := fmt.Sprintf("did:sonr:%x", credentialBytes[:16]) - docJSON, _ := json.Marshal(map[string]any{ - "@context": []string{"https://www.w3.org/ns/did/v1"}, - "id": did, - }) - - doc, err := k.queries.CreateDID(ctx, CreateDIDParams{ - Did: did, - Controller: did, - Document: docJSON, - Sequence: 0, - }) - if err != nil { - return "", fmt.Errorf("keybase: create DID: %w", err) - } - - k.did = did - k.didID = doc.ID - - return did, nil -} - -// Load restores the database state from serialized bytes and sets the current DID. -func (k *Keybase) Load(ctx context.Context, data []byte) (string, error) { - if len(data) < 100 { - return "", fmt.Errorf("keybase: invalid database format") - } - - k.mu.Lock() - defer k.mu.Unlock() - - if k.conn == nil { - return "", fmt.Errorf("keybase: database not initialized") - } - - if err := serdes.Deserialize(k.conn, "main", data); err != nil { - return "", fmt.Errorf("keybase: deserialize database: %w", err) - } - - docs, err := k.queries.ListAllDIDs(ctx) - if err != nil { - return "", fmt.Errorf("keybase: list DIDs: %w", err) - } - - if len(docs) == 0 { - return "", fmt.Errorf("keybase: no DID found in database") - } - - k.did = docs[0].Did - k.didID = docs[0].ID - - return k.did, nil -} - -// Serialize exports the database state as bytes using native SQLite serialization. -func (k *Keybase) Serialize() ([]byte, error) { - k.mu.RLock() - defer k.mu.RUnlock() - - if k.conn == nil { - return nil, fmt.Errorf("keybase: database not initialized") - } - - return serdes.Serialize(k.conn, "main") -} - -func (k *Keybase) RestoreFromDump(data []byte) error { - k.mu.Lock() - defer k.mu.Unlock() - - if k.conn == nil { - return fmt.Errorf("keybase: database not initialized") - } - - if err := serdes.Deserialize(k.conn, "main", data); err != nil { - return fmt.Errorf("keybase: deserialize database: %w", err) - } - - docs, err := k.queries.ListAllDIDs(context.Background()) - if err != nil { - return fmt.Errorf("keybase: failed to list DIDs: %w", err) - } - - if len(docs) > 0 { - k.did = docs[0].Did - k.didID = docs[0].ID - } - - return nil -} - -// WithTx executes a function within a database transaction. -func (k *Keybase) WithTx(ctx context.Context, fn func(*Queries) error) error { - tx, err := k.db.BeginTx(ctx, nil) - if err != nil { - return fmt.Errorf("keybase: begin tx: %w", err) - } - - if err := fn(k.queries.WithTx(tx)); err != nil { - tx.Rollback() - return err - } - - return tx.Commit() -} diff --git a/internal/keybase/models.go b/internal/keybase/models.go index 65b3e81..db6e7f6 100644 --- a/internal/keybase/models.go +++ b/internal/keybase/models.go @@ -11,7 +11,7 @@ import ( type Account struct { ID int64 `json:"id"` DidID int64 `json:"did_id"` - KeyShareID int64 `json:"key_share_id"` + EnclaveID int64 `json:"enclave_id"` Address string `json:"address"` ChainID string `json:"chain_id"` CoinType int64 `json:"coin_type"` @@ -64,22 +64,19 @@ type Grant struct { ExpiresAt *string `json:"expires_at"` } -type KeyShare struct { - ID int64 `json:"id"` - DidID int64 `json:"did_id"` - ShareID string `json:"share_id"` - KeyID string `json:"key_id"` - PartyIndex int64 `json:"party_index"` - Threshold int64 `json:"threshold"` - TotalParties int64 `json:"total_parties"` - Curve string `json:"curve"` - ShareData string `json:"share_data"` - PublicKey string `json:"public_key"` - ChainCode *string `json:"chain_code"` - DerivationPath *string `json:"derivation_path"` - Status string `json:"status"` - CreatedAt string `json:"created_at"` - RotatedAt *string `json:"rotated_at"` +type MpcEnclafe struct { + ID int64 `json:"id"` + DidID int64 `json:"did_id"` + EnclaveID string `json:"enclave_id"` + PublicKeyHex string `json:"public_key_hex"` + PublicKey []byte `json:"public_key"` + ValShare []byte `json:"val_share"` + UserShare []byte `json:"user_share"` + Nonce []byte `json:"nonce"` + Curve string `json:"curve"` + Status string `json:"status"` + CreatedAt string `json:"created_at"` + RotatedAt *string `json:"rotated_at"` } type Service struct { @@ -158,6 +155,85 @@ type UcanRevocation struct { RevokedAt string `json:"revoked_at"` } +type VAccount struct { + ID int64 `json:"id"` + DidID int64 `json:"did_id"` + EnclaveID int64 `json:"enclave_id"` + Address string `json:"address"` + ChainID string `json:"chain_id"` + CoinType int64 `json:"coin_type"` + AccountIndex int64 `json:"account_index"` + AddressIndex int64 `json:"address_index"` + Label *string `json:"label"` + IsDefault int64 `json:"is_default"` + CreatedAt string `json:"created_at"` + PublicKeyHex string `json:"public_key_hex"` + Curve string `json:"curve"` + EnclaveRef string `json:"enclave_ref"` +} + +type VActiveDelegation struct { + ID int64 `json:"id"` + DidID int64 `json:"did_id"` + Cid string `json:"cid"` + Envelope []byte `json:"envelope"` + Iss string `json:"iss"` + Aud string `json:"aud"` + Sub *string `json:"sub"` + Cmd string `json:"cmd"` + Pol *string `json:"pol"` + Nbf *string `json:"nbf"` + Exp *string `json:"exp"` + IsRoot int64 `json:"is_root"` + IsPowerline int64 `json:"is_powerline"` + CreatedAt string `json:"created_at"` +} + +type VActiveEnclafe struct { + ID int64 `json:"id"` + DidID int64 `json:"did_id"` + EnclaveID string `json:"enclave_id"` + PublicKeyHex string `json:"public_key_hex"` + PublicKey []byte `json:"public_key"` + ValShare []byte `json:"val_share"` + UserShare []byte `json:"user_share"` + Nonce []byte `json:"nonce"` + Curve string `json:"curve"` + Status string `json:"status"` + CreatedAt string `json:"created_at"` + RotatedAt *string `json:"rotated_at"` +} + +type VGrant struct { + ID int64 `json:"id"` + DidID int64 `json:"did_id"` + ServiceID int64 `json:"service_id"` + DelegationCid *string `json:"delegation_cid"` + Scopes string `json:"scopes"` + Accounts string `json:"accounts"` + Status string `json:"status"` + GrantedAt string `json:"granted_at"` + LastUsed *string `json:"last_used"` + ExpiresAt *string `json:"expires_at"` + ServiceName string `json:"service_name"` + ServiceOrigin string `json:"service_origin"` + ServiceLogo *string `json:"service_logo"` +} + +type VSession struct { + ID int64 `json:"id"` + DidID int64 `json:"did_id"` + CredentialID int64 `json:"credential_id"` + SessionID string `json:"session_id"` + DeviceInfo *string `json:"device_info"` + IsCurrent int64 `json:"is_current"` + LastActivity string `json:"last_activity"` + ExpiresAt string `json:"expires_at"` + CreatedAt string `json:"created_at"` + DeviceName string `json:"device_name"` + Authenticator *string `json:"authenticator"` +} + type VerificationMethod struct { ID int64 `json:"id"` DidID int64 `json:"did_id"` diff --git a/internal/keybase/querier.go b/internal/keybase/querier.go index 43e960b..958bd73 100644 --- a/internal/keybase/querier.go +++ b/internal/keybase/querier.go @@ -9,7 +9,7 @@ import ( ) type Querier interface { - ArchiveKeyShare(ctx context.Context, id int64) error + ArchiveEnclave(ctx context.Context, id int64) error CleanExpiredDelegations(ctx context.Context) error CleanOldInvocations(ctx context.Context) error CountActiveGrants(ctx context.Context, didID int64) (int64, error) @@ -18,9 +18,9 @@ type Querier interface { CreateCredential(ctx context.Context, arg CreateCredentialParams) (Credential, error) CreateDID(ctx context.Context, arg CreateDIDParams) (DidDocument, error) CreateDelegation(ctx context.Context, arg CreateDelegationParams) (UcanDelegation, error) + CreateEnclave(ctx context.Context, arg CreateEnclaveParams) (MpcEnclafe, error) CreateGrant(ctx context.Context, arg CreateGrantParams) (Grant, error) CreateInvocation(ctx context.Context, arg CreateInvocationParams) (UcanInvocation, error) - CreateKeyShare(ctx context.Context, arg CreateKeyShareParams) (KeyShare, error) // ============================================================================= // UCAN REVOCATION QUERIES // ============================================================================= @@ -31,8 +31,8 @@ type Querier interface { DeleteAccount(ctx context.Context, arg DeleteAccountParams) error DeleteCredential(ctx context.Context, arg DeleteCredentialParams) error DeleteDelegation(ctx context.Context, arg DeleteDelegationParams) error + DeleteEnclave(ctx context.Context, arg DeleteEnclaveParams) error DeleteExpiredSessions(ctx context.Context) error - DeleteKeyShare(ctx context.Context, arg DeleteKeyShareParams) error DeleteSession(ctx context.Context, id int64) error DeleteVerificationMethod(ctx context.Context, id int64) error GetAccountByAddress(ctx context.Context, address string) (Account, error) @@ -49,14 +49,14 @@ type Querier interface { // ============================================================================= GetDelegationByCID(ctx context.Context, cid string) (UcanDelegation, error) GetDelegationEnvelopeByCID(ctx context.Context, cid string) ([]byte, error) + GetEnclaveByID(ctx context.Context, enclaveID string) (MpcEnclafe, error) + GetEnclaveByPubKeyHex(ctx context.Context, publicKeyHex string) (MpcEnclafe, error) GetGrantByService(ctx context.Context, arg GetGrantByServiceParams) (Grant, error) // ============================================================================= // UCAN INVOCATION QUERIES (v1.0.0-rc.1) // ============================================================================= GetInvocationByCID(ctx context.Context, cid string) (UcanInvocation, error) GetInvocationEnvelopeByCID(ctx context.Context, cid string) ([]byte, error) - GetKeyShareByID(ctx context.Context, shareID string) (KeyShare, error) - GetKeyShareByKeyID(ctx context.Context, arg GetKeyShareByKeyIDParams) (KeyShare, error) GetRevocation(ctx context.Context, delegationCid string) (UcanRevocation, error) GetServiceByID(ctx context.Context, id int64) (Service, error) // ============================================================================= @@ -74,7 +74,7 @@ type Querier interface { // ============================================================================= // ACCOUNT QUERIES // ============================================================================= - ListAccountsByDID(ctx context.Context, didID int64) ([]ListAccountsByDIDRow, error) + ListAccountsByDID(ctx context.Context, didID int64) ([]VAccount, error) ListAllDIDs(ctx context.Context) ([]DidDocument, error) // ============================================================================= // CREDENTIAL QUERIES @@ -86,17 +86,17 @@ type Querier interface { ListDelegationsBySubject(ctx context.Context, sub *string) ([]UcanDelegation, error) ListDelegationsForCommand(ctx context.Context, arg ListDelegationsForCommandParams) ([]UcanDelegation, error) // ============================================================================= + // MPC ENCLAVE QUERIES + // ============================================================================= + ListEnclavesByDID(ctx context.Context, didID int64) ([]MpcEnclafe, error) + // ============================================================================= // GRANT QUERIES // ============================================================================= - ListGrantsByDID(ctx context.Context, didID int64) ([]ListGrantsByDIDRow, error) + ListGrantsByDID(ctx context.Context, didID int64) ([]VGrant, error) ListInvocationsByDID(ctx context.Context, arg ListInvocationsByDIDParams) ([]UcanInvocation, error) ListInvocationsByIssuer(ctx context.Context, arg ListInvocationsByIssuerParams) ([]UcanInvocation, error) ListInvocationsBySubject(ctx context.Context, arg ListInvocationsBySubjectParams) ([]UcanInvocation, error) ListInvocationsForCommand(ctx context.Context, arg ListInvocationsForCommandParams) ([]UcanInvocation, error) - // ============================================================================= - // KEY SHARE QUERIES - // ============================================================================= - ListKeySharesByDID(ctx context.Context, didID int64) ([]KeyShare, error) ListPendingInvocations(ctx context.Context, didID int64) ([]UcanInvocation, error) ListPowerlineDelegations(ctx context.Context, didID int64) ([]UcanDelegation, error) ListRevocationsByRevoker(ctx context.Context, revokedBy string) ([]UcanRevocation, error) @@ -104,7 +104,7 @@ type Querier interface { // ============================================================================= // SESSION QUERIES // ============================================================================= - ListSessionsByDID(ctx context.Context, didID int64) ([]ListSessionsByDIDRow, error) + ListSessionsByDID(ctx context.Context, didID int64) ([]VSession, error) ListSyncCheckpoints(ctx context.Context, didID int64) ([]SyncCheckpoint, error) // ============================================================================= // VERIFICATION METHOD QUERIES @@ -115,7 +115,7 @@ type Querier interface { ReactivateGrant(ctx context.Context, id int64) error RenameCredential(ctx context.Context, arg RenameCredentialParams) error RevokeGrant(ctx context.Context, id int64) error - RotateKeyShare(ctx context.Context, id int64) error + RotateEnclave(ctx context.Context, id int64) error SetCurrentSession(ctx context.Context, arg SetCurrentSessionParams) error SetDefaultAccount(ctx context.Context, arg SetDefaultAccountParams) error SuspendGrant(ctx context.Context, id int64) error diff --git a/internal/keybase/query.sql.go b/internal/keybase/query.sql.go index 289f411..1232add 100644 --- a/internal/keybase/query.sql.go +++ b/internal/keybase/query.sql.go @@ -10,12 +10,12 @@ import ( "encoding/json" ) -const archiveKeyShare = `-- name: ArchiveKeyShare :exec -UPDATE key_shares SET status = 'archived' WHERE id = ? +const archiveEnclave = `-- name: ArchiveEnclave :exec +UPDATE mpc_enclaves SET status = 'archived' WHERE id = ? ` -func (q *Queries) ArchiveKeyShare(ctx context.Context, id int64) error { - _, err := q.db.ExecContext(ctx, archiveKeyShare, id) +func (q *Queries) ArchiveEnclave(ctx context.Context, id int64) error { + _, err := q.db.ExecContext(ctx, archiveEnclave, id) return err } @@ -60,14 +60,14 @@ func (q *Queries) CountCredentialsByDID(ctx context.Context, didID int64) (int64 } const createAccount = `-- name: CreateAccount :one -INSERT INTO accounts (did_id, key_share_id, address, chain_id, coin_type, account_index, address_index, label) +INSERT INTO accounts (did_id, enclave_id, address, chain_id, coin_type, account_index, address_index, label) VALUES (?, ?, ?, ?, ?, ?, ?, ?) -RETURNING id, did_id, key_share_id, address, chain_id, coin_type, account_index, address_index, label, is_default, created_at +RETURNING id, did_id, enclave_id, address, chain_id, coin_type, account_index, address_index, label, is_default, created_at ` type CreateAccountParams struct { DidID int64 `json:"did_id"` - KeyShareID int64 `json:"key_share_id"` + EnclaveID int64 `json:"enclave_id"` Address string `json:"address"` ChainID string `json:"chain_id"` CoinType int64 `json:"coin_type"` @@ -79,7 +79,7 @@ type CreateAccountParams struct { func (q *Queries) CreateAccount(ctx context.Context, arg CreateAccountParams) (Account, error) { row := q.db.QueryRowContext(ctx, createAccount, arg.DidID, - arg.KeyShareID, + arg.EnclaveID, arg.Address, arg.ChainID, arg.CoinType, @@ -91,7 +91,7 @@ func (q *Queries) CreateAccount(ctx context.Context, arg CreateAccountParams) (A err := row.Scan( &i.ID, &i.DidID, - &i.KeyShareID, + &i.EnclaveID, &i.Address, &i.ChainID, &i.CoinType, @@ -254,6 +254,54 @@ func (q *Queries) CreateDelegation(ctx context.Context, arg CreateDelegationPara return i, err } +const createEnclave = `-- name: CreateEnclave :one +INSERT INTO mpc_enclaves ( + did_id, enclave_id, public_key_hex, public_key, val_share, user_share, nonce, curve +) +VALUES (?, ?, ?, ?, ?, ?, ?, ?) +RETURNING id, did_id, enclave_id, public_key_hex, public_key, val_share, user_share, nonce, curve, status, created_at, rotated_at +` + +type CreateEnclaveParams struct { + DidID int64 `json:"did_id"` + EnclaveID string `json:"enclave_id"` + PublicKeyHex string `json:"public_key_hex"` + PublicKey []byte `json:"public_key"` + ValShare []byte `json:"val_share"` + UserShare []byte `json:"user_share"` + Nonce []byte `json:"nonce"` + Curve string `json:"curve"` +} + +func (q *Queries) CreateEnclave(ctx context.Context, arg CreateEnclaveParams) (MpcEnclafe, error) { + row := q.db.QueryRowContext(ctx, createEnclave, + arg.DidID, + arg.EnclaveID, + arg.PublicKeyHex, + arg.PublicKey, + arg.ValShare, + arg.UserShare, + arg.Nonce, + arg.Curve, + ) + var i MpcEnclafe + err := row.Scan( + &i.ID, + &i.DidID, + &i.EnclaveID, + &i.PublicKeyHex, + &i.PublicKey, + &i.ValShare, + &i.UserShare, + &i.Nonce, + &i.Curve, + &i.Status, + &i.CreatedAt, + &i.RotatedAt, + ) + return i, err +} + const createGrant = `-- name: CreateGrant :one INSERT INTO grants (did_id, service_id, delegation_cid, scopes, accounts, expires_at) VALUES (?, ?, ?, ?, ?, ?) @@ -348,64 +396,6 @@ func (q *Queries) CreateInvocation(ctx context.Context, arg CreateInvocationPara return i, err } -const createKeyShare = `-- name: CreateKeyShare :one -INSERT INTO key_shares ( - did_id, share_id, key_id, party_index, threshold, total_parties, - curve, share_data, public_key, chain_code, derivation_path -) -VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) -RETURNING id, did_id, share_id, key_id, party_index, threshold, total_parties, curve, share_data, public_key, chain_code, derivation_path, status, created_at, rotated_at -` - -type CreateKeyShareParams struct { - DidID int64 `json:"did_id"` - ShareID string `json:"share_id"` - KeyID string `json:"key_id"` - PartyIndex int64 `json:"party_index"` - Threshold int64 `json:"threshold"` - TotalParties int64 `json:"total_parties"` - Curve string `json:"curve"` - ShareData string `json:"share_data"` - PublicKey string `json:"public_key"` - ChainCode *string `json:"chain_code"` - DerivationPath *string `json:"derivation_path"` -} - -func (q *Queries) CreateKeyShare(ctx context.Context, arg CreateKeyShareParams) (KeyShare, error) { - row := q.db.QueryRowContext(ctx, createKeyShare, - arg.DidID, - arg.ShareID, - arg.KeyID, - arg.PartyIndex, - arg.Threshold, - arg.TotalParties, - arg.Curve, - arg.ShareData, - arg.PublicKey, - arg.ChainCode, - arg.DerivationPath, - ) - var i KeyShare - err := row.Scan( - &i.ID, - &i.DidID, - &i.ShareID, - &i.KeyID, - &i.PartyIndex, - &i.Threshold, - &i.TotalParties, - &i.Curve, - &i.ShareData, - &i.PublicKey, - &i.ChainCode, - &i.DerivationPath, - &i.Status, - &i.CreatedAt, - &i.RotatedAt, - ) - return i, err -} - const createRevocation = `-- name: CreateRevocation :exec INSERT INTO ucan_revocations (delegation_cid, revoked_by, invocation_cid, reason) @@ -592,6 +582,20 @@ func (q *Queries) DeleteDelegation(ctx context.Context, arg DeleteDelegationPara return err } +const deleteEnclave = `-- name: DeleteEnclave :exec +DELETE FROM mpc_enclaves WHERE id = ? AND did_id = ? +` + +type DeleteEnclaveParams struct { + ID int64 `json:"id"` + DidID int64 `json:"did_id"` +} + +func (q *Queries) DeleteEnclave(ctx context.Context, arg DeleteEnclaveParams) error { + _, err := q.db.ExecContext(ctx, deleteEnclave, arg.ID, arg.DidID) + return err +} + const deleteExpiredSessions = `-- name: DeleteExpiredSessions :exec DELETE FROM sessions WHERE expires_at < datetime('now') ` @@ -601,20 +605,6 @@ func (q *Queries) DeleteExpiredSessions(ctx context.Context) error { return err } -const deleteKeyShare = `-- name: DeleteKeyShare :exec -DELETE FROM key_shares WHERE id = ? AND did_id = ? -` - -type DeleteKeyShareParams struct { - ID int64 `json:"id"` - DidID int64 `json:"did_id"` -} - -func (q *Queries) DeleteKeyShare(ctx context.Context, arg DeleteKeyShareParams) error { - _, err := q.db.ExecContext(ctx, deleteKeyShare, arg.ID, arg.DidID) - return err -} - const deleteSession = `-- name: DeleteSession :exec DELETE FROM sessions WHERE id = ? ` @@ -634,7 +624,7 @@ func (q *Queries) DeleteVerificationMethod(ctx context.Context, id int64) error } const getAccountByAddress = `-- name: GetAccountByAddress :one -SELECT id, did_id, key_share_id, address, chain_id, coin_type, account_index, address_index, label, is_default, created_at FROM accounts WHERE address = ? LIMIT 1 +SELECT id, did_id, enclave_id, address, chain_id, coin_type, account_index, address_index, label, is_default, created_at FROM accounts WHERE address = ? LIMIT 1 ` func (q *Queries) GetAccountByAddress(ctx context.Context, address string) (Account, error) { @@ -643,7 +633,7 @@ func (q *Queries) GetAccountByAddress(ctx context.Context, address string) (Acco err := row.Scan( &i.ID, &i.DidID, - &i.KeyShareID, + &i.EnclaveID, &i.Address, &i.ChainID, &i.CoinType, @@ -749,7 +739,7 @@ func (q *Queries) GetDIDByID(ctx context.Context, id int64) (DidDocument, error) } const getDefaultAccount = `-- name: GetDefaultAccount :one -SELECT id, did_id, key_share_id, address, chain_id, coin_type, account_index, address_index, label, is_default, created_at FROM accounts WHERE did_id = ? AND chain_id = ? AND is_default = 1 LIMIT 1 +SELECT id, did_id, enclave_id, address, chain_id, coin_type, account_index, address_index, label, is_default, created_at FROM accounts WHERE did_id = ? AND chain_id = ? AND is_default = 1 LIMIT 1 ` type GetDefaultAccountParams struct { @@ -763,7 +753,7 @@ func (q *Queries) GetDefaultAccount(ctx context.Context, arg GetDefaultAccountPa err := row.Scan( &i.ID, &i.DidID, - &i.KeyShareID, + &i.EnclaveID, &i.Address, &i.ChainID, &i.CoinType, @@ -817,6 +807,54 @@ func (q *Queries) GetDelegationEnvelopeByCID(ctx context.Context, cid string) ([ return envelope, err } +const getEnclaveByID = `-- name: GetEnclaveByID :one +SELECT id, did_id, enclave_id, public_key_hex, public_key, val_share, user_share, nonce, curve, status, created_at, rotated_at FROM mpc_enclaves WHERE enclave_id = ? LIMIT 1 +` + +func (q *Queries) GetEnclaveByID(ctx context.Context, enclaveID string) (MpcEnclafe, error) { + row := q.db.QueryRowContext(ctx, getEnclaveByID, enclaveID) + var i MpcEnclafe + err := row.Scan( + &i.ID, + &i.DidID, + &i.EnclaveID, + &i.PublicKeyHex, + &i.PublicKey, + &i.ValShare, + &i.UserShare, + &i.Nonce, + &i.Curve, + &i.Status, + &i.CreatedAt, + &i.RotatedAt, + ) + return i, err +} + +const getEnclaveByPubKeyHex = `-- name: GetEnclaveByPubKeyHex :one +SELECT id, did_id, enclave_id, public_key_hex, public_key, val_share, user_share, nonce, curve, status, created_at, rotated_at FROM mpc_enclaves WHERE public_key_hex = ? LIMIT 1 +` + +func (q *Queries) GetEnclaveByPubKeyHex(ctx context.Context, publicKeyHex string) (MpcEnclafe, error) { + row := q.db.QueryRowContext(ctx, getEnclaveByPubKeyHex, publicKeyHex) + var i MpcEnclafe + err := row.Scan( + &i.ID, + &i.DidID, + &i.EnclaveID, + &i.PublicKeyHex, + &i.PublicKey, + &i.ValShare, + &i.UserShare, + &i.Nonce, + &i.Curve, + &i.Status, + &i.CreatedAt, + &i.RotatedAt, + ) + return i, err +} + const getGrantByService = `-- name: GetGrantByService :one SELECT id, did_id, service_id, delegation_cid, scopes, accounts, status, granted_at, last_used, expires_at FROM grants WHERE did_id = ? AND service_id = ? LIMIT 1 ` @@ -885,65 +923,6 @@ func (q *Queries) GetInvocationEnvelopeByCID(ctx context.Context, cid string) ([ return envelope, err } -const getKeyShareByID = `-- name: GetKeyShareByID :one -SELECT id, did_id, share_id, key_id, party_index, threshold, total_parties, curve, share_data, public_key, chain_code, derivation_path, status, created_at, rotated_at FROM key_shares WHERE share_id = ? LIMIT 1 -` - -func (q *Queries) GetKeyShareByID(ctx context.Context, shareID string) (KeyShare, error) { - row := q.db.QueryRowContext(ctx, getKeyShareByID, shareID) - var i KeyShare - err := row.Scan( - &i.ID, - &i.DidID, - &i.ShareID, - &i.KeyID, - &i.PartyIndex, - &i.Threshold, - &i.TotalParties, - &i.Curve, - &i.ShareData, - &i.PublicKey, - &i.ChainCode, - &i.DerivationPath, - &i.Status, - &i.CreatedAt, - &i.RotatedAt, - ) - return i, err -} - -const getKeyShareByKeyID = `-- name: GetKeyShareByKeyID :one -SELECT id, did_id, share_id, key_id, party_index, threshold, total_parties, curve, share_data, public_key, chain_code, derivation_path, status, created_at, rotated_at FROM key_shares WHERE did_id = ? AND key_id = ? AND status = 'active' LIMIT 1 -` - -type GetKeyShareByKeyIDParams struct { - DidID int64 `json:"did_id"` - KeyID string `json:"key_id"` -} - -func (q *Queries) GetKeyShareByKeyID(ctx context.Context, arg GetKeyShareByKeyIDParams) (KeyShare, error) { - row := q.db.QueryRowContext(ctx, getKeyShareByKeyID, arg.DidID, arg.KeyID) - var i KeyShare - err := row.Scan( - &i.ID, - &i.DidID, - &i.ShareID, - &i.KeyID, - &i.PartyIndex, - &i.Threshold, - &i.TotalParties, - &i.Curve, - &i.ShareData, - &i.PublicKey, - &i.ChainCode, - &i.DerivationPath, - &i.Status, - &i.CreatedAt, - &i.RotatedAt, - ) - return i, err -} - const getRevocation = `-- name: GetRevocation :one SELECT id, delegation_cid, revoked_by, invocation_cid, reason, revoked_at FROM ucan_revocations WHERE delegation_cid = ? LIMIT 1 ` @@ -1093,7 +1072,7 @@ func (q *Queries) IsDelegationRevoked(ctx context.Context, delegationCid string) } const listAccountsByChain = `-- name: ListAccountsByChain :many -SELECT id, did_id, key_share_id, address, chain_id, coin_type, account_index, address_index, label, is_default, created_at FROM accounts WHERE did_id = ? AND chain_id = ? ORDER BY account_index, address_index +SELECT id, did_id, enclave_id, address, chain_id, coin_type, account_index, address_index, label, is_default, created_at FROM accounts WHERE did_id = ? AND chain_id = ? ORDER BY account_index, address_index ` type ListAccountsByChainParams struct { @@ -1113,7 +1092,7 @@ func (q *Queries) ListAccountsByChain(ctx context.Context, arg ListAccountsByCha if err := rows.Scan( &i.ID, &i.DidID, - &i.KeyShareID, + &i.EnclaveID, &i.Address, &i.ChainID, &i.CoinType, @@ -1138,45 +1117,25 @@ func (q *Queries) ListAccountsByChain(ctx context.Context, arg ListAccountsByCha const listAccountsByDID = `-- name: ListAccountsByDID :many -SELECT a.id, a.did_id, a.key_share_id, a.address, a.chain_id, a.coin_type, a.account_index, a.address_index, a.label, a.is_default, a.created_at, k.public_key as share_public_key, k.curve -FROM accounts a -JOIN key_shares k ON a.key_share_id = k.id -WHERE a.did_id = ? -ORDER BY a.is_default DESC, a.created_at +SELECT id, did_id, enclave_id, address, chain_id, coin_type, account_index, address_index, label, is_default, created_at, public_key_hex, curve, enclave_ref FROM v_accounts WHERE did_id = ? ORDER BY is_default DESC, created_at ` -type ListAccountsByDIDRow struct { - ID int64 `json:"id"` - DidID int64 `json:"did_id"` - KeyShareID int64 `json:"key_share_id"` - Address string `json:"address"` - ChainID string `json:"chain_id"` - CoinType int64 `json:"coin_type"` - AccountIndex int64 `json:"account_index"` - AddressIndex int64 `json:"address_index"` - Label *string `json:"label"` - IsDefault int64 `json:"is_default"` - CreatedAt string `json:"created_at"` - SharePublicKey string `json:"share_public_key"` - Curve string `json:"curve"` -} - // ============================================================================= // ACCOUNT QUERIES // ============================================================================= -func (q *Queries) ListAccountsByDID(ctx context.Context, didID int64) ([]ListAccountsByDIDRow, error) { +func (q *Queries) ListAccountsByDID(ctx context.Context, didID int64) ([]VAccount, error) { rows, err := q.db.QueryContext(ctx, listAccountsByDID, didID) if err != nil { return nil, err } defer rows.Close() - items := []ListAccountsByDIDRow{} + items := []VAccount{} for rows.Next() { - var i ListAccountsByDIDRow + var i VAccount if err := rows.Scan( &i.ID, &i.DidID, - &i.KeyShareID, + &i.EnclaveID, &i.Address, &i.ChainID, &i.CoinType, @@ -1185,8 +1144,9 @@ func (q *Queries) ListAccountsByDID(ctx context.Context, didID int64) ([]ListAcc &i.Label, &i.IsDefault, &i.CreatedAt, - &i.SharePublicKey, + &i.PublicKeyHex, &i.Curve, + &i.EnclaveRef, ); err != nil { return nil, err } @@ -1512,43 +1472,67 @@ func (q *Queries) ListDelegationsForCommand(ctx context.Context, arg ListDelegat return items, nil } -const listGrantsByDID = `-- name: ListGrantsByDID :many +const listEnclavesByDID = `-- name: ListEnclavesByDID :many -SELECT g.id, g.did_id, g.service_id, g.delegation_cid, g.scopes, g.accounts, g.status, g.granted_at, g.last_used, g.expires_at, s.name as service_name, s.origin as service_origin, s.logo_url as service_logo -FROM grants g -JOIN services s ON g.service_id = s.id -WHERE g.did_id = ? AND g.status = 'active' -ORDER BY g.last_used DESC NULLS LAST +SELECT id, did_id, enclave_id, public_key_hex, public_key, val_share, user_share, nonce, curve, status, created_at, rotated_at FROM mpc_enclaves WHERE did_id = ? AND status = 'active' ORDER BY created_at ` -type ListGrantsByDIDRow struct { - ID int64 `json:"id"` - DidID int64 `json:"did_id"` - ServiceID int64 `json:"service_id"` - DelegationCid *string `json:"delegation_cid"` - Scopes json.RawMessage `json:"scopes"` - Accounts json.RawMessage `json:"accounts"` - Status string `json:"status"` - GrantedAt string `json:"granted_at"` - LastUsed *string `json:"last_used"` - ExpiresAt *string `json:"expires_at"` - ServiceName string `json:"service_name"` - ServiceOrigin string `json:"service_origin"` - ServiceLogo *string `json:"service_logo"` +// ============================================================================= +// MPC ENCLAVE QUERIES +// ============================================================================= +func (q *Queries) ListEnclavesByDID(ctx context.Context, didID int64) ([]MpcEnclafe, error) { + rows, err := q.db.QueryContext(ctx, listEnclavesByDID, didID) + if err != nil { + return nil, err + } + defer rows.Close() + items := []MpcEnclafe{} + for rows.Next() { + var i MpcEnclafe + if err := rows.Scan( + &i.ID, + &i.DidID, + &i.EnclaveID, + &i.PublicKeyHex, + &i.PublicKey, + &i.ValShare, + &i.UserShare, + &i.Nonce, + &i.Curve, + &i.Status, + &i.CreatedAt, + &i.RotatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil } +const listGrantsByDID = `-- name: ListGrantsByDID :many + +SELECT id, did_id, service_id, delegation_cid, scopes, accounts, status, granted_at, last_used, expires_at, service_name, service_origin, service_logo FROM v_grants WHERE did_id = ? AND status = 'active' ORDER BY last_used DESC NULLS LAST +` + // ============================================================================= // GRANT QUERIES // ============================================================================= -func (q *Queries) ListGrantsByDID(ctx context.Context, didID int64) ([]ListGrantsByDIDRow, error) { +func (q *Queries) ListGrantsByDID(ctx context.Context, didID int64) ([]VGrant, error) { rows, err := q.db.QueryContext(ctx, listGrantsByDID, didID) if err != nil { return nil, err } defer rows.Close() - items := []ListGrantsByDIDRow{} + items := []VGrant{} for rows.Next() { - var i ListGrantsByDIDRow + var i VGrant if err := rows.Scan( &i.ID, &i.DidID, @@ -1778,53 +1762,6 @@ func (q *Queries) ListInvocationsForCommand(ctx context.Context, arg ListInvocat return items, nil } -const listKeySharesByDID = `-- name: ListKeySharesByDID :many - -SELECT id, did_id, share_id, key_id, party_index, threshold, total_parties, curve, share_data, public_key, chain_code, derivation_path, status, created_at, rotated_at FROM key_shares WHERE did_id = ? AND status = 'active' ORDER BY created_at -` - -// ============================================================================= -// KEY SHARE QUERIES -// ============================================================================= -func (q *Queries) ListKeySharesByDID(ctx context.Context, didID int64) ([]KeyShare, error) { - rows, err := q.db.QueryContext(ctx, listKeySharesByDID, didID) - if err != nil { - return nil, err - } - defer rows.Close() - items := []KeyShare{} - for rows.Next() { - var i KeyShare - if err := rows.Scan( - &i.ID, - &i.DidID, - &i.ShareID, - &i.KeyID, - &i.PartyIndex, - &i.Threshold, - &i.TotalParties, - &i.Curve, - &i.ShareData, - &i.PublicKey, - &i.ChainCode, - &i.DerivationPath, - &i.Status, - &i.CreatedAt, - &i.RotatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Close(); err != nil { - return nil, err - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - const listPendingInvocations = `-- name: ListPendingInvocations :many SELECT id, did_id, cid, envelope, iss, sub, aud, cmd, prf, exp, iat, executed_at, result_cid, created_at FROM ucan_invocations WHERE did_id = ? AND executed_at IS NULL AND (exp IS NULL OR exp > datetime('now')) @@ -1995,39 +1932,21 @@ func (q *Queries) ListRootDelegations(ctx context.Context, didID int64) ([]UcanD const listSessionsByDID = `-- name: ListSessionsByDID :many -SELECT s.id, s.did_id, s.credential_id, s.session_id, s.device_info, s.is_current, s.last_activity, s.expires_at, s.created_at, c.device_name, c.authenticator -FROM sessions s -JOIN credentials c ON s.credential_id = c.id -WHERE s.did_id = ? AND s.expires_at > datetime('now') -ORDER BY s.last_activity DESC +SELECT id, did_id, credential_id, session_id, device_info, is_current, last_activity, expires_at, created_at, device_name, authenticator FROM v_sessions WHERE did_id = ? ORDER BY last_activity DESC ` -type ListSessionsByDIDRow struct { - ID int64 `json:"id"` - DidID int64 `json:"did_id"` - CredentialID int64 `json:"credential_id"` - SessionID string `json:"session_id"` - DeviceInfo json.RawMessage `json:"device_info"` - IsCurrent int64 `json:"is_current"` - LastActivity string `json:"last_activity"` - ExpiresAt string `json:"expires_at"` - CreatedAt string `json:"created_at"` - DeviceName string `json:"device_name"` - Authenticator *string `json:"authenticator"` -} - // ============================================================================= // SESSION QUERIES // ============================================================================= -func (q *Queries) ListSessionsByDID(ctx context.Context, didID int64) ([]ListSessionsByDIDRow, error) { +func (q *Queries) ListSessionsByDID(ctx context.Context, didID int64) ([]VSession, error) { rows, err := q.db.QueryContext(ctx, listSessionsByDID, didID) if err != nil { return nil, err } defer rows.Close() - items := []ListSessionsByDIDRow{} + items := []VSession{} for rows.Next() { - var i ListSessionsByDIDRow + var i VSession if err := rows.Scan( &i.ID, &i.DidID, @@ -2213,14 +2132,14 @@ func (q *Queries) RevokeGrant(ctx context.Context, id int64) error { return err } -const rotateKeyShare = `-- name: RotateKeyShare :exec -UPDATE key_shares +const rotateEnclave = `-- name: RotateEnclave :exec +UPDATE mpc_enclaves SET status = 'rotating', rotated_at = datetime('now') WHERE id = ? ` -func (q *Queries) RotateKeyShare(ctx context.Context, id int64) error { - _, err := q.db.ExecContext(ctx, rotateKeyShare, id) +func (q *Queries) RotateEnclave(ctx context.Context, id int64) error { + _, err := q.db.ExecContext(ctx, rotateEnclave, id) return err } diff --git a/internal/migrations/query.sql b/internal/migrations/query.sql index 26abf31..ebfdffe 100644 --- a/internal/migrations/query.sql +++ b/internal/migrations/query.sql @@ -72,47 +72,42 @@ DELETE FROM credentials WHERE id = ? AND did_id = ?; SELECT COUNT(*) FROM credentials WHERE did_id = ?; -- ============================================================================= --- KEY SHARE QUERIES +-- MPC ENCLAVE QUERIES -- ============================================================================= --- name: ListKeySharesByDID :many -SELECT * FROM key_shares WHERE did_id = ? AND status = 'active' ORDER BY created_at; +-- name: ListEnclavesByDID :many +SELECT * FROM mpc_enclaves WHERE did_id = ? AND status = 'active' ORDER BY created_at; --- name: GetKeyShareByID :one -SELECT * FROM key_shares WHERE share_id = ? LIMIT 1; +-- name: GetEnclaveByID :one +SELECT * FROM mpc_enclaves WHERE enclave_id = ? LIMIT 1; --- name: GetKeyShareByKeyID :one -SELECT * FROM key_shares WHERE did_id = ? AND key_id = ? AND status = 'active' LIMIT 1; +-- name: GetEnclaveByPubKeyHex :one +SELECT * FROM mpc_enclaves WHERE public_key_hex = ? LIMIT 1; --- name: CreateKeyShare :one -INSERT INTO key_shares ( - did_id, share_id, key_id, party_index, threshold, total_parties, - curve, share_data, public_key, chain_code, derivation_path +-- name: CreateEnclave :one +INSERT INTO mpc_enclaves ( + did_id, enclave_id, public_key_hex, public_key, val_share, user_share, nonce, curve ) -VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) +VALUES (?, ?, ?, ?, ?, ?, ?, ?) RETURNING *; --- name: RotateKeyShare :exec -UPDATE key_shares +-- name: RotateEnclave :exec +UPDATE mpc_enclaves SET status = 'rotating', rotated_at = datetime('now') WHERE id = ?; --- name: ArchiveKeyShare :exec -UPDATE key_shares SET status = 'archived' WHERE id = ?; +-- name: ArchiveEnclave :exec +UPDATE mpc_enclaves SET status = 'archived' WHERE id = ?; --- name: DeleteKeyShare :exec -DELETE FROM key_shares WHERE id = ? AND did_id = ?; +-- name: DeleteEnclave :exec +DELETE FROM mpc_enclaves WHERE id = ? AND did_id = ?; -- ============================================================================= -- ACCOUNT QUERIES -- ============================================================================= -- name: ListAccountsByDID :many -SELECT a.*, k.public_key as share_public_key, k.curve -FROM accounts a -JOIN key_shares k ON a.key_share_id = k.id -WHERE a.did_id = ? -ORDER BY a.is_default DESC, a.created_at; +SELECT * FROM v_accounts WHERE did_id = ? ORDER BY is_default DESC, created_at; -- name: ListAccountsByChain :many SELECT * FROM accounts WHERE did_id = ? AND chain_id = ? ORDER BY account_index, address_index; @@ -124,7 +119,7 @@ SELECT * FROM accounts WHERE address = ? LIMIT 1; SELECT * FROM accounts WHERE did_id = ? AND chain_id = ? AND is_default = 1 LIMIT 1; -- name: CreateAccount :one -INSERT INTO accounts (did_id, key_share_id, address, chain_id, coin_type, account_index, address_index, label) +INSERT INTO accounts (did_id, enclave_id, address, chain_id, coin_type, account_index, address_index, label) VALUES (?, ?, ?, ?, ?, ?, ?, ?) RETURNING *; @@ -277,11 +272,7 @@ ORDER BY revoked_at DESC; -- ============================================================================= -- name: ListSessionsByDID :many -SELECT s.*, c.device_name, c.authenticator -FROM sessions s -JOIN credentials c ON s.credential_id = c.id -WHERE s.did_id = ? AND s.expires_at > datetime('now') -ORDER BY s.last_activity DESC; +SELECT * FROM v_sessions WHERE did_id = ? ORDER BY last_activity DESC; -- name: GetSessionByID :one SELECT * FROM sessions WHERE session_id = ? LIMIT 1; @@ -336,11 +327,7 @@ SELECT * FROM services WHERE is_verified = 1 ORDER BY name; -- ============================================================================= -- name: ListGrantsByDID :many -SELECT g.*, s.name as service_name, s.origin as service_origin, s.logo_url as service_logo -FROM grants g -JOIN services s ON g.service_id = s.id -WHERE g.did_id = ? AND g.status = 'active' -ORDER BY g.last_used DESC NULLS LAST; +SELECT * FROM v_grants WHERE did_id = ? AND status = 'active' ORDER BY last_used DESC NULLS LAST; -- name: GetGrantByService :one SELECT * FROM grants WHERE did_id = ? AND service_id = ? LIMIT 1; diff --git a/internal/migrations/schema.sql b/internal/migrations/schema.sql index 317e255..3166af5 100644 --- a/internal/migrations/schema.sql +++ b/internal/migrations/schema.sql @@ -66,46 +66,41 @@ CREATE INDEX idx_credentials_did_id ON credentials(did_id); CREATE INDEX idx_credentials_credential_id ON credentials(credential_id); -- ============================================================================= --- MPC KEY SHARES +-- MPC ENCLAVES -- ============================================================================= --- Key Shares: MPC/TSS key share storage -CREATE TABLE IF NOT EXISTS key_shares ( +CREATE TABLE IF NOT EXISTS mpc_enclaves ( id INTEGER PRIMARY KEY, did_id INTEGER NOT NULL REFERENCES did_documents(id) ON DELETE CASCADE, - share_id TEXT NOT NULL UNIQUE, -- Unique identifier for this share - key_id TEXT NOT NULL, -- Identifier for the full key (shared across parties) - party_index INTEGER NOT NULL, -- This party's index (1, 2, 3...) - threshold INTEGER NOT NULL, -- Minimum shares needed to sign - total_parties INTEGER NOT NULL, -- Total number of parties - curve TEXT NOT NULL DEFAULT 'secp256k1', -- secp256k1, ed25519 - share_data TEXT NOT NULL, -- Encrypted key share (base64) - public_key TEXT NOT NULL, -- Full public key (base64) - chain_code TEXT, -- BIP32 chain code for derivation - derivation_path TEXT, -- BIP44 path: m/44'/60'/0'/0 + enclave_id TEXT NOT NULL UNIQUE, + public_key_hex TEXT NOT NULL, + public_key BLOB NOT NULL, + val_share BLOB NOT NULL, + user_share BLOB NOT NULL, + nonce BLOB NOT NULL, + curve TEXT NOT NULL DEFAULT 'secp256k1', status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'rotating', 'archived')), created_at TEXT NOT NULL DEFAULT (datetime('now')), rotated_at TEXT, - UNIQUE(did_id, key_id, party_index) + UNIQUE(did_id, enclave_id) ); -CREATE INDEX idx_key_shares_did_id ON key_shares(did_id); -CREATE INDEX idx_key_shares_key_id ON key_shares(key_id); +CREATE INDEX idx_mpc_enclaves_did_id ON mpc_enclaves(did_id); +CREATE INDEX idx_mpc_enclaves_public_key_hex ON mpc_enclaves(public_key_hex); --- Derived Accounts: Wallet accounts derived from key shares CREATE TABLE IF NOT EXISTS accounts ( id INTEGER PRIMARY KEY, did_id INTEGER NOT NULL REFERENCES did_documents(id) ON DELETE CASCADE, - key_share_id INTEGER NOT NULL REFERENCES key_shares(id) ON DELETE CASCADE, - address TEXT NOT NULL, -- Derived address - chain_id TEXT NOT NULL, -- sonr-mainnet-1, ethereum, etc. - coin_type INTEGER NOT NULL, -- BIP44 coin type (118=cosmos, 60=eth) - account_index INTEGER NOT NULL DEFAULT 0, -- BIP44 account index - address_index INTEGER NOT NULL DEFAULT 0, -- BIP44 address index - label TEXT DEFAULT '', -- User-assigned label + enclave_id INTEGER NOT NULL REFERENCES mpc_enclaves(id) ON DELETE CASCADE, + address TEXT NOT NULL, + chain_id TEXT NOT NULL, + coin_type INTEGER NOT NULL, + account_index INTEGER NOT NULL DEFAULT 0, + address_index INTEGER NOT NULL DEFAULT 0, + label TEXT DEFAULT '', is_default INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL DEFAULT (datetime('now')), - UNIQUE(key_share_id, chain_id, account_index, address_index) + UNIQUE(enclave_id, chain_id, account_index, address_index) ); CREATE INDEX idx_accounts_did_id ON accounts(did_id); @@ -291,3 +286,40 @@ CREATE TRIGGER IF NOT EXISTS did_documents_updated_at BEGIN UPDATE did_documents SET updated_at = datetime('now') WHERE id = NEW.id; END; + +-- ============================================================================= +-- VIEWS (pre-computed JOINs for common queries) +-- ============================================================================= + +CREATE VIEW IF NOT EXISTS v_accounts AS +SELECT + a.id, a.did_id, a.enclave_id, a.address, a.chain_id, + a.coin_type, a.account_index, a.address_index, + a.label, a.is_default, a.created_at, + e.public_key_hex, e.curve, e.enclave_id as enclave_ref +FROM accounts a +JOIN mpc_enclaves e ON a.enclave_id = e.id; + +CREATE VIEW IF NOT EXISTS v_sessions AS +SELECT + s.id, s.did_id, s.credential_id, s.session_id, s.device_info, + s.is_current, s.last_activity, s.expires_at, s.created_at, + c.device_name, c.authenticator +FROM sessions s +JOIN credentials c ON s.credential_id = c.id +WHERE s.expires_at > datetime('now'); + +CREATE VIEW IF NOT EXISTS v_grants AS +SELECT + g.id, g.did_id, g.service_id, g.delegation_cid, g.scopes, + g.accounts, g.status, g.granted_at, g.last_used, g.expires_at, + s.name as service_name, s.origin as service_origin, s.logo_url as service_logo +FROM grants g +JOIN services s ON g.service_id = s.id; + +CREATE VIEW IF NOT EXISTS v_active_delegations AS +SELECT * FROM ucan_delegations +WHERE exp IS NULL OR exp > datetime('now'); + +CREATE VIEW IF NOT EXISTS v_active_enclaves AS +SELECT * FROM mpc_enclaves WHERE status = 'active'; diff --git a/internal/types/generate.go b/internal/types/generate.go index 2803f36..3f897e1 100644 --- a/internal/types/generate.go +++ b/internal/types/generate.go @@ -1,39 +1,17 @@ package types -// GenerateInput represents the input for the generate function type GenerateInput struct { - Credential string `json:"credential"` // Base64-encoded WebAuthn credential - - // MPC keyshare data (optional - if provided, creates initial keyshare and account) - KeyShare *KeyShareInput `json:"key_share,omitempty"` + Credential string `json:"credential"` } -// KeyShareInput represents MPC keyshare data for initialization -type KeyShareInput struct { - KeyID string `json:"key_id"` - PartyIndex int64 `json:"party_index"` - Threshold int64 `json:"threshold"` - TotalParties int64 `json:"total_parties"` - Curve string `json:"curve"` - ShareData string `json:"share_data"` - PublicKey string `json:"public_key"` - ChainCode string `json:"chain_code,omitempty"` - DerivationPath string `json:"derivation_path,omitempty"` -} - -// GenerateOutput represents the output of the generate function type GenerateOutput struct { - DID string `json:"did"` - Database []byte `json:"database"` - - // KeyShare info if a keyshare was provided - KeyShareID string `json:"key_share_id,omitempty"` - - // Account info if an account was created - Account *AccountInfo `json:"account,omitempty"` + DID string `json:"did"` + Database []byte `json:"database"` + EnclaveID string `json:"enclave_id"` + PublicKey string `json:"public_key"` + Accounts []AccountInfo `json:"accounts"` } -// AccountInfo represents created account information type AccountInfo struct { Address string `json:"address"` ChainID string `json:"chain_id"`