feat(enclave): add MPC enclave generation support
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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"`
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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"`
|
||||
|
||||
Reference in New Issue
Block a user