diff --git a/.github/aider/prompts/data-modeler-cosmos.md b/.github/AIDER/data-modeler-cosmos.md similarity index 100% rename from .github/aider/prompts/data-modeler-cosmos.md rename to .github/AIDER/data-modeler-cosmos.md diff --git a/.github/aider/prompts/data-modeler.md b/.github/AIDER/data-modeler.md similarity index 100% rename from .github/aider/prompts/data-modeler.md rename to .github/AIDER/data-modeler.md diff --git a/.github/aider/prompts/sonr-tech-lead.md b/.github/AIDER/sonr-tech-lead.md similarity index 100% rename from .github/aider/prompts/sonr-tech-lead.md rename to .github/AIDER/sonr-tech-lead.md diff --git a/.gitignore b/.gitignore index 9a864a977..1ad517e45 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ # Binaries +no .data schemas *.db @@ -95,4 +96,8 @@ sonr.wiki !buf.lock .air.toml +mprocs.yaml +mprocs.log +tools-stamp +sonr.log diff --git a/Makefile b/Makefile index cabbee6c1..2257ba7c5 100644 --- a/Makefile +++ b/Makefile @@ -306,7 +306,7 @@ sh-testnet: mod-tidy ############################################################################### ### generation ### ############################################################################### -.PHONY: gen-pkl gen-templ +.PHONY: gen-pkl gen-templ gen-sqlc gen-pkl: init-env pkl-gen-go pkl/sonr.orm/UCAN.pkl @@ -314,8 +314,11 @@ gen-pkl: init-env pkl-gen-go pkl/sonr.net/Hway.pkl pkl-gen-go pkl/sonr.net/Motr.pkl +gen-sqlc: init-env + @cd internal/database && sqlc generate + gen-templ: init-env - templ generate + @templ generate ############################################################################### diff --git a/README.md b/README.md index 92b2969e6..aca1f39a4 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,6 @@ [![Static Badge](https://img.shields.io/badge/homepage-sonr.io-blue?style=flat-square)](https://sonr.io) [![Go Report Card](https://goreportcard.com/badge/github.com/onsonr/sonr)](https://goreportcard.com/report/github.com/onsonr/sonr) [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=sonrhq_sonr&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=sonr-io_sonr) -[![Latest version of 'sonrd' @ Cloudsmith](https://api-prd.cloudsmith.io/v1/badges/version/sonr/sonr/deb/sonrd/latest/a=amd64;xc=main;d=ubuntu%252Fxenial;t=binary/?render=true&show_latest=true)](https://cloudsmith.io/~sonr/repos/sonr/packages/detail/deb/sonrd/latest/a=amd64;xc=main;d=ubuntu%252Fxenial;t=binary/) > Sonr is a combination of decentralized primitives. Fundamentally, it is a peer-to-peer identity and asset management system that leverages DID documents, Webauthn, and IPFS—providing users with a secure, portable decentralized identity. diff --git a/cmd/hway/cmds.go b/cmd/hway/cmds.go index e942cb7ed..ca6c4f4b1 100644 --- a/cmd/hway/cmds.go +++ b/cmd/hway/cmds.go @@ -6,6 +6,8 @@ import ( "net/http" "os" + "github.com/onsonr/sonr/pkg/common" + "github.com/onsonr/sonr/pkg/gateway" "github.com/spf13/cobra" ) @@ -18,11 +20,10 @@ var ( sonrGrpcURL string // Sonr gRPC URL (default localhost:9090) sonrRPCURL string // Sonr RPC URL (default localhost:26657) - sqliteFile string // SQLite database file (default hway.db) - psqlHost string // PostgresSQL Host Flag - psqlUser string // PostgresSQL User Flag - psqlPass string // PostgresSQL Password Flag - psqlDB string // PostgresSQL Database Flag + psqlHost string // PostgresSQL Host Flag + psqlUser string // PostgresSQL User Flag + psqlPass string // PostgresSQL Password Flag + psqlDB string // PostgresSQL Database Flag ) func rootCmd() *cobra.Command { @@ -34,11 +35,11 @@ func rootCmd() *cobra.Command { if err != nil { panic(err) } - db, ipc, err := initDeps(env) + ipc, err := common.NewIPFS() if err != nil { panic(err) } - e, err := setupServer(env, db, ipc) + e, err := gateway.New(env, ipc) if err != nil { panic(err) } @@ -55,7 +56,6 @@ func rootCmd() *cobra.Command { cmd.Flags().StringVar(&sonrAPIURL, "sonr-api-url", "localhost:1317", "Sonr API URL") cmd.Flags().StringVar(&sonrGrpcURL, "sonr-grpc-url", "localhost:9090", "Sonr gRPC URL") cmd.Flags().StringVar(&sonrRPCURL, "sonr-rpc-url", "localhost:26657", "Sonr RPC URL") - cmd.Flags().StringVar(&sqliteFile, "sqlite-file", "hway.db", "File to store sqlite database") cmd.Flags().StringVar(&psqlHost, "psql-host", "", "PostgresSQL Host") cmd.Flags().StringVar(&psqlUser, "psql-user", "", "PostgresSQL User") cmd.Flags().StringVar(&psqlPass, "psql-pass", "", "PostgresSQL Password") diff --git a/cmd/hway/main.go b/cmd/hway/main.go index faa812dbb..6bf5bb017 100644 --- a/cmd/hway/main.go +++ b/cmd/hway/main.go @@ -5,15 +5,7 @@ import ( "fmt" "os" - "github.com/labstack/echo-contrib/echoprometheus" - "github.com/labstack/echo/v4" - "github.com/labstack/echo/v4/middleware" - "github.com/onsonr/sonr/crypto/ucan" - "github.com/onsonr/sonr/internal/gateway" - config "github.com/onsonr/sonr/pkg/config/hway" - "github.com/onsonr/sonr/pkg/didauth/producer" - "github.com/onsonr/sonr/pkg/ipfsapi" - "gorm.io/gorm" + config "github.com/onsonr/sonr/internal/config/hway" ) // main is the entry point for the application @@ -26,20 +18,6 @@ func main() { os.Exit(0) } -func initDeps(env config.Hway) (*gorm.DB, ipfsapi.Client, error) { - db, err := gateway.NewDB(env) - if err != nil { - return nil, nil, err - } - - ipc, err := ipfsapi.NewClient() - if err != nil { - return nil, nil, err - } - - return db, ipc, nil -} - func loadEnvImplFromArgs(args []string) (config.Hway, error) { cmd := rootCmd() if err := cmd.ParseFlags(args); err != nil { @@ -48,7 +26,6 @@ func loadEnvImplFromArgs(args []string) (config.Hway, error) { env := &config.HwayImpl{ ServePort: servePort, - SqliteFile: sqliteFile, ChainId: chainID, IpfsGatewayUrl: ipfsGatewayURL, SonrApiUrl: sonrAPIURL, @@ -58,15 +35,3 @@ func loadEnvImplFromArgs(args []string) (config.Hway, error) { } return env, nil } - -// setupServer sets up the server -func setupServer(env config.Hway, db *gorm.DB, ipc ipfsapi.Client) (*echo.Echo, error) { - e := echo.New() - e.Use(echoprometheus.NewMiddleware("hway")) - e.IPExtractor = echo.ExtractIPDirect() - e.Use(middleware.Logger()) - e.Use(middleware.Recover()) - e.Use(producer.Middleware(ipc, ucan.ServicePermissions)) - gateway.RegisterRoutes(e, env, db) - return e, nil -} diff --git a/cmd/motr/main.go b/cmd/motr/main.go index c9e30c41c..c19094b79 100644 --- a/cmd/motr/main.go +++ b/cmd/motr/main.go @@ -8,10 +8,9 @@ import ( "syscall/js" "github.com/labstack/echo/v4" - "github.com/onsonr/sonr/internal/vault" - "github.com/onsonr/sonr/pkg/common/wasm" - "github.com/onsonr/sonr/pkg/config/motr" - // "github.com/onsonr/sonr/pkg/didauth/controller" + "github.com/onsonr/sonr/pkg/vault/routes" + "github.com/onsonr/sonr/cmd/motr/wasm" + "github.com/onsonr/sonr/internal/config/motr" ) var ( @@ -28,7 +27,7 @@ func simulateTx(this js.Value, args []js.Value) interface{} { return nil } -func processConfig(this js.Value, args []js.Value) interface{} { +func syncData(this js.Value, args []js.Value) interface{} { if len(args) < 1 { return nil } @@ -45,7 +44,7 @@ func main() { // Load dwn config js.Global().Set("broadcastTx", js.FuncOf(broadcastTx)) js.Global().Set("simulateTx", js.FuncOf(simulateTx)) - js.Global().Set("processConfig", js.FuncOf(processConfig)) + js.Global().Set("syncData", js.FuncOf(syncData)) e := echo.New() e.Use(wasm.ContextMiddleware) diff --git a/pkg/common/wasm/fetch.go b/cmd/motr/wasm/fetch.go similarity index 100% rename from pkg/common/wasm/fetch.go rename to cmd/motr/wasm/fetch.go diff --git a/pkg/common/wasm/wasm.go b/cmd/motr/wasm/wasm.go similarity index 100% rename from pkg/common/wasm/wasm.go rename to cmd/motr/wasm/wasm.go diff --git a/cmd/sonrd/initpkl.go b/cmd/sonrd/pkl_init.go similarity index 100% rename from cmd/sonrd/initpkl.go rename to cmd/sonrd/pkl_init.go diff --git a/crypto/keys/didkey.go b/crypto/keys/didkey.go index ad4d0ec89..8ecb18f7b 100644 --- a/crypto/keys/didkey.go +++ b/crypto/keys/didkey.go @@ -38,6 +38,11 @@ func NewDID(pub crypto.PubKey) (DID, error) { } } +// NewFromPubKey constructs an Identifier from a public key +func NewFromPubKey(pub PubKey) DID { + return DID{PubKey: pub} +} + // MulticodecType indicates the type for this multicodec func (id DID) MulticodecType() uint64 { switch id.Type() { diff --git a/crypto/keys/pubkey.go b/crypto/keys/pubkey.go index b682f7f82..4b64328b8 100644 --- a/crypto/keys/pubkey.go +++ b/crypto/keys/pubkey.go @@ -1,16 +1,21 @@ package keys import ( + "bytes" "crypto/ecdsa" "encoding/hex" + p2pcrypto "github.com/libp2p/go-libp2p/core/crypto" + p2ppb "github.com/libp2p/go-libp2p/core/crypto/pb" "github.com/onsonr/sonr/crypto/core/curves" "golang.org/x/crypto/sha3" ) type PubKey interface { Bytes() []byte - Type() string + Raw() ([]byte, error) + Equals(b p2pcrypto.Key) bool + Type() p2ppb.KeyType Hex() string Verify(msg []byte, sig []byte) (bool, error) } @@ -30,12 +35,31 @@ func (p pubKey) Bytes() []byte { return p.publicPoint.ToAffineCompressed() } +func (p pubKey) Raw() ([]byte, error) { + return p.publicPoint.ToAffineCompressed(), nil +} + +func (p pubKey) Equals(b p2pcrypto.Key) bool { + if b == nil { + return false + } + apbz, err := b.Raw() + if err != nil { + return false + } + bbz, err := p.Raw() + if err != nil { + return false + } + return bytes.Equal(apbz, bbz) +} + func (p pubKey) Hex() string { return hex.EncodeToString(p.publicPoint.ToAffineCompressed()) } -func (p pubKey) Type() string { - return "secp256k1" +func (p pubKey) Type() p2ppb.KeyType { + return p2ppb.KeyType_Secp256k1 } func (p pubKey) Verify(data []byte, sigBz []byte) (bool, error) { diff --git a/crypto/mpc/codec.go b/crypto/mpc/codec.go index bbe8fdfc0..b5f31cf81 100644 --- a/crypto/mpc/codec.go +++ b/crypto/mpc/codec.go @@ -3,6 +3,7 @@ package mpc import ( "github.com/onsonr/sonr/crypto/core/curves" "github.com/onsonr/sonr/crypto/core/protocol" + "github.com/onsonr/sonr/crypto/keys" "github.com/onsonr/sonr/crypto/tecdsa/dklsv1/dkg" ) @@ -20,3 +21,22 @@ type ( RefreshFunc interface{ protocol.Iterator } // RefreshFunc is the type for the refresh function SignFunc interface{ protocol.Iterator } // SignFunc is the type for the sign function ) + +const ( + RoleVal = "validator" + RoleUser = "user" +) + +// Enclave defines the interface for key management operations +type Enclave interface { + Address() string // Address returns the Sonr address of the keyEnclave + DID() keys.DID // DID returns the DID of the keyEnclave + Export(key []byte) ([]byte, error) // Export returns encrypted enclave data + Import(data []byte, key []byte) error // Import decrypts and loads enclave data + IsValid() bool // IsValid returns true if the keyEnclave is valid + PubKey() keys.PubKey // PubKey returns the public key of the keyEnclave + Refresh() (Enclave, error) // Refresh returns a new keyEnclave + Serialize() ([]byte, error) // Serialize returns the serialized keyEnclave + Sign(data []byte) ([]byte, error) // Sign returns the signature of the data + Verify(data []byte, sig []byte) (bool, error) // Verify returns true if the signature is valid +} diff --git a/crypto/mpc/codec_test.go b/crypto/mpc/codec_test.go index ac2a15ba4..39f66d2d5 100644 --- a/crypto/mpc/codec_test.go +++ b/crypto/mpc/codec_test.go @@ -1,6 +1,7 @@ package mpc import ( + "crypto/rand" "strings" "testing" @@ -8,22 +9,74 @@ import ( "github.com/stretchr/testify/require" ) +func randNonce() []byte { + nonce := make([]byte, 12) + rand.Read(nonce) + return nonce +} + func TestKeyShareGeneration(t *testing.T) { t.Run("Generate Valid Enclave", func(t *testing.T) { + nonce := randNonce() // Generate enclave - enclave, err := GenEnclave() + enclave, err := GenEnclave(nonce) require.NoError(t, err) require.NotNil(t, enclave) // Validate enclave contents assert.True(t, enclave.IsValid()) }) + + t.Run("Export and Import", func(t *testing.T) { + nonce := randNonce() + // Generate original enclave + original, err := GenEnclave(nonce) + require.NoError(t, err) + + // Test key for encryption/decryption (32 bytes) + testKey := []byte("test-key-12345678-test-key-123456") + + // Test Export/Import + t.Run("Full Enclave", func(t *testing.T) { + // Export enclave + data, err := original.Export(testKey) + require.NoError(t, err) + require.NotEmpty(t, data) + + // Create new empty enclave + newEnclave, err := GenEnclave(nonce) + require.NoError(t, err) + + // Import enclave + err = newEnclave.Import(data, testKey) + require.NoError(t, err) + + // Verify the imported enclave works by signing + testData := []byte("test message") + sig, err := newEnclave.Sign(testData) + require.NoError(t, err) + valid, err := newEnclave.Verify(testData, sig) + require.NoError(t, err) + assert.True(t, valid) + }) + + // Test Invalid Key + t.Run("Invalid Key", func(t *testing.T) { + data, err := original.Export(testKey) + require.NoError(t, err) + + wrongKey := []byte("wrong-key-12345678") + err = original.Import(data, wrongKey) + assert.Error(t, err) + }) + }) } func TestEnclaveOperations(t *testing.T) { t.Run("Signing and Verification", func(t *testing.T) { + nonce := randNonce() // Generate valid enclave - enclave, err := GenEnclave() + enclave, err := GenEnclave(nonce) require.NoError(t, err) // Test signing @@ -45,7 +98,8 @@ func TestEnclaveOperations(t *testing.T) { }) t.Run("Address and Public Key", func(t *testing.T) { - enclave, err := GenEnclave() + nonce := randNonce() + enclave, err := GenEnclave(nonce) require.NoError(t, err) // Test Address @@ -60,17 +114,18 @@ func TestEnclaveOperations(t *testing.T) { }) t.Run("Refresh Operation", func(t *testing.T) { - enclave, err := GenEnclave() + nonce := randNonce() + enclave, err := GenEnclave(nonce) require.NoError(t, err) // Test refresh refreshedEnclave, err := enclave.Refresh() require.NoError(t, err) require.NotNil(t, refreshedEnclave) - + // Verify refreshed enclave is valid assert.True(t, refreshedEnclave.IsValid()) - + // Verify it maintains the same address assert.Equal(t, enclave.Address(), refreshedEnclave.Address()) }) @@ -78,28 +133,28 @@ func TestEnclaveOperations(t *testing.T) { func TestEnclaveSerialization(t *testing.T) { t.Run("Marshal and Unmarshal", func(t *testing.T) { + nonce := randNonce() // Generate original enclave - original, err := GenEnclave() + original, err := GenEnclave(nonce) require.NoError(t, err) require.NotNil(t, original) // Marshal - keyEnclave, ok := original.(*KeyEnclave) + keyclave, ok := original.(*keyEnclave) require.True(t, ok) - - data, err := keyEnclave.Marshal() + + data, err := keyclave.Serialize() require.NoError(t, err) require.NotEmpty(t, data) // Unmarshal - restored := &KeyEnclave{} + restored := &keyEnclave{} err = restored.Unmarshal(data) require.NoError(t, err) // Verify restored enclave - assert.Equal(t, keyEnclave.Addr, restored.Addr) - assert.True(t, keyEnclave.PubPoint.Equal(restored.PubPoint)) - assert.Equal(t, keyEnclave.VaultCID, restored.VaultCID) + assert.Equal(t, keyclave.Addr, restored.Addr) + assert.True(t, keyclave.PubPoint.Equal(restored.PubPoint)) assert.True(t, restored.IsValid()) }) } diff --git a/crypto/mpc/enclave.go b/crypto/mpc/enclave.go index 197d7d36f..6f1eb1e26 100644 --- a/crypto/mpc/enclave.go +++ b/crypto/mpc/enclave.go @@ -1,63 +1,31 @@ package mpc import ( + "crypto/aes" + "crypto/cipher" "crypto/ecdsa" "encoding/json" + "fmt" "github.com/onsonr/sonr/crypto/core/curves" - "github.com/onsonr/sonr/crypto/core/protocol" "github.com/onsonr/sonr/crypto/keys" - "github.com/onsonr/sonr/crypto/tecdsa/dklsv1" "golang.org/x/crypto/sha3" ) -// Enclave defines the interface for key management operations -type Enclave interface { - Address() string - IsValid() bool - PubKey() keys.PubKey - Refresh() (Enclave, error) - Sign(data []byte) ([]byte, error) - Verify(data []byte, sig []byte) (bool, error) -} - -// KeyEnclave implements the Enclave interface -type KeyEnclave struct { +// keyEnclave implements the Enclave interface +type keyEnclave struct { + // Serialized fields Addr string `json:"address"` PubPoint curves.Point `json:"-"` PubBytes []byte `json:"pub_key"` ValShare Message `json:"val_share"` UserShare Message `json:"user_share"` - VaultCID string `json:"vault_cid,omitempty"` + + // Extra fields + nonce []byte } -// Marshal returns the JSON encoding of KeyEnclave -func (k *KeyEnclave) Marshal() ([]byte, error) { - // Store compressed public point bytes before marshaling - k.PubBytes = k.PubPoint.ToAffineCompressed() - return json.Marshal(k) -} - -// Unmarshal parses the JSON-encoded data and stores the result -func (k *KeyEnclave) Unmarshal(data []byte) error { - if err := json.Unmarshal(data, k); err != nil { - return err - } - // Reconstruct Point from bytes - curve := curves.K256() - point, err := curve.NewIdentityPoint().FromAffineCompressed(k.PubBytes) - if err != nil { - return err - } - k.PubPoint = point - return nil -} - -func (k *KeyEnclave) IsValid() bool { - return k.PubPoint != nil && k.ValShare != nil && k.UserShare != nil && k.Addr != "" -} - -func initKeyEnclave(valShare, userShare Message) (*KeyEnclave, error) { +func newEnclave(valShare, userShare Message, nonce []byte) (Enclave, error) { pubPoint, err := getAlicePubPoint(valShare) if err != nil { return nil, err @@ -67,47 +35,105 @@ func initKeyEnclave(valShare, userShare Message) (*KeyEnclave, error) { if err != nil { return nil, err } - return &KeyEnclave{ + return &keyEnclave{ Addr: addr, PubPoint: pubPoint, ValShare: valShare, UserShare: userShare, + nonce: nonce, }, nil } -func (k *KeyEnclave) Address() string { +// Address returns the Sonr address of the keyEnclave +func (k *keyEnclave) Address() string { return k.Addr } -func (k *KeyEnclave) PubKey() keys.PubKey { +// DID returns the DID of the keyEnclave +func (k *keyEnclave) DID() keys.DID { + return keys.NewFromPubKey(k.PubKey()) +} + +// Export returns encrypted enclave data +func (k *keyEnclave) Export(key []byte) ([]byte, error) { + data, err := k.Serialize() + if err != nil { + return nil, fmt.Errorf("failed to serialize enclave: %w", err) + } + + hashedKey := hashKey(key) + block, err := aes.NewCipher(hashedKey) + if err != nil { + return nil, err + } + + aesgcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + return aesgcm.Seal(nil, k.nonce, data, nil), nil +} + +// Import decrypts and loads enclave data +func (k *keyEnclave) Import(data []byte, key []byte) error { + hashedKey := hashKey(key) + block, err := aes.NewCipher(hashedKey) + if err != nil { + return err + } + + aesgcm, err := cipher.NewGCM(block) + if err != nil { + return err + } + + decrypted, err := aesgcm.Open(nil, k.nonce, data, nil) + if err != nil { + return err + } + + return k.Unmarshal(decrypted) +} + +// IsValid returns true if the keyEnclave is valid +func (k *keyEnclave) IsValid() bool { + return k.PubPoint != nil && k.ValShare != nil && k.UserShare != nil && k.Addr != "" +} + +// PubKey returns the public key of the keyEnclave +func (k *keyEnclave) PubKey() keys.PubKey { return keys.NewPubKey(k.PubPoint) } -func (k *KeyEnclave) Refresh() (Enclave, error) { - refreshFuncVal, err := k.valRefreshFunc() +// Refresh returns a new keyEnclave +func (k *keyEnclave) Refresh() (Enclave, error) { + refreshFuncVal, err := valRefreshFunc(k) if err != nil { return nil, err } - refreshFuncUser, err := k.userRefreshFunc() + refreshFuncUser, err := userRefreshFunc(k) if err != nil { return nil, err } - return ExecuteRefresh(refreshFuncVal, refreshFuncUser) + return ExecuteRefresh(refreshFuncVal, refreshFuncUser, k.nonce) } -func (k *KeyEnclave) Sign(data []byte) ([]byte, error) { - userSign, err := k.userSignFunc(data) +// Sign returns the signature of the data +func (k *keyEnclave) Sign(data []byte) ([]byte, error) { + userSign, err := userSignFunc(k, data) if err != nil { return nil, err } - valSign, err := k.valSignFunc(data) + valSign, err := valSignFunc(k, data) if err != nil { return nil, err } return ExecuteSigning(valSign, userSign) } -func (k *KeyEnclave) Verify(data []byte, sig []byte) (bool, error) { +// Verify returns true if the signature is valid +func (k *keyEnclave) Verify(data []byte, sig []byte) (bool, error) { edSig, err := deserializeSignature(sig) if err != nil { return false, err @@ -121,31 +147,33 @@ func (k *KeyEnclave) Verify(data []byte, sig []byte) (bool, error) { X: ePub.X, Y: ePub.Y, } - + // Hash the message using SHA3-256 hash := sha3.New256() hash.Write(data) digest := hash.Sum(nil) - + return ecdsa.Verify(pk, digest, edSig.R, edSig.S), nil } -func (k *KeyEnclave) userSignFunc(bz []byte) (SignFunc, error) { - curve := curves.K256() - return dklsv1.NewBobSign(curve, sha3.New256(), bz, k.UserShare, protocol.Version1) +// Marshal returns the JSON encoding of keyEnclave +func (k *keyEnclave) Serialize() ([]byte, error) { + // Store compressed public point bytes before marshaling + k.PubBytes = k.PubPoint.ToAffineCompressed() + return json.Marshal(k) } -func (k *KeyEnclave) userRefreshFunc() (RefreshFunc, error) { +// Unmarshal parses the JSON-encoded data and stores the result +func (k *keyEnclave) Unmarshal(data []byte) error { + if err := json.Unmarshal(data, k); err != nil { + return err + } + // Reconstruct Point from bytes curve := curves.K256() - return dklsv1.NewBobRefresh(curve, k.UserShare, protocol.Version1) -} - -func (k *KeyEnclave) valSignFunc(bz []byte) (SignFunc, error) { - curve := curves.K256() - return dklsv1.NewAliceSign(curve, sha3.New256(), bz, k.ValShare, protocol.Version1) -} - -func (k *KeyEnclave) valRefreshFunc() (RefreshFunc, error) { - curve := curves.K256() - return dklsv1.NewAliceRefresh(curve, k.ValShare, protocol.Version1) + point, err := curve.NewIdentityPoint().FromAffineCompressed(k.PubBytes) + if err != nil { + return err + } + k.PubPoint = point + return nil } diff --git a/crypto/mpc/protocol.go b/crypto/mpc/protocol.go index aff0036c5..7c137588b 100644 --- a/crypto/mpc/protocol.go +++ b/crypto/mpc/protocol.go @@ -1,14 +1,13 @@ package mpc import ( - "github.com/ipfs/kubo/client/rpc" "github.com/onsonr/sonr/crypto/core/curves" "github.com/onsonr/sonr/crypto/core/protocol" "github.com/onsonr/sonr/crypto/tecdsa/dklsv1" ) // GenEnclave generates a new MPC keyshare -func GenEnclave() (Enclave, error) { +func GenEnclave(nonce []byte) (Enclave, error) { curve := curves.K256() valKs := dklsv1.NewAliceDkg(curve, protocol.Version1) userKs := dklsv1.NewBobDkg(curve, protocol.Version1) @@ -24,31 +23,7 @@ func GenEnclave() (Enclave, error) { if err != nil { return nil, err } - return initKeyEnclave(valRes, userRes) -} - -// GenEnclaveIPFS generates a new MPC keyshare -func GenEnclaveIPFS(ipc *rpc.HttpApi) (Enclave, error) { - curve := curves.K256() - valKs := dklsv1.NewAliceDkg(curve, protocol.Version1) - userKs := dklsv1.NewBobDkg(curve, protocol.Version1) - aErr, bErr := RunProtocol(userKs, valKs) - if err := checkIteratedErrors(aErr, bErr); err != nil { - return nil, err - } - valRes, err := valKs.Result(protocol.Version1) - if err != nil { - return nil, err - } - userRes, err := userKs.Result(protocol.Version1) - if err != nil { - return nil, err - } - e, err := initKeyEnclave(valRes, userRes) - if err != nil { - return nil, err - } - return addEnclaveIPFS(e, ipc) + return newEnclave(valRes, userRes, nonce) } // ExecuteSigning runs the MPC signing protocol @@ -73,7 +48,7 @@ func ExecuteSigning(signFuncVal SignFunc, signFuncUser SignFunc) ([]byte, error) } // ExecuteRefresh runs the MPC refresh protocol -func ExecuteRefresh(refreshFuncVal RefreshFunc, refreshFuncUser RefreshFunc) (*KeyEnclave, error) { +func ExecuteRefresh(refreshFuncVal RefreshFunc, refreshFuncUser RefreshFunc, nonce []byte) (Enclave, error) { aErr, bErr := RunProtocol(refreshFuncVal, refreshFuncUser) if err := checkIteratedErrors(aErr, bErr); err != nil { return nil, err @@ -86,7 +61,7 @@ func ExecuteRefresh(refreshFuncVal RefreshFunc, refreshFuncUser RefreshFunc) (*K if err != nil { return nil, err } - return initKeyEnclave(valRefreshResult, userRefreshResult) + return newEnclave(valRefreshResult, userRefreshResult, nonce) } // For DKG bob starts first. For refresh and sign, Alice starts first. diff --git a/crypto/mpc/spec/source.go b/crypto/mpc/spec/source.go index d1452ada4..4479b6b62 100644 --- a/crypto/mpc/spec/source.go +++ b/crypto/mpc/spec/source.go @@ -57,9 +57,10 @@ func (k ucanKeyshare) ChainCode() ([]byte, error) { // DefaultOriginToken returns a default token with the keyshare's issuer as the audience func (k ucanKeyshare) OriginToken() (*Token, error) { - att := ucan.NewSmartAccount(k.addr) + // att := ucan.NewSmartAccount(k.addr) zero := time.Time{} - return k.NewOriginToken(k.issuerDID, att, nil, zero, zero) + // return k.NewOriginToken(k.issuerDID, att, nil, zero, zero) + return k.newToken(k.issuerDID, nil, nil, nil, zero, zero) } func (k ucanKeyshare) SignData(data []byte) ([]byte, error) { @@ -101,7 +102,6 @@ func (k ucanKeyshare) UCANParser() *ucan.TokenParser { if key == ucan.CapKey { cap = val } else { - rsc = ucan.NewStringLengthResource(key, val) } } diff --git a/crypto/mpc/utils.go b/crypto/mpc/utils.go index c3c0b3317..783b77669 100644 --- a/crypto/mpc/utils.go +++ b/crypto/mpc/utils.go @@ -1,34 +1,19 @@ package mpc import ( - "context" - "encoding/json" + "crypto/aes" + "crypto/cipher" "errors" "fmt" "math/big" "github.com/cosmos/cosmos-sdk/types/bech32" - "github.com/ipfs/boxo/files" - "github.com/ipfs/kubo/client/rpc" "github.com/onsonr/sonr/crypto/core/curves" "github.com/onsonr/sonr/crypto/core/protocol" "github.com/onsonr/sonr/crypto/tecdsa/dklsv1" + "golang.org/x/crypto/sha3" ) -func addEnclaveIPFS(enclave *KeyEnclave, ipc *rpc.HttpApi) (Enclave, error) { - jsonEnclave, err := json.Marshal(enclave) - if err != nil { - return nil, err - } - // Save enclave to IPFS - cid, err := ipc.Unixfs().Add(context.Background(), files.NewBytesFile(jsonEnclave)) - if err != nil { - return nil, err - } - enclave.VaultCID = cid.String() - return enclave, nil -} - func checkIteratedErrors(aErr, bErr error) error { if aErr == protocol.ErrProtocolFinished && bErr == protocol.ErrProtocolFinished { return nil @@ -51,6 +36,47 @@ func computeSonrAddr(pp Point) (string, error) { return sonrAddr, nil } +func hashKey(key []byte) []byte { + hash := sha3.New256() + hash.Write(key) + return hash.Sum(nil)[:32] // Use first 32 bytes of hash +} + +func decryptKeyshare(msg []byte, key []byte, nonce []byte) ([]byte, error) { + hashedKey := hashKey(key) + block, err := aes.NewCipher(hashedKey) + if err != nil { + return nil, err + } + aesgcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + plaintext, err := aesgcm.Open(nil, nonce, msg, nil) + if err != nil { + return nil, err + } + return plaintext, nil +} + +func encryptKeyshare(msg Message, key []byte, nonce []byte) ([]byte, error) { + hashedKey := hashKey(key) + msgBytes, err := protocol.EncodeMessage(msg) + if err != nil { + return nil, err + } + block, err := aes.NewCipher(hashedKey) + if err != nil { + return nil, err + } + aesgcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + ciphertext := aesgcm.Seal(nil, nonce, []byte(msgBytes), nil) + return ciphertext, nil +} + func getAliceOut(msg *protocol.Message) (AliceOut, error) { return dklsv1.DecodeAliceDkgResult(msg) } @@ -122,3 +148,23 @@ func deserializeSignature(sigBytes []byte) (*curves.EcdsaSignature, error) { S: s, }, nil } + +func userSignFunc(k *keyEnclave, bz []byte) (SignFunc, error) { + curve := curves.K256() + return dklsv1.NewBobSign(curve, sha3.New256(), bz, k.UserShare, protocol.Version1) +} + +func userRefreshFunc(k *keyEnclave) (RefreshFunc, error) { + curve := curves.K256() + return dklsv1.NewBobRefresh(curve, k.UserShare, protocol.Version1) +} + +func valSignFunc(k *keyEnclave, bz []byte) (SignFunc, error) { + curve := curves.K256() + return dklsv1.NewAliceSign(curve, sha3.New256(), bz, k.ValShare, protocol.Version1) +} + +func valRefreshFunc(k *keyEnclave) (RefreshFunc, error) { + curve := curves.K256() + return dklsv1.NewAliceRefresh(curve, k.ValShare, protocol.Version1) +} diff --git a/crypto/ucan/attenuation.go b/crypto/ucan/attenuation.go index 43a8d0ad2..eb25afa22 100644 --- a/crypto/ucan/attenuation.go +++ b/crypto/ucan/attenuation.go @@ -73,32 +73,6 @@ type Resource interface { Contains(b Resource) bool } -type stringLengthRsc struct { - t string - v string -} - -// NewStringLengthResource is a silly implementation of resource to use while -// I figure out what an OR filter on strings is. Don't use this. -func NewStringLengthResource(typ, val string) Resource { - return stringLengthRsc{ - t: typ, - v: val, - } -} - -func (r stringLengthRsc) Type() string { - return r.t -} - -func (r stringLengthRsc) Value() string { - return r.v -} - -func (r stringLengthRsc) Contains(b Resource) bool { - return r.Type() == b.Type() && len(r.Value()) <= len(b.Value()) -} - // Capability is an action users can perform type Capability interface { // A Capability must be expressable as a string diff --git a/crypto/ucan/attenuation_test.go b/crypto/ucan/attenuation_test.go deleted file mode 100644 index 0d0c3e77c..000000000 --- a/crypto/ucan/attenuation_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package ucan - -import ( - "encoding/json" - "fmt" - "testing" -) - -func TestAttenuationsContains(t *testing.T) { - aContains := [][2]string{ - { - `[ - { "cap": "SUPER_USER", "dataset": "b5/world_bank_population"}, - { "cap": "OVERWRITE", "api": "https://api.qri.cloud" } - ]`, - `[ - {"cap": "SOFT_DELETE", "dataset": "b5/world_bank_population" } - ]`, - }, - { - `[ - { "cap": "SUPER_USER", "dataset": "b5/world_bank_population"}, - { "cap": "OVERWRITE", "api": "https://api.qri.cloud" } - ]`, - `[ - {"cap": "SUPER_USER", "dataset": "b5/world_bank_population" } - ]`, - }, - } - - for i, c := range aContains { - t.Run(fmt.Sprintf("contains_%d", i), func(t *testing.T) { - a := testAttenuations(c[0]) - b := testAttenuations(c[1]) - if !a.Contains(b) { - t.Errorf("expected a attenuations to contain b attenuations") - } - }) - } - - aNotContains := [][2]string{ - { - `[ - { "cap": "SUPER_USER", "dataset": "b5/world_bank_population"}, - { "cap": "OVERWRITE", "api": "https://api.qri.cloud" } - ]`, - `[ - { "cap": "CREATE", "dataset": "b5" } - ]`, - }, - } - - for i, c := range aNotContains { - t.Run(fmt.Sprintf("not_contains_%d", i), func(t *testing.T) { - a := testAttenuations(c[0]) - b := testAttenuations(c[1]) - if a.Contains(b) { - t.Errorf("expected a attenuations to NOT contain b attenuations") - } - }) - } -} - -func mustJSON(data string, v interface{}) { - if err := json.Unmarshal([]byte(data), v); err != nil { - panic(err) - } -} - -func testAttenuations(data string) Attenuations { - caps := NewNestedCapabilities("SUPER_USER", "OVERWRITE", "SOFT_DELETE", "REVISE", "CREATE") - - v := []map[string]string{} - mustJSON(data, &v) - - var att Attenuations - for _, x := range v { - var cap Capability - var rsc Resource - for key, val := range x { - switch key { - case CapKey: - cap = caps.Cap(val) - default: - rsc = NewStringLengthResource(key, val) - } - } - att = append(att, Attenuation{cap, rsc}) - } - - return att -} - -func TestNestedCapabilities(t *testing.T) { - -} diff --git a/crypto/ucan/attns/capability/Capability.pkl.go b/crypto/ucan/attns/capability/Capability.pkl.go deleted file mode 100644 index 621a0100f..000000000 --- a/crypto/ucan/attns/capability/Capability.pkl.go +++ /dev/null @@ -1,79 +0,0 @@ -// Code generated from Pkl module `sonr.orm.UCAN`. DO NOT EDIT. -package capability - -import ( - "encoding" - "fmt" -) - -type Capability string - -const ( - CAPOWNER Capability = "CAP_OWNER" - CAPOPERATOR Capability = "CAP_OPERATOR" - CAPOBSERVER Capability = "CAP_OBSERVER" - CAPAUTHENTICATE Capability = "CAP_AUTHENTICATE" - CAPAUTHORIZE Capability = "CAP_AUTHORIZE" - CAPDELEGATE Capability = "CAP_DELEGATE" - CAPINVOKE Capability = "CAP_INVOKE" - CAPEXECUTE Capability = "CAP_EXECUTE" - CAPPROPOSE Capability = "CAP_PROPOSE" - CAPSIGN Capability = "CAP_SIGN" - CAPSETPOLICY Capability = "CAP_SET_POLICY" - CAPSETTHRESHOLD Capability = "CAP_SET_THRESHOLD" - CAPRECOVER Capability = "CAP_RECOVER" - CAPSOCIAL Capability = "CAP_SOCIAL" - CAPVOTE Capability = "CAP_VOTE" - CAPRESOLVER Capability = "CAP_RESOLVER" - CAPPRODUCER Capability = "CAP_PRODUCER" -) - -// String returns the string representation of Capability -func (rcv Capability) String() string { - return string(rcv) -} - -var _ encoding.BinaryUnmarshaler = new(Capability) - -// UnmarshalBinary implements encoding.BinaryUnmarshaler for Capability. -func (rcv *Capability) UnmarshalBinary(data []byte) error { - switch str := string(data); str { - case "CAP_OWNER": - *rcv = CAPOWNER - case "CAP_OPERATOR": - *rcv = CAPOPERATOR - case "CAP_OBSERVER": - *rcv = CAPOBSERVER - case "CAP_AUTHENTICATE": - *rcv = CAPAUTHENTICATE - case "CAP_AUTHORIZE": - *rcv = CAPAUTHORIZE - case "CAP_DELEGATE": - *rcv = CAPDELEGATE - case "CAP_INVOKE": - *rcv = CAPINVOKE - case "CAP_EXECUTE": - *rcv = CAPEXECUTE - case "CAP_PROPOSE": - *rcv = CAPPROPOSE - case "CAP_SIGN": - *rcv = CAPSIGN - case "CAP_SET_POLICY": - *rcv = CAPSETPOLICY - case "CAP_SET_THRESHOLD": - *rcv = CAPSETTHRESHOLD - case "CAP_RECOVER": - *rcv = CAPRECOVER - case "CAP_SOCIAL": - *rcv = CAPSOCIAL - case "CAP_VOTE": - *rcv = CAPVOTE - case "CAP_RESOLVER": - *rcv = CAPRESOLVER - case "CAP_PRODUCER": - *rcv = CAPPRODUCER - default: - return fmt.Errorf(`illegal: "%s" is not a valid Capability`, str) - } - return nil -} diff --git a/crypto/ucan/attns/capaccount/CapAccount.pkl.go b/crypto/ucan/attns/capaccount/CapAccount.pkl.go new file mode 100644 index 000000000..fdaff7e0f --- /dev/null +++ b/crypto/ucan/attns/capaccount/CapAccount.pkl.go @@ -0,0 +1,49 @@ +// Code generated from Pkl module `sonr.orm.UCAN`. DO NOT EDIT. +package capaccount + +import ( + "encoding" + "fmt" +) + +type CapAccount string + +const ( + ExecBroadcast CapAccount = "exec/broadcast" + ExecQuery CapAccount = "exec/query" + ExecSimulate CapAccount = "exec/simulate" + ExecVote CapAccount = "exec/vote" + ExecDelegate CapAccount = "exec/delegate" + ExecInvoke CapAccount = "exec/invoke" + ExecSend CapAccount = "exec/send" +) + +// String returns the string representation of CapAccount +func (rcv CapAccount) String() string { + return string(rcv) +} + +var _ encoding.BinaryUnmarshaler = new(CapAccount) + +// UnmarshalBinary implements encoding.BinaryUnmarshaler for CapAccount. +func (rcv *CapAccount) UnmarshalBinary(data []byte) error { + switch str := string(data); str { + case "exec/broadcast": + *rcv = ExecBroadcast + case "exec/query": + *rcv = ExecQuery + case "exec/simulate": + *rcv = ExecSimulate + case "exec/vote": + *rcv = ExecVote + case "exec/delegate": + *rcv = ExecDelegate + case "exec/invoke": + *rcv = ExecInvoke + case "exec/send": + *rcv = ExecSend + default: + return fmt.Errorf(`illegal: "%s" is not a valid CapAccount`, str) + } + return nil +} diff --git a/crypto/ucan/attns/capaccount/caps.go b/crypto/ucan/attns/capaccount/caps.go new file mode 100644 index 000000000..53839351f --- /dev/null +++ b/crypto/ucan/attns/capaccount/caps.go @@ -0,0 +1,11 @@ +package capaccount + +import "github.com/onsonr/sonr/crypto/ucan" + +func NewCap(ty CapAccount) ucan.Capability { + return ucan.Capability(ty) +} + +func (c CapAccount) Contains(b ucan.Capability) bool { + return c.String() == b.String() +} diff --git a/crypto/ucan/attns/capinterchain/CapInterchain.pkl.go b/crypto/ucan/attns/capinterchain/CapInterchain.pkl.go new file mode 100644 index 000000000..4048860cc --- /dev/null +++ b/crypto/ucan/attns/capinterchain/CapInterchain.pkl.go @@ -0,0 +1,43 @@ +// Code generated from Pkl module `sonr.orm.UCAN`. DO NOT EDIT. +package capinterchain + +import ( + "encoding" + "fmt" +) + +type CapInterchain string + +const ( + TransferSwap CapInterchain = "transfer/swap" + TransferSend CapInterchain = "transfer/send" + TransferAtomic CapInterchain = "transfer/atomic" + TransferBatch CapInterchain = "transfer/batch" + TransferP2p CapInterchain = "transfer/p2p" +) + +// String returns the string representation of CapInterchain +func (rcv CapInterchain) String() string { + return string(rcv) +} + +var _ encoding.BinaryUnmarshaler = new(CapInterchain) + +// UnmarshalBinary implements encoding.BinaryUnmarshaler for CapInterchain. +func (rcv *CapInterchain) UnmarshalBinary(data []byte) error { + switch str := string(data); str { + case "transfer/swap": + *rcv = TransferSwap + case "transfer/send": + *rcv = TransferSend + case "transfer/atomic": + *rcv = TransferAtomic + case "transfer/batch": + *rcv = TransferBatch + case "transfer/p2p": + *rcv = TransferP2p + default: + return fmt.Errorf(`illegal: "%s" is not a valid CapInterchain`, str) + } + return nil +} diff --git a/crypto/ucan/attns/capinterchain/caps.go b/crypto/ucan/attns/capinterchain/caps.go new file mode 100644 index 000000000..5f0a4aa1c --- /dev/null +++ b/crypto/ucan/attns/capinterchain/caps.go @@ -0,0 +1,11 @@ +package capinterchain + +import "github.com/onsonr/sonr/crypto/ucan" + +func NewCap(ty CapInterchain) ucan.Capability { + return ucan.Capability(ty) +} + +func (c CapInterchain) Contains(b ucan.Capability) bool { + return c.String() == b.String() +} diff --git a/crypto/ucan/attns/capvault/CapVault.pkl.go b/crypto/ucan/attns/capvault/CapVault.pkl.go new file mode 100644 index 000000000..6c2c53e8e --- /dev/null +++ b/crypto/ucan/attns/capvault/CapVault.pkl.go @@ -0,0 +1,49 @@ +// Code generated from Pkl module `sonr.orm.UCAN`. DO NOT EDIT. +package capvault + +import ( + "encoding" + "fmt" +) + +type CapVault string + +const ( + CrudAsset CapVault = "crud/asset" + CrudAuthzgrant CapVault = "crud/authzgrant" + CrudProfile CapVault = "crud/profile" + CrudRecord CapVault = "crud/record" + UseRecovery CapVault = "use/recovery" + UseSync CapVault = "use/sync" + UseSigner CapVault = "use/signer" +) + +// String returns the string representation of CapVault +func (rcv CapVault) String() string { + return string(rcv) +} + +var _ encoding.BinaryUnmarshaler = new(CapVault) + +// UnmarshalBinary implements encoding.BinaryUnmarshaler for CapVault. +func (rcv *CapVault) UnmarshalBinary(data []byte) error { + switch str := string(data); str { + case "crud/asset": + *rcv = CrudAsset + case "crud/authzgrant": + *rcv = CrudAuthzgrant + case "crud/profile": + *rcv = CrudProfile + case "crud/record": + *rcv = CrudRecord + case "use/recovery": + *rcv = UseRecovery + case "use/sync": + *rcv = UseSync + case "use/signer": + *rcv = UseSigner + default: + return fmt.Errorf(`illegal: "%s" is not a valid CapVault`, str) + } + return nil +} diff --git a/crypto/ucan/attns/capvault/caps.go b/crypto/ucan/attns/capvault/caps.go new file mode 100644 index 000000000..927b449d2 --- /dev/null +++ b/crypto/ucan/attns/capvault/caps.go @@ -0,0 +1,11 @@ +package capvault + +import "github.com/onsonr/sonr/crypto/ucan" + +func NewCap(ty CapVault) ucan.Capability { + return ucan.Capability(ty) +} + +func (c CapVault) Contains(b ucan.Capability) bool { + return c.String() == b.String() +} diff --git a/crypto/ucan/attns/exports.go b/crypto/ucan/attns/exports.go new file mode 100644 index 000000000..d474dd805 --- /dev/null +++ b/crypto/ucan/attns/exports.go @@ -0,0 +1,114 @@ +// Package attns implements the UCAN resource and capability types +package attns + +import ( + "github.com/onsonr/sonr/crypto/ucan" + "github.com/onsonr/sonr/crypto/ucan/attns/capaccount" + "github.com/onsonr/sonr/crypto/ucan/attns/capinterchain" + "github.com/onsonr/sonr/crypto/ucan/attns/capvault" + "github.com/onsonr/sonr/crypto/ucan/attns/resaccount" + "github.com/onsonr/sonr/crypto/ucan/attns/resinterchain" + "github.com/onsonr/sonr/crypto/ucan/attns/resvault" +) + +// Capability hierarchy for sonr network +// ------------------------------------- +// VAULT (DWN) +// +// └─ CRUD/ASSET +// └─ CRUD/AUTHZGRANT +// └─ CRUD/PROFILE +// └─ CRUD/RECORD +// └─ USE/RECOVERY +// └─ USE/SYNC +// └─ USE/SIGNER +// +// ACCOUNT (DID) +// +// └─ EXEC/BROADCAST +// └─ EXEC/QUERY +// └─ EXEC/SIMULATE +// └─ EXEC/VOTE +// └─ EXEC/DELEGATE +// └─ EXEC/INVOKE +// └─ EXEC/SEND +// +// INTERCHAIN +// +// └─ TRANSFER/SWAP +// └─ TRANSFER/SEND +// └─ TRANSFER/ATOMIC +// └─ TRANSFER/BATCH +// └─ TRANSFER/P2P +// └─ TRANSFER/SEND + +type Capability string + +const ( + CapExecBroadcast = capaccount.ExecBroadcast + CapExecQuery = capaccount.ExecQuery + CapExecSimulate = capaccount.ExecSimulate + CapExecVote = capaccount.ExecVote + CapExecDelegate = capaccount.ExecDelegate + CapExecInvoke = capaccount.ExecInvoke + CapExecSend = capaccount.ExecSend + + CapTransferSwap = capinterchain.TransferSwap + CapTransferSend = capinterchain.TransferSend + CapTransferAtomic = capinterchain.TransferAtomic + CapTransferBatch = capinterchain.TransferBatch + CapTransferP2P = capinterchain.TransferP2p + + CapCrudAsset = capvault.CrudAsset + CapCrudAuthzgrant = capvault.CrudAuthzgrant + CapCrudProfile = capvault.CrudProfile + CapCrudRecord = capvault.CrudRecord + CapUseRecovery = capvault.UseRecovery + CapUseSync = capvault.UseSync + CapUseSigner = capvault.UseSigner +) + +type NewCapFunc func(string) ucan.Capability + +type BuildResourceFunc func(string, string) ucan.Resource + +func CreateArray(attns ...ucan.Attenuation) ucan.Attenuations { + return ucan.Attenuations(attns) +} + +func New(cap ucan.Capability, rsc ucan.Resource) ucan.Attenuation { + return ucan.Attenuation{ + Cap: cap, + Rsc: rsc, + } +} + +// NewAccountCap creates a new account capability +func NewAccountCap(ty capaccount.CapAccount) ucan.Capability { + return capaccount.NewCap(ty) +} + +// NewInterchainCap creates a new interchain capability +func NewInterchainCap(ty capinterchain.CapInterchain) ucan.Capability { + return capinterchain.NewCap(ty) +} + +// NewVaultCap creates a new vault capability +func NewVaultCap(ty capvault.CapVault) ucan.Capability { + return capvault.NewCap(ty) +} + +// BuildAccountResource creates a new account resource +func BuildAccountResource(ty resaccount.ResAccount, value string) ucan.Resource { + return resaccount.Build(ty, value) +} + +// BuildInterchainResource creates a new interchain resource +func BuildInterchainResource(ty resinterchain.ResInterchain, value string) ucan.Resource { + return resinterchain.Build(ty, value) +} + +// BuildVaultResource creates a new vault resource +func BuildVaultResource(ty resvault.ResVault, value string) ucan.Resource { + return resvault.Build(ty, value) +} diff --git a/crypto/ucan/attns/policytype/PolicyType.pkl.go b/crypto/ucan/attns/policytype/PolicyType.pkl.go deleted file mode 100644 index 02fc6403e..000000000 --- a/crypto/ucan/attns/policytype/PolicyType.pkl.go +++ /dev/null @@ -1,40 +0,0 @@ -// Code generated from Pkl module `sonr.orm.UCAN`. DO NOT EDIT. -package policytype - -import ( - "encoding" - "fmt" -) - -type PolicyType string - -const ( - POLICYTHRESHOLD PolicyType = "POLICY_THRESHOLD" - POLICYTIMELOCK PolicyType = "POLICY_TIMELOCK" - POLICYWHITELIST PolicyType = "POLICY_WHITELIST" - POLICYKEYGEN PolicyType = "POLICY_KEYGEN" -) - -// String returns the string representation of PolicyType -func (rcv PolicyType) String() string { - return string(rcv) -} - -var _ encoding.BinaryUnmarshaler = new(PolicyType) - -// UnmarshalBinary implements encoding.BinaryUnmarshaler for PolicyType. -func (rcv *PolicyType) UnmarshalBinary(data []byte) error { - switch str := string(data); str { - case "POLICY_THRESHOLD": - *rcv = POLICYTHRESHOLD - case "POLICY_TIMELOCK": - *rcv = POLICYTIMELOCK - case "POLICY_WHITELIST": - *rcv = POLICYWHITELIST - case "POLICY_KEYGEN": - *rcv = POLICYKEYGEN - default: - return fmt.Errorf(`illegal: "%s" is not a valid PolicyType`, str) - } - return nil -} diff --git a/crypto/ucan/attns/resaccount/ResAccount.pkl.go b/crypto/ucan/attns/resaccount/ResAccount.pkl.go new file mode 100644 index 000000000..29d8aa24c --- /dev/null +++ b/crypto/ucan/attns/resaccount/ResAccount.pkl.go @@ -0,0 +1,43 @@ +// Code generated from Pkl module `sonr.orm.UCAN`. DO NOT EDIT. +package resaccount + +import ( + "encoding" + "fmt" +) + +type ResAccount string + +const ( + AccSequence ResAccount = "acc/sequence" + AccNumber ResAccount = "acc/number" + ChainId ResAccount = "chain/id" + AssetCode ResAccount = "asset/code" + AuthzGrant ResAccount = "authz/grant" +) + +// String returns the string representation of ResAccount +func (rcv ResAccount) String() string { + return string(rcv) +} + +var _ encoding.BinaryUnmarshaler = new(ResAccount) + +// UnmarshalBinary implements encoding.BinaryUnmarshaler for ResAccount. +func (rcv *ResAccount) UnmarshalBinary(data []byte) error { + switch str := string(data); str { + case "acc/sequence": + *rcv = AccSequence + case "acc/number": + *rcv = AccNumber + case "chain/id": + *rcv = ChainId + case "asset/code": + *rcv = AssetCode + case "authz/grant": + *rcv = AuthzGrant + default: + return fmt.Errorf(`illegal: "%s" is not a valid ResAccount`, str) + } + return nil +} diff --git a/crypto/ucan/attns/resaccount/resource.go b/crypto/ucan/attns/resaccount/resource.go new file mode 100644 index 000000000..e088cc378 --- /dev/null +++ b/crypto/ucan/attns/resaccount/resource.go @@ -0,0 +1,33 @@ +package resaccount + +import "github.com/onsonr/sonr/crypto/ucan" + +func Build(ty ResAccount, value string) ucan.Resource { + return newStringLengthResource(ty.String(), value) +} + +type stringLengthRsc struct { + t string + v string +} + +// NewStringLengthResource is a silly implementation of resource to use while +// I figure out what an OR filter on strings is. Don't use this. +func newStringLengthResource(typ, val string) ucan.Resource { + return stringLengthRsc{ + t: typ, + v: val, + } +} + +func (r stringLengthRsc) Type() string { + return r.t +} + +func (r stringLengthRsc) Value() string { + return r.v +} + +func (r stringLengthRsc) Contains(b ucan.Resource) bool { + return r.Type() == b.Type() && len(r.Value()) <= len(b.Value()) +} diff --git a/crypto/ucan/attns/resinterchain/ResInterchain.pkl.go b/crypto/ucan/attns/resinterchain/ResInterchain.pkl.go new file mode 100644 index 000000000..4d16806e6 --- /dev/null +++ b/crypto/ucan/attns/resinterchain/ResInterchain.pkl.go @@ -0,0 +1,43 @@ +// Code generated from Pkl module `sonr.orm.UCAN`. DO NOT EDIT. +package resinterchain + +import ( + "encoding" + "fmt" +) + +type ResInterchain string + +const ( + ChannnelPort ResInterchain = "channnel/port" + ChainId ResInterchain = "chain/id" + ChainName ResInterchain = "chain/name" + AccHost ResInterchain = "acc/host" + AccController ResInterchain = "acc/controller" +) + +// String returns the string representation of ResInterchain +func (rcv ResInterchain) String() string { + return string(rcv) +} + +var _ encoding.BinaryUnmarshaler = new(ResInterchain) + +// UnmarshalBinary implements encoding.BinaryUnmarshaler for ResInterchain. +func (rcv *ResInterchain) UnmarshalBinary(data []byte) error { + switch str := string(data); str { + case "channnel/port": + *rcv = ChannnelPort + case "chain/id": + *rcv = ChainId + case "chain/name": + *rcv = ChainName + case "acc/host": + *rcv = AccHost + case "acc/controller": + *rcv = AccController + default: + return fmt.Errorf(`illegal: "%s" is not a valid ResInterchain`, str) + } + return nil +} diff --git a/crypto/ucan/attns/resinterchain/resource.go b/crypto/ucan/attns/resinterchain/resource.go new file mode 100644 index 000000000..87bcdb277 --- /dev/null +++ b/crypto/ucan/attns/resinterchain/resource.go @@ -0,0 +1,33 @@ +package resinterchain + +import "github.com/onsonr/sonr/crypto/ucan" + +func Build(ty ResInterchain, value string) ucan.Resource { + return newStringLengthResource(ty.String(), value) +} + +type stringLengthRsc struct { + t string + v string +} + +// NewStringLengthResource is a silly implementation of resource to use while +// I figure out what an OR filter on strings is. Don't use this. +func newStringLengthResource(typ, val string) ucan.Resource { + return stringLengthRsc{ + t: typ, + v: val, + } +} + +func (r stringLengthRsc) Type() string { + return r.t +} + +func (r stringLengthRsc) Value() string { + return r.v +} + +func (r stringLengthRsc) Contains(b ucan.Resource) bool { + return r.Type() == b.Type() && len(r.Value()) <= len(b.Value()) +} diff --git a/crypto/ucan/attns/resourcetype/ResourceType.pkl.go b/crypto/ucan/attns/resourcetype/ResourceType.pkl.go deleted file mode 100644 index 1d3695f05..000000000 --- a/crypto/ucan/attns/resourcetype/ResourceType.pkl.go +++ /dev/null @@ -1,52 +0,0 @@ -// Code generated from Pkl module `sonr.orm.UCAN`. DO NOT EDIT. -package resourcetype - -import ( - "encoding" - "fmt" -) - -type ResourceType string - -const ( - RESACCOUNT ResourceType = "RES_ACCOUNT" - RESTRANSACTION ResourceType = "RES_TRANSACTION" - RESPOLICY ResourceType = "RES_POLICY" - RESRECOVERY ResourceType = "RES_RECOVERY" - RESVAULT ResourceType = "RES_VAULT" - RESIPFS ResourceType = "RES_IPFS" - RESIPNS ResourceType = "RES_IPNS" - RESKEYSHARE ResourceType = "RES_KEYSHARE" -) - -// String returns the string representation of ResourceType -func (rcv ResourceType) String() string { - return string(rcv) -} - -var _ encoding.BinaryUnmarshaler = new(ResourceType) - -// UnmarshalBinary implements encoding.BinaryUnmarshaler for ResourceType. -func (rcv *ResourceType) UnmarshalBinary(data []byte) error { - switch str := string(data); str { - case "RES_ACCOUNT": - *rcv = RESACCOUNT - case "RES_TRANSACTION": - *rcv = RESTRANSACTION - case "RES_POLICY": - *rcv = RESPOLICY - case "RES_RECOVERY": - *rcv = RESRECOVERY - case "RES_VAULT": - *rcv = RESVAULT - case "RES_IPFS": - *rcv = RESIPFS - case "RES_IPNS": - *rcv = RESIPNS - case "RES_KEYSHARE": - *rcv = RESKEYSHARE - default: - return fmt.Errorf(`illegal: "%s" is not a valid ResourceType`, str) - } - return nil -} diff --git a/crypto/ucan/attns/resvault/ResVault.pkl.go b/crypto/ucan/attns/resvault/ResVault.pkl.go new file mode 100644 index 000000000..f70518245 --- /dev/null +++ b/crypto/ucan/attns/resvault/ResVault.pkl.go @@ -0,0 +1,46 @@ +// Code generated from Pkl module `sonr.orm.UCAN`. DO NOT EDIT. +package resvault + +import ( + "encoding" + "fmt" +) + +type ResVault string + +const ( + KsEnclave ResVault = "ks/enclave" + LocCid ResVault = "loc/cid" + LocEntity ResVault = "loc/entity" + LocIpns ResVault = "loc/ipns" + AddrSonr ResVault = "addr/sonr" + ChainCode ResVault = "chain/code" +) + +// String returns the string representation of ResVault +func (rcv ResVault) String() string { + return string(rcv) +} + +var _ encoding.BinaryUnmarshaler = new(ResVault) + +// UnmarshalBinary implements encoding.BinaryUnmarshaler for ResVault. +func (rcv *ResVault) UnmarshalBinary(data []byte) error { + switch str := string(data); str { + case "ks/enclave": + *rcv = KsEnclave + case "loc/cid": + *rcv = LocCid + case "loc/entity": + *rcv = LocEntity + case "loc/ipns": + *rcv = LocIpns + case "addr/sonr": + *rcv = AddrSonr + case "chain/code": + *rcv = ChainCode + default: + return fmt.Errorf(`illegal: "%s" is not a valid ResVault`, str) + } + return nil +} diff --git a/crypto/ucan/attns/resvault/resource.go b/crypto/ucan/attns/resvault/resource.go new file mode 100644 index 000000000..799a88b4f --- /dev/null +++ b/crypto/ucan/attns/resvault/resource.go @@ -0,0 +1,33 @@ +package resvault + +import "github.com/onsonr/sonr/crypto/ucan" + +func Build(ty ResVault, value string) ucan.Resource { + return newStringLengthResource(ty.String(), value) +} + +type stringLengthRsc struct { + t string + v string +} + +// NewStringLengthResource is a silly implementation of resource to use while +// I figure out what an OR filter on strings is. Don't use this. +func newStringLengthResource(typ, val string) ucan.Resource { + return stringLengthRsc{ + t: typ, + v: val, + } +} + +func (r stringLengthRsc) Type() string { + return r.t +} + +func (r stringLengthRsc) Value() string { + return r.v +} + +func (r stringLengthRsc) Contains(b ucan.Resource) bool { + return r.Type() == b.Type() && len(r.Value()) <= len(b.Value()) +} diff --git a/crypto/ucan/codec.go b/crypto/ucan/codec.go deleted file mode 100644 index 8a757a1f7..000000000 --- a/crypto/ucan/codec.go +++ /dev/null @@ -1,150 +0,0 @@ -package ucan - -import ( - "fmt" - - "github.com/onsonr/sonr/crypto/mpc" - "github.com/onsonr/sonr/crypto/ucan/attns/capability" - "github.com/onsonr/sonr/crypto/ucan/attns/policytype" - "github.com/onsonr/sonr/crypto/ucan/attns/resourcetype" -) - -// NewSmartAccount creates default attenuations for a smart account -func NewSmartAccount( - accountAddr string, -) Attenuations { - caps := AccountPermissions.GetCapabilities() - return Attenuations{ - // Owner capabilities - {Cap: caps.Cap(CapOwner.String()), Rsc: NewResource(ResAccount, accountAddr)}, - - // Operation capabilities - {Cap: caps.Cap(capability.CAPEXECUTE.String()), Rsc: NewResource(ResTransaction, fmt.Sprintf("%s:*", accountAddr))}, - {Cap: caps.Cap(capability.CAPPROPOSE.String()), Rsc: NewResource(ResTransaction, fmt.Sprintf("%s:*", accountAddr))}, - {Cap: caps.Cap(capability.CAPSIGN.String()), Rsc: NewResource(ResTransaction, fmt.Sprintf("%s:*", accountAddr))}, - - // Policy capabilities - {Cap: caps.Cap(capability.CAPSETPOLICY.String()), Rsc: NewResource(ResPolicy, fmt.Sprintf("%s:*", accountAddr))}, - {Cap: caps.Cap(capability.CAPSETTHRESHOLD.String()), Rsc: NewResource(ResPolicy, fmt.Sprintf("%s:threshold", accountAddr))}, - } -} - -// NewSmartAccountPolicy creates attenuations for policy management -func NewSmartAccountPolicy( - accountAddr string, - policyType policytype.PolicyType, -) Attenuations { - caps := AccountPermissions.GetCapabilities() - return Attenuations{ - { - Cap: caps.Cap(capability.CAPSETPOLICY.String()), - Rsc: NewResource( - ResPolicy, - fmt.Sprintf("%s:%s", accountAddr, policyType), - ), - }, - } -} - -// SmartAccountCapabilities defines the capability hierarchy -func SmartAccountCapabilities() []string { - return []string{ - CapOwner.String(), - CapOperator.String(), - CapObserver.String(), - CapExecute.String(), - CapPropose.String(), - CapSign.String(), - CapSetPolicy.String(), - CapSetThreshold.String(), - CapRecover.String(), - CapSocial.String(), - } -} - -// CreateVaultAttenuations creates default attenuations for a smart account -func NewService( - origin string, -) Attenuations { - caps := ServicePermissions.GetCapabilities() - return Attenuations{ - // Owner capabilities - {Cap: caps.Cap(capability.CAPOWNER.String()), Rsc: NewResource(resourcetype.RESACCOUNT, origin)}, - - // Operation capabilities - {Cap: caps.Cap(capability.CAPEXECUTE.String()), Rsc: NewResource(resourcetype.RESTRANSACTION, fmt.Sprintf("%s:*", origin))}, - {Cap: caps.Cap(capability.CAPPROPOSE.String()), Rsc: NewResource(resourcetype.RESTRANSACTION, fmt.Sprintf("%s:*", origin))}, - {Cap: caps.Cap(capability.CAPSIGN.String()), Rsc: NewResource(resourcetype.RESTRANSACTION, fmt.Sprintf("%s:*", origin))}, - - // Policy capabilities - {Cap: caps.Cap(capability.CAPSETPOLICY.String()), Rsc: NewResource(resourcetype.RESPOLICY, fmt.Sprintf("%s:*", origin))}, - {Cap: caps.Cap(capability.CAPSETTHRESHOLD.String()), Rsc: NewResource(resourcetype.RESPOLICY, fmt.Sprintf("%s:threshold", origin))}, - } -} - -// ServiceCapabilities defines the capability hierarchy -func ServiceCapabilities() []string { - return []string{ - CapOwner.String(), - CapOperator.String(), - CapObserver.String(), - CapExecute.String(), - CapPropose.String(), - CapSign.String(), - CapResolver.String(), - CapProducer.String(), - } -} - -// NewVault creates default attenuations for a smart account -func NewVault( - kss mpc.KeyEnclave, -) Attenuations { - accountAddr := kss.Address() - caps := VaultPermissions.GetCapabilities() - return Attenuations{ - // Owner capabilities - {Cap: caps.Cap(capability.CAPOWNER.String()), Rsc: NewResource(resourcetype.RESACCOUNT, accountAddr)}, - - // Operation capabilities - {Cap: caps.Cap(capability.CAPEXECUTE.String()), Rsc: NewResource(resourcetype.RESTRANSACTION, fmt.Sprintf("%s:*", accountAddr))}, - {Cap: caps.Cap(capability.CAPPROPOSE.String()), Rsc: NewResource(resourcetype.RESTRANSACTION, fmt.Sprintf("%s:*", accountAddr))}, - {Cap: caps.Cap(capability.CAPSIGN.String()), Rsc: NewResource(resourcetype.RESTRANSACTION, fmt.Sprintf("%s:*", accountAddr))}, - - // Policy capabilities - {Cap: caps.Cap(capability.CAPSETPOLICY.String()), Rsc: NewResource(resourcetype.RESPOLICY, fmt.Sprintf("%s:*", accountAddr))}, - {Cap: caps.Cap(capability.CAPSETTHRESHOLD.String()), Rsc: NewResource(resourcetype.RESPOLICY, fmt.Sprintf("%s:threshold", accountAddr))}, - } -} - -// NewVaultPolicy creates attenuations for policy management -func NewVaultPolicy( - accountAddr string, - policyType policytype.PolicyType, -) Attenuations { - caps := VaultPermissions.GetCapabilities() - return Attenuations{ - { - Cap: caps.Cap(capability.CAPSETPOLICY.String()), - Rsc: NewResource( - resourcetype.RESPOLICY, - fmt.Sprintf("%s:%s", accountAddr, policyType), - ), - }, - } -} - -// VaultCapabilities defines the capability hierarchy -func VaultCapabilities() []string { - return []string{ - CapOwner.String(), - CapOperator.String(), - CapObserver.String(), - CapAuthenticate.String(), - CapAuthorize.String(), - CapDelegate.String(), - CapInvoke.String(), - CapExecute.String(), - CapRecover.String(), - } -} diff --git a/crypto/ucan/context.go b/crypto/ucan/context.go index e6093fead..3a99b6b7f 100644 --- a/crypto/ucan/context.go +++ b/crypto/ucan/context.go @@ -1,27 +1,66 @@ package ucan import ( - "context" + "fmt" ) -// CtxKey defines a distinct type for context keys used by the access -// package -type CtxKey string - -// TokenCtxKey is the key for adding an access UCAN to a context.Context -const TokenCtxKey CtxKey = "UCAN" - -// CtxWithToken adds a UCAN value to a context -func CtxWithToken(ctx context.Context, t Token) context.Context { - return context.WithValue(ctx, TokenCtxKey, t) +var EmptyAttenuation = Attenuation{ + Cap: Capability(nil), + Rsc: Resource(nil), } -// FromCtx extracts a token from a given context if one is set, returning nil -// otherwise -func FromCtx(ctx context.Context) *Token { - iface := ctx.Value(TokenCtxKey) - if ref, ok := iface.(*Token); ok { - return ref +// Permissions represents the type of attenuation +type Permissions string + +const ( + // AccountPermissions represents the smart account attenuation + AccountPermissions = Permissions("account") + + // ServicePermissions represents the service attenuation + ServicePermissions = Permissions("service") + + // VaultPermissions represents the vault attenuation + VaultPermissions = Permissions("vault") +) + +// Cap returns the capability for the given AttenuationPreset +func (a Permissions) NewCap(c string) Capability { + return a.GetCapabilities().Cap(c) +} + +// NestedCapabilities returns the nested capabilities for the given AttenuationPreset +func (a Permissions) GetCapabilities() NestedCapabilities { + var caps []string + switch a { + case AccountPermissions: + // caps = SmartAccountCapabilities() + case VaultPermissions: + // caps = VaultCapabilities() } - return nil + return NewNestedCapabilities(caps...) +} + +// Equals returns true if the given AttenuationPreset is equal to the receiver +func (a Permissions) Equals(b Permissions) bool { + return a == b +} + +// String returns the string representation of the AttenuationPreset +func (a Permissions) String() string { + return string(a) +} + +// ParseAttenuationData parses raw attenuation data into a structured format +func ParseAttenuationData(data map[string]interface{}) (Permissions, map[string]interface{}, error) { + typeRaw, ok := data["preset"] + if !ok { + return "", nil, fmt.Errorf("missing preset type in attenuation data") + } + + presetType, ok := typeRaw.(string) + if !ok { + return "", nil, fmt.Errorf("invalid preset type format") + } + + return Permissions(presetType), data, nil } diff --git a/crypto/ucan/exports.go b/crypto/ucan/exports.go deleted file mode 100644 index 458552768..000000000 --- a/crypto/ucan/exports.go +++ /dev/null @@ -1,164 +0,0 @@ -package ucan - -import ( - "fmt" - - "github.com/onsonr/sonr/crypto/ucan/attns/capability" - "github.com/onsonr/sonr/crypto/ucan/attns/policytype" - "github.com/onsonr/sonr/crypto/ucan/attns/resourcetype" -) - -var EmptyAttenuation = Attenuation{ - Cap: Capability(nil), - Rsc: Resource(nil), -} - -const ( - // Owner - CapOwner = capability.CAPOWNER - CapOperator = capability.CAPOPERATOR - CapObserver = capability.CAPOBSERVER - - // Auth - CapAuthenticate = capability.CAPAUTHENTICATE - CapAuthorize = capability.CAPAUTHORIZE - CapDelegate = capability.CAPDELEGATE - CapInvoke = capability.CAPINVOKE - CapExecute = capability.CAPEXECUTE - CapPropose = capability.CAPPROPOSE - CapSign = capability.CAPSIGN - CapSetPolicy = capability.CAPSETPOLICY - CapSetThreshold = capability.CAPSETTHRESHOLD - CapRecover = capability.CAPRECOVER - CapSocial = capability.CAPSOCIAL - CapResolver = capability.CAPRESOLVER - CapProducer = capability.CAPPRODUCER - - // Resources - ResAccount = resourcetype.RESACCOUNT - ResTransaction = resourcetype.RESTRANSACTION - ResPolicy = resourcetype.RESPOLICY - ResRecovery = resourcetype.RESRECOVERY - ResVault = resourcetype.RESVAULT - ResIPFS = resourcetype.RESIPFS - ResIPNS = resourcetype.RESIPNS - ResKeyShare = resourcetype.RESKEYSHARE - - // PolicyTypes - PolicyThreshold = policytype.POLICYTHRESHOLD - PolicyTimelock = policytype.POLICYTIMELOCK - PolicyWhitelist = policytype.POLICYWHITELIST - PolicyKeyShare = policytype.POLICYKEYGEN -) - -// NewVaultResource creates a new resource identifier -func NewResource(resType resourcetype.ResourceType, path string) Resource { - return NewStringLengthResource(string(resType), path) -} - -// Permissions represents the type of attenuation -type Permissions string - -const ( - // AccountPermissions represents the smart account attenuation - AccountPermissions = Permissions("account") - - // ServicePermissions represents the service attenuation - ServicePermissions = Permissions("service") - - // VaultPermissions represents the vault attenuation - VaultPermissions = Permissions("vault") -) - -// Cap returns the capability for the given AttenuationPreset -func (a Permissions) NewCap(c capability.Capability) Capability { - return a.GetCapabilities().Cap(c.String()) -} - -// NestedCapabilities returns the nested capabilities for the given AttenuationPreset -func (a Permissions) GetCapabilities() NestedCapabilities { - var caps []string - switch a { - case AccountPermissions: - caps = SmartAccountCapabilities() - case VaultPermissions: - caps = VaultCapabilities() - } - return NewNestedCapabilities(caps...) -} - -// Equals returns true if the given AttenuationPreset is equal to the receiver -func (a Permissions) Equals(b Permissions) bool { - return a == b -} - -// String returns the string representation of the AttenuationPreset -func (a Permissions) String() string { - return string(a) -} - -// GetConstructor returns the AttenuationConstructorFunc for a Permission -func (a Permissions) GetConstructor() AttenuationConstructorFunc { - return NewAttenuationFromPreset(a) -} - -// NewAttenuationFromPreset creates an AttenuationConstructorFunc for the given preset -func NewAttenuationFromPreset(preset Permissions) AttenuationConstructorFunc { - return func(v map[string]interface{}) (Attenuation, error) { - // Extract capability and resource from map - capStr, ok := v["cap"].(string) - if !ok { - return EmptyAttenuation, fmt.Errorf("missing or invalid capability in attenuation data") - } - - resType, ok := v["type"].(string) - if !ok { - return EmptyAttenuation, fmt.Errorf("missing or invalid resource type in attenuation data") - } - - path, ok := v["path"].(string) - if !ok { - path = "/" // Default path if not specified - } - - // Create capability from preset - cap := preset.NewCap(capability.Capability(capStr)) - if cap == nil { - return EmptyAttenuation, fmt.Errorf("invalid capability %s for preset %s", capStr, preset) - } - - // Create resource - resource := NewResource(resourcetype.ResourceType(resType), path) - - return Attenuation{ - Cap: cap, - Rsc: resource, - }, nil - } -} - -// GetPresetConstructor returns the appropriate AttenuationConstructorFunc for a given type -func GetPresetConstructor(attType string) (AttenuationConstructorFunc, error) { - preset := Permissions(attType) - switch preset { - case AccountPermissions, ServicePermissions, VaultPermissions: - return NewAttenuationFromPreset(preset), nil - default: - return nil, fmt.Errorf("unknown attenuation preset: %s", attType) - } -} - -// ParseAttenuationData parses raw attenuation data into a structured format -func ParseAttenuationData(data map[string]interface{}) (Permissions, map[string]interface{}, error) { - typeRaw, ok := data["preset"] - if !ok { - return "", nil, fmt.Errorf("missing preset type in attenuation data") - } - - presetType, ok := typeRaw.(string) - if !ok { - return "", nil, fmt.Errorf("invalid preset type format") - } - - return Permissions(presetType), data, nil -} diff --git a/crypto/ucan/exports_test.go b/crypto/ucan/exports_test.go deleted file mode 100644 index 50e1b31bc..000000000 --- a/crypto/ucan/exports_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package ucan - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestAttenuationPresetConstructor(t *testing.T) { - tests := []struct { - name string - data map[string]interface{} - wantErr bool - }{ - { - name: "valid smart account attenuation", - data: map[string]interface{}{ - "preset": "account", - "cap": string(CapOwner), - "type": string(ResAccount), - "path": "/accounts/123", - }, - wantErr: false, - }, - { - name: "valid vault attenuation", - data: map[string]interface{}{ - "preset": "vault", - "cap": string(CapOperator), - "type": string(ResVault), - "path": "/vaults/456", - }, - wantErr: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - preset, data, err := ParseAttenuationData(tt.data) - if tt.wantErr { - assert.Error(t, err) - return - } - assert.NoError(t, err) - - constructor, err := GetPresetConstructor(preset.String()) - if tt.wantErr { - assert.Error(t, err) - return - } - assert.NoError(t, err) - - attenuation, err := constructor(data) - if tt.wantErr { - assert.Error(t, err) - return - } - assert.NoError(t, err) - assert.NotNil(t, attenuation) - }) - } -} diff --git a/deploy/sink/schema.sql b/deploy/indexer/schema.sql similarity index 100% rename from deploy/sink/schema.sql rename to deploy/indexer/schema.sql diff --git a/.github/aider/guides/cosmos-proto.md b/docs/docs/concepts/tools/cosmos-proto.md similarity index 100% rename from .github/aider/guides/cosmos-proto.md rename to docs/docs/concepts/tools/cosmos-proto.md diff --git a/.github/aider/guides/cosmos-rfc.md b/docs/docs/concepts/tools/cosmos-rfc.md similarity index 100% rename from .github/aider/guides/cosmos-rfc.md rename to docs/docs/concepts/tools/cosmos-rfc.md diff --git a/.github/aider/guides/cosmos-sdk.md b/docs/docs/concepts/tools/cosmos-sdk.md similarity index 100% rename from .github/aider/guides/cosmos-sdk.md rename to docs/docs/concepts/tools/cosmos-sdk.md diff --git a/docs/docs/concepts/ibc-accounts.md b/docs/docs/concepts/tools/ibc-accounts.md similarity index 100% rename from docs/docs/concepts/ibc-accounts.md rename to docs/docs/concepts/tools/ibc-accounts.md diff --git a/docs/docs/concepts/tools/ibc-fee-middleware.md b/docs/docs/concepts/tools/ibc-fee-middleware.md new file mode 100644 index 000000000..10bd6b5db --- /dev/null +++ b/docs/docs/concepts/tools/ibc-fee-middleware.md @@ -0,0 +1,310 @@ +--- +title: Overview +--- + +# Overview + +:::note Synopsis +Learn about what the Fee Middleware module is, and how to build custom modules that utilize the Fee Middleware functionality +::: + +## What is the Fee Middleware module? + +IBC does not depend on relayer operators for transaction verification. However, the relayer infrastructure ensures liveness of the Interchain network — operators listen for packets sent through channels opened between chains, and perform the vital service of ferrying these packets (and proof of the transaction on the sending chain/receipt on the receiving chain) to the clients on each side of the channel. + +Though relaying is permissionless and completely decentralized and accessible, it does come with operational costs. Running full nodes to query transaction proofs and paying for transaction fees associated with IBC packets are two of the primary cost burdens which have driven the overall discussion on **a general, in-protocol incentivization mechanism for relayers**. + +Initially, a [simple proposal](https://github.com/cosmos/ibc/pull/577/files) was created to incentivize relaying on ICS20 token transfers on the destination chain. However, the proposal was specific to ICS20 token transfers and would have to be reimplemented in this format on every other IBC application module. + +After much discussion, the proposal was expanded to a [general incentivisation design](https://github.com/cosmos/ibc/tree/master/spec/app/ics-029-fee-payment) that can be adopted by any ICS application protocol as [middleware](../../01-ibc/04-middleware/02-develop.md). + +## Concepts + +ICS29 fee payments in this middleware design are built on the assumption that sender chains are the source of incentives — the chain on which packets are incentivized is the chain that distributes fees to relayer operators. However, as part of the IBC packet flow, messages have to be submitted on both sender and destination chains. This introduces the requirement of a mapping of relayer operator's addresses on both chains. + +To achieve the stated requirements, the **fee middleware module has two main groups of functionality**: + +- Registering of relayer addresses associated with each party involved in relaying the packet on the source chain. This registration process can be automated on start up of relayer infrastructure and happens only once, not every packet flow. + + This is described in the [Fee distribution section](04-fee-distribution.md). + +- Escrowing fees by any party which will be paid out to each rightful party on completion of the packet lifecycle. + + This is described in the [Fee messages section](03-msgs.md). + +We complete the introduction by giving a list of definitions of relevant terminology. + +`Forward relayer`: The relayer that submits the `MsgRecvPacket` message for a given packet (on the destination chain). + +`Reverse relayer`: The relayer that submits the `MsgAcknowledgement` message for a given packet (on the source chain). + +`Timeout relayer`: The relayer that submits the `MsgTimeout` or `MsgTimeoutOnClose` messages for a given packet (on the source chain). + +`Payee`: The account address on the source chain to be paid on completion of the packet lifecycle. The packet lifecycle on the source chain completes with the receipt of a `MsgTimeout`/`MsgTimeoutOnClose` or a `MsgAcknowledgement`. + +`Counterparty payee`: The account address to be paid on completion of the packet lifecycle on the destination chain. The package lifecycle on the destination chain completes with a successful `MsgRecvPacket`. + +`Refund address`: The address of the account paying for the incentivization of packet relaying. The account is refunded timeout fees upon successful acknowledgement. In the event of a packet timeout, both acknowledgement and receive fees are refunded. + +## Known Limitations + +- At the time of the release of the feature (ibc-go v4) fee payments middleware only supported incentivisation of new channels; however, with the release of channel upgradeability (ibc-go v8.1) it is possible to enable incentivisation of all existing channels. +- Even though unlikely, there exists a DoS attack vector on a fee-enabled channel if 1) there exists a relayer software implementation that is incentivised to timeout packets if the timeout fee is greater than the sum of the fees to receive and acknowledge the packet, and 2) only this type of implementation is used by operators relaying on the channel. In this situation, an attacker could continuously incentivise the relayers to never deliver the packets by incrementing the timeout fee of the packets above the sum of the receive and acknowledge fees. However, this situation is unlikely to occur because 1) another relayer behaving honestly could relay the packets before they timeout, and 2) the attack would be costly because the attacker would need to incentivise the timeout fee of the packets with their own funds. Given the low impact and unlikelihood of the attack we have decided to accept this risk and not implement any mitigation mesaures. + + +## Module Integration + +The Fee Middleware module, as the name suggests, plays the role of an IBC middleware and as such must be configured by chain developers to route and handle IBC messages correctly. +For Cosmos SDK chains this setup is done via the `app/app.go` file, where modules are constructed and configured in order to bootstrap the blockchain application. + +## Example integration of the Fee Middleware module + +```go +// app.go + +// Register the AppModule for the fee middleware module +ModuleBasics = module.NewBasicManager( + ... + ibcfee.AppModuleBasic{}, + ... +) + +... + +// Add module account permissions for the fee middleware module +maccPerms = map[string][]string{ + ... + ibcfeetypes.ModuleName: nil, +} + +... + +// Add fee middleware Keeper +type App struct { + ... + + IBCFeeKeeper ibcfeekeeper.Keeper + + ... +} + +... + +// Create store keys +keys := sdk.NewKVStoreKeys( + ... + ibcfeetypes.StoreKey, + ... +) + +... + +app.IBCFeeKeeper = ibcfeekeeper.NewKeeper( + appCodec, keys[ibcfeetypes.StoreKey], + app.IBCKeeper.ChannelKeeper, // may be replaced with IBC middleware + app.IBCKeeper.ChannelKeeper, + &app.IBCKeeper.PortKeeper, app.AccountKeeper, app.BankKeeper, +) + + +// See the section below for configuring an application stack with the fee middleware module + +... + +// Register fee middleware AppModule +app.moduleManager = module.NewManager( + ... + ibcfee.NewAppModule(app.IBCFeeKeeper), +) + +... + +// Add fee middleware to begin blocker logic +app.moduleManager.SetOrderBeginBlockers( + ... + ibcfeetypes.ModuleName, + ... +) + +// Add fee middleware to end blocker logic +app.moduleManager.SetOrderEndBlockers( + ... + ibcfeetypes.ModuleName, + ... +) + +// Add fee middleware to init genesis logic +app.moduleManager.SetOrderInitGenesis( + ... + ibcfeetypes.ModuleName, + ... +) +``` + +## Configuring an application stack with Fee Middleware + +As mentioned in [IBC middleware development](../../01-ibc/04-middleware/02-develop.md) an application stack may be composed of many or no middlewares that nest a base application. +These layers form the complete set of application logic that enable developers to build composable and flexible IBC application stacks. +For example, an application stack may be just a single base application like `transfer`, however, the same application stack composed with `29-fee` will nest the `transfer` base application +by wrapping it with the Fee Middleware module. + +### Transfer + +See below for an example of how to create an application stack using `transfer` and `29-fee`. +The following `transferStack` is configured in `app/app.go` and added to the IBC `Router`. +The in-line comments describe the execution flow of packets between the application stack and IBC core. + +```go +// Create Transfer Stack +// SendPacket, since it is originating from the application to core IBC: +// transferKeeper.SendPacket -> fee.SendPacket -> channel.SendPacket + +// RecvPacket, message that originates from core IBC and goes down to app, the flow is the other way +// channel.RecvPacket -> fee.OnRecvPacket -> transfer.OnRecvPacket + +// transfer stack contains (from top to bottom): +// - IBC Fee Middleware +// - Transfer + +// create IBC module from bottom to top of stack +var transferStack porttypes.IBCModule +transferStack = transfer.NewIBCModule(app.TransferKeeper) +transferStack = ibcfee.NewIBCMiddleware(transferStack, app.IBCFeeKeeper) + +// Add transfer stack to IBC Router +ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferStack) +``` + +### Interchain Accounts + +See below for an example of how to create an application stack using `27-interchain-accounts` and `29-fee`. +The following `icaControllerStack` and `icaHostStack` are configured in `app/app.go` and added to the IBC `Router` with the associated authentication module. +The in-line comments describe the execution flow of packets between the application stack and IBC core. + +```go +// Create Interchain Accounts Stack +// SendPacket, since it is originating from the application to core IBC: +// icaAuthModuleKeeper.SendTx -> icaController.SendPacket -> fee.SendPacket -> channel.SendPacket + +// initialize ICA module with mock module as the authentication module on the controller side +var icaControllerStack porttypes.IBCModule +icaControllerStack = ibcmock.NewIBCModule(&mockModule, ibcmock.NewMockIBCApp("", scopedICAMockKeeper)) +app.ICAAuthModule = icaControllerStack.(ibcmock.IBCModule) +icaControllerStack = icacontroller.NewIBCMiddleware(icaControllerStack, app.ICAControllerKeeper) +icaControllerStack = ibcfee.NewIBCMiddleware(icaControllerStack, app.IBCFeeKeeper) + +// RecvPacket, message that originates from core IBC and goes down to app, the flow is: +// channel.RecvPacket -> fee.OnRecvPacket -> icaHost.OnRecvPacket + +var icaHostStack porttypes.IBCModule +icaHostStack = icahost.NewIBCModule(app.ICAHostKeeper) +icaHostStack = ibcfee.NewIBCMiddleware(icaHostStack, app.IBCFeeKeeper) + +// Add authentication module, controller and host to IBC router +ibcRouter. + // the ICA Controller middleware needs to be explicitly added to the IBC Router because the + // ICA controller module owns the port capability for ICA. The ICA authentication module + // owns the channel capability. + AddRoute(ibcmock.ModuleName+icacontrollertypes.SubModuleName, icaControllerStack) // ica with mock auth module stack route to ica (top level of middleware stack) + AddRoute(icacontrollertypes.SubModuleName, icaControllerStack). + AddRoute(icahosttypes.SubModuleName, icaHostStack). +``` + +## Fee Distribution + +Packet fees are divided into 3 distinct amounts in order to compensate relayer operators for packet relaying on fee enabled IBC channels. + +- `RecvFee`: The sum of all packet receive fees distributed to a payee for successful execution of `MsgRecvPacket`. +- `AckFee`: The sum of all packet acknowledgement fees distributed to a payee for successful execution of `MsgAcknowledgement`. +- `TimeoutFee`: The sum of all packet timeout fees distributed to a payee for successful execution of `MsgTimeout`. + +## Register a counterparty payee address for forward relaying + +As mentioned in [ICS29 Concepts](01-overview.md#concepts), the forward relayer describes the actor who performs the submission of `MsgRecvPacket` on the destination chain. +Fee distribution for incentivized packet relays takes place on the packet source chain. + +> Relayer operators are expected to register a counterparty payee address, in order to be compensated accordingly with `RecvFee`s upon completion of a packet lifecycle. + +The counterparty payee address registered on the destination chain is encoded into the packet acknowledgement and communicated as such to the source chain for fee distribution. +**If a counterparty payee is not registered for the forward relayer on the destination chain, the escrowed fees will be refunded upon fee distribution.** + +### Relayer operator actions + +A transaction must be submitted **to the destination chain** including a `CounterpartyPayee` address of an account on the source chain. +The transaction must be signed by the `Relayer`. + +Note: If a module account address is used as the `CounterpartyPayee` but the module has been set as a blocked address in the `BankKeeper`, the refunding to the module account will fail. This is because many modules use invariants to compare internal tracking of module account balances against the actual balance of the account stored in the `BankKeeper`. If a token transfer to the module account occurs without going through this module and updating the account balance of the module on the `BankKeeper`, then invariants may break and unknown behaviour could occur depending on the module implementation. Therefore, if it is desirable to use a module account that is currently blocked, the module developers should be consulted to gauge to possibility of removing the module account from the blocked list. + +```go +type MsgRegisterCounterpartyPayee struct { + // unique port identifier + PortId string + // unique channel identifier + ChannelId string + // the relayer address + Relayer string + // the counterparty payee address + CounterpartyPayee string +} +``` + +> This message is expected to fail if: +> +> - `PortId` is invalid (see [24-host naming requirements](https://github.com/cosmos/ibc/blob/master/spec/core/ics-024-host-requirements/README.md#paths-identifiers-separators). +> - `ChannelId` is invalid (see [24-host naming requirements](https://github.com/cosmos/ibc/blob/master/spec/core/ics-024-host-requirements/README.md#paths-identifiers-separators)). +> - `Relayer` is an invalid address (see [Cosmos SDK Addresses](https://github.com/cosmos/cosmos-sdk/blob/main/docs/learn/beginner/03-accounts.md#addresses)). +> - `CounterpartyPayee` is empty or contains more than 2048 bytes. + +See below for an example CLI command: + +```bash +simd tx ibc-fee register-counterparty-payee transfer channel-0 \ + cosmos1rsp837a4kvtgp2m4uqzdge0zzu6efqgucm0qdh \ + osmo1v5y0tz01llxzf4c2afml8s3awue0ymju22wxx2 \ + --from cosmos1rsp837a4kvtgp2m4uqzdge0zzu6efqgucm0qdh +``` + +## Register an alternative payee address for reverse and timeout relaying + +As mentioned in [ICS29 Concepts](01-overview.md#concepts), the reverse relayer describes the actor who performs the submission of `MsgAcknowledgement` on the source chain. +Similarly the timeout relayer describes the actor who performs the submission of `MsgTimeout` (or `MsgTimeoutOnClose`) on the source chain. + +> Relayer operators **may choose** to register an optional payee address, in order to be compensated accordingly with `AckFee`s and `TimeoutFee`s upon completion of a packet life cycle. + +If a payee is not registered for the reverse or timeout relayer on the source chain, then fee distribution assumes the default behaviour, where fees are paid out to the relayer account which delivers `MsgAcknowledgement` or `MsgTimeout`/`MsgTimeoutOnClose`. + +### Relayer operator actions + +A transaction must be submitted **to the source chain** including a `Payee` address of an account on the source chain. +The transaction must be signed by the `Relayer`. + +Note: If a module account address is used as the `Payee` it is recommended to [turn off invariant checks](https://github.com/cosmos/ibc-go/blob/v7.0.0/testing/simapp/app.go#L727) for that module. + +```go +type MsgRegisterPayee struct { + // unique port identifier + PortId string + // unique channel identifier + ChannelId string + // the relayer address + Relayer string + // the payee address + Payee string +} +``` + +> This message is expected to fail if: +> +> - `PortId` is invalid (see [24-host naming requirements](https://github.com/cosmos/ibc/blob/master/spec/core/ics-024-host-requirements/README.md#paths-identifiers-separators). +> - `ChannelId` is invalid (see [24-host naming requirements](https://github.com/cosmos/ibc/blob/master/spec/core/ics-024-host-requirements/README.md#paths-identifiers-separators)). +> - `Relayer` is an invalid address (see [Cosmos SDK Addresses](https://github.com/cosmos/cosmos-sdk/blob/main/docs/learn/beginner/03-accounts.md#addresses)). +> - `Payee` is an invalid address (see [Cosmos SDK Addresses](https://github.com/cosmos/cosmos-sdk/blob/main/docs/learn/beginner/03-accounts.md#addresses)). + +See below for an example CLI command: + +```bash +simd tx ibc-fee register-payee transfer channel-0 \ + cosmos1rsp837a4kvtgp2m4uqzdge0zzu6efqgucm0qdh \ + cosmos153lf4zntqt33a4v0sm5cytrxyqn78q7kz8j8x5 \ + --from cosmos1rsp837a4kvtgp2m4uqzdge0zzu6efqgucm0qdh +``` diff --git a/docs/docs/concepts/tools/ibc-transfer.md b/docs/docs/concepts/tools/ibc-transfer.md new file mode 100644 index 000000000..e64718e84 --- /dev/null +++ b/docs/docs/concepts/tools/ibc-transfer.md @@ -0,0 +1,178 @@ +--- +title: Overview +--- + +# Overview + +:::note Synopsis +Learn about what the token Transfer module is +::: + +## What is the Transfer module? + +Transfer is the Cosmos SDK implementation of the [ICS-20](https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transfer) protocol, which enables cross-chain fungible token transfers. + +## Concepts + +### Acknowledgements + +ICS20 uses the recommended acknowledgement format as specified by [ICS 04](https://github.com/cosmos/ibc/tree/master/spec/core/ics-004-channel-and-packet-semantics#acknowledgement-envelope). + +A successful receive of a transfer packet will result in a Result Acknowledgement being written +with the value `[]byte{byte(1)}` in the `Response` field. + +An unsuccessful receive of a transfer packet will result in an Error Acknowledgement being written +with the error message in the `Response` field. + +### Denomination trace + +The denomination trace corresponds to the information that allows a token to be traced back to its +origin chain. It contains a sequence of port and channel identifiers ordered from the most recent to +the oldest in the timeline of transfers. + +This information is included on the token's base denomination field in the form of a hash to prevent an +unbounded denomination length. For example, the token `transfer/channelToA/uatom` will be displayed +as `ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2`. The human readable denomination +is stored using `x/bank` module's [denom metadata](https://docs.cosmos.network/main/build/modules/bank#denom-metadata) +feature. You may display the human readable denominations by querying balances with the `--resolve-denom` flag, as in: + +```shell +simd query bank balances [address] --resolve-denom +``` + +Each send to any chain other than the one it was previously received from is a movement forwards in +the token's timeline. This causes trace to be added to the token's history and the destination port +and destination channel to be prefixed to the denomination. In these instances the sender chain is +acting as the "source zone". When the token is sent back to the chain it previously received from, the +prefix is removed. This is a backwards movement in the token's timeline and the sender chain is +acting as the "sink zone". + +It is strongly recommended to read the full details of [ADR 001: Coin Source Tracing](/architecture/adr-001-coin-source-tracing) to understand the implications and context of the IBC token representations. + +## UX suggestions for clients + +For clients (wallets, exchanges, applications, block explorers, etc) that want to display the source of the token, it is recommended to use the following alternatives for each of the cases below: + +### Direct connection + +If the denomination trace contains a single identifier prefix pair (as in the example above), then +the easiest way to retrieve the chain and light client identifier is to map the trace information +directly. In summary, this requires querying the channel from the denomination trace identifiers, +and then the counterparty client state using the counterparty port and channel identifiers from the +retrieved channel. + +A general pseudo algorithm would look like the following: + +1. Query the full denomination trace. +2. Query the channel with the `portID/channelID` pair, which corresponds to the first destination of the + token. +3. Query the client state using the identifiers pair. Note that this query will return a `"Not +Found"` response if the current chain is not connected to this channel. +4. Retrieve the client identifier or chain identifier from the client state (eg: on + Tendermint clients) and store it locally. + +Using the gRPC gateway client service the steps above would be, with a given IBC token `ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2` stored on `chainB`: + +1. `GET /ibc/apps/transfer/v1/denom_traces/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2` -> `{"path": "transfer/channelToA", "base_denom": "uatom"}` +2. `GET /ibc/apps/transfer/v1/channels/channelToA/ports/transfer/client_state"` -> `{"client_id": "clientA", "chain-id": "chainA", ...}` +3. `GET /ibc/apps/transfer/v1/channels/channelToA/ports/transfer"` -> `{"channel_id": "channelToA", port_id": "transfer", counterparty: {"channel_id": "channelToB", port_id": "transfer"}, ...}` +4. `GET /ibc/apps/transfer/v1/channels/channelToB/ports/transfer/client_state" -> {"client_id": "clientB", "chain-id": "chainB", ...}` + +Then, the token transfer chain path for the `uatom` denomination would be: `chainA` -> `chainB`. + +### Multiple hops + +The multiple channel hops case applies when the token has passed through multiple chains between the original source and final destination chains. + +The IBC protocol doesn't know the topology of the overall network (i.e connections between chains and identifier names between them). For this reason, in the multiple hops case, a particular chain in the timeline of the individual transfers can't query the chain and client identifiers of the other chains. + +Take for example the following sequence of transfers `A -> B -> C` for an IBC token, with a final prefix path (trace info) of `transfer/channelChainC/transfer/channelChainB`. What the paragraph above means is that even in the case that chain `C` is directly connected to chain `A`, querying the port and channel identifiers that chain `B` uses to connect to chain `A` (eg: `transfer/channelChainA`) can be completely different from the one that chain `C` uses to connect to chain `A` (eg: `transfer/channelToChainA`). + +Thus the proposed solution for clients that the IBC team recommends are the following: + +- **Connect to all chains**: Connecting to all the chains in the timeline would allow clients to + perform the queries outlined in the [direct connection](#direct-connection) section to each + relevant chain. By repeatedly following the port and channel denomination trace transfer timeline, + clients should always be able to find all the relevant identifiers. This comes at the tradeoff + that the client must connect to nodes on each of the chains in order to perform the queries. +- **Relayer as a Service (RaaS)**: A longer term solution is to use/create a relayer service that + could map the denomination trace to the chain path timeline for each token (i.e `origin chain -> +chain #1 -> ... -> chain #(n-1) -> final chain`). These services could provide merkle proofs in + order to allow clients to optionally verify the path timeline correctness for themselves by + running light clients. If the proofs are not verified, they should be considered as trusted third + parties services. Additionally, client would be advised in the future to use RaaS that support the + largest number of connections between chains in the ecosystem. Unfortunately, none of the existing + public relayers (in [Golang](https://github.com/cosmos/relayer) and + [Rust](https://github.com/informalsystems/ibc-rs)), provide this service to clients. + +:::tip +The only viable alternative for clients (at the time of writing) to tokens with multiple connection hops, is to connect to all chains directly and perform relevant queries to each of them in the sequence. +::: + +## Forwarding + +:::info +Token forwarding and unwinding is supported only on ICS20 v2 transfer channels. +::: + +Forwarding allows tokens to be routed to a final destination through multiple (up to 8) intermediary +chains. With forwarding, it's also possible to unwind IBC vouchers to their native chain, and forward +them afterwards to another destination, all with just a single transfer transaction on the sending chain. + +### Forward tokens + +Native tokens or IBC vouchers on any chain can be forwarded through intermediary chains to reach their +final destination. For example, given the topology below, with 3 chains and a transfer channel between +chains A and B and between chains B and C: + +![Light Mode Forwarding](./images/forwarding-3-chains-light.png#gh-light-mode-only)![Dark Mode Forwarding](./images/forwarding-3-chains-dark.png#gh-dark-mode-only) + +Native tokens on chain `A` can be sent to chain `C` through chain `B`. The routing is specified by the +source port ID and channel ID of choice on every intermediary chain. In this example, there is only one +forwarding hop on chain `B` and the port ID, channel ID pair is `transfer`, `channelBToC`. Forwarding of +a multi-denom collections of tokens is also allowed (i.e. forwarding of tokens of different denominations). + +### Unwind tokens + +Taking again as an example the topology from the previous section, we assume that native tokens on chain `A` +have been transferred to chain `C`. The IBC vouchers on chain `C` have the denomination trace +`transfer/channelCtoB/transfer/channelBtoA`, and with forwarding it is possible to submit a transfer message +on chain `C` and automatically unwind the vouchers through chain `B` to chain `A`, so that the tokens recovered +on the origin chain regain their native denomination. In order to execute automatic unwinding, the transfer +module does not require extra user input: the unwind route is encoded in the denomination trace with the +pairs of destination port ID, channel ID that are added on every chain where the tokens are received. + +Please note that unwinding of vouchers is only allowed when vouchers transferred all share the same denomination +trace (signifying coins that all originate from the same source). It is not possible to unwind vouchers of two different +IBC denominations, since they come from different source chains. + +### Unwind tokens and then forward + +Unwinding and forwarding can be used in combination, so that vouchers are first unwound to their origin chain +and then forwarded to a final destination. The same restriction as in the unwinding case applies: only vouchers +of a single IBC denomination can be used. + +## Locked funds + +In some [exceptional cases](/architecture/adr-026-ibc-client-recovery-mechanisms#exceptional-cases), a client state associated with a given channel cannot be updated. This causes that funds from fungible tokens in that channel will be permanently locked and thus can no longer be transferred. + +To mitigate this, a client update governance proposal can be submitted to update the frozen client +with a new valid header. Once the proposal passes the client state will be unfrozen and the funds +from the associated channels will then be unlocked. This mechanism only applies to clients that +allow updates via governance, such as Tendermint clients. + +In addition to this, it's important to mention that a token must be sent back along the exact route +that it took originally in order to return it to its original form on the source chain (eg: the +Cosmos Hub for the `uatom`). Sending a token back to the same chain across a different channel will +**not** move the token back across its timeline. If a channel in the chain history closes before the +token can be sent back across that channel, then the token will not be returnable to its original +form. + +## Security considerations + +For safety, no other module must be capable of minting tokens with the `ibc/` prefix. The IBC +transfer module needs a subset of the denomination space that only it can create tokens in. + +## Channel Closure + +The IBC transfer module does not support channel closure. diff --git a/.github/aider/guides/sonr-did.md b/docs/docs/modules/sonr-did.md similarity index 100% rename from .github/aider/guides/sonr-did.md rename to docs/docs/modules/sonr-did.md diff --git a/.github/aider/guides/sonr-dwn.md b/docs/docs/modules/sonr-dwn.md similarity index 100% rename from .github/aider/guides/sonr-dwn.md rename to docs/docs/modules/sonr-dwn.md diff --git a/.github/aider/guides/sonr-service.md b/docs/docs/modules/sonr-service.md similarity index 100% rename from .github/aider/guides/sonr-service.md rename to docs/docs/modules/sonr-service.md diff --git a/.github/aider/guides/sonr-token.md b/docs/docs/modules/sonr-token.md similarity index 100% rename from .github/aider/guides/sonr-token.md rename to docs/docs/modules/sonr-token.md diff --git a/.github/aider/guides/ucan-spec.md b/docs/docs/modules/ucan-spec.md similarity index 100% rename from .github/aider/guides/ucan-spec.md rename to docs/docs/modules/ucan-spec.md diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index db7936b91..c5310d08a 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -41,14 +41,14 @@ theme: primary: cyan accent: cyan toggle: - icon: material/toggle-switch + icon: material/moon-waning-crescent name: Switch to dark mode - media: "(prefers-color-scheme: dark)" scheme: slate primary: black accent: cyan toggle: - icon: material/toggle-switch-off + icon: material/sun name: Switch to system preference font: text: Geist diff --git a/go.mod b/go.mod index ff8be065d..ef04afc34 100644 --- a/go.mod +++ b/go.mod @@ -83,6 +83,7 @@ require ( github.com/multiformats/go-multicodec v0.9.0 github.com/multiformats/go-multihash v0.2.3 github.com/multiformats/go-varint v0.0.7 + github.com/ncruces/go-sqlite3 v0.21.1 github.com/pkg/errors v0.9.1 github.com/segmentio/ksuid v1.0.4 github.com/spf13/cast v1.6.0 @@ -94,13 +95,9 @@ require ( github.com/strangelove-ventures/tokenfactory v0.50.0 github.com/stretchr/testify v1.10.0 golang.org/x/crypto v0.31.0 - golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 google.golang.org/grpc v1.67.1 google.golang.org/protobuf v1.35.2 - gorm.io/driver/postgres v1.5.11 - gorm.io/driver/sqlite v1.5.6 - gorm.io/gorm v1.25.12 lukechampine.com/blake3 v1.3.0 ) @@ -235,15 +232,9 @@ require ( github.com/ipld/go-codec-dagpb v1.6.0 // indirect github.com/ipld/go-ipld-prime v0.21.0 // indirect github.com/ipshipyard/p2p-forge v0.0.2 // indirect - github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect - github.com/jackc/pgx/v5 v5.7.1 // indirect - github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect github.com/jbenet/goprocess v0.1.4 // indirect - github.com/jinzhu/inflection v1.0.0 // indirect - github.com/jinzhu/now v1.1.5 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/klauspost/compress v1.17.11 // indirect @@ -270,7 +261,6 @@ require ( github.com/manifoldco/promptui v0.9.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/mholt/acmez/v2 v2.0.3 // indirect github.com/miekg/dns v1.1.62 // indirect github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 // indirect @@ -287,6 +277,7 @@ require ( github.com/multiformats/go-multiaddr-dns v0.4.1 // indirect github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect github.com/multiformats/go-multistream v0.6.0 // indirect + github.com/ncruces/julianday v1.0.0 // indirect github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a // indirect github.com/oklog/run v1.1.0 // indirect github.com/onsi/ginkgo/v2 v2.22.0 // indirect @@ -334,6 +325,7 @@ require ( github.com/subosito/gotenv v1.6.0 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tendermint/go-amino v0.16.0 // indirect + github.com/tetratelabs/wazero v1.8.2 // indirect github.com/tidwall/btree v1.7.0 // indirect github.com/ulikunitz/xz v0.5.11 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect @@ -362,6 +354,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect go4.org v0.0.0-20230225012048-214862532bf5 // indirect + golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d // indirect golang.org/x/mod v0.22.0 // indirect golang.org/x/net v0.32.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect diff --git a/go.sum b/go.sum index a45f36d32..b91f5519e 100644 --- a/go.sum +++ b/go.sum @@ -1766,14 +1766,6 @@ github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbk github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI= github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= -github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= -github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.7.1 h1:x7SYsPBYDkHDksogeSmZZ5xzThcTgRz++I5E+ePFUcs= -github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA= -github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= -github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= @@ -1786,10 +1778,6 @@ github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267/go.mod h1:h1n github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jhump/protoreflect v1.15.3 h1:6SFRuqU45u9hIZPJAoZ8c28T3nK64BNdp9w6jFonzls= github.com/jhump/protoreflect v1.15.3/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4Elxc8qKP+p1k= -github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= -github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= -github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= @@ -1971,8 +1959,6 @@ github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsO github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= -github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -2073,6 +2059,10 @@ github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7 github.com/nats-io/nkeys v0.4.4/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5sNvt64= github.com/nats-io/nkeys v0.4.5/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5sNvt64= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/ncruces/go-sqlite3 v0.21.1 h1:cbzIOY3jQrXZWVsBfH9TCFj/iqqMIcJ7PLye4AAEwoQ= +github.com/ncruces/go-sqlite3 v0.21.1/go.mod h1:zxMOaSG5kFYVFK4xQa0pdwIszqxqJ0W0BxBgwdrNjuA= +github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M= +github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= @@ -2390,6 +2380,8 @@ github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45 github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= +github.com/tetratelabs/wazero v1.8.2 h1:yIgLR/b2bN31bjxwXHD8a3d+BogigR952csSDdLYEv4= +github.com/tetratelabs/wazero v1.8.2/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs= github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= @@ -3485,12 +3477,6 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314= -gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= -gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE= -gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4= -gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= -gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= diff --git a/pkg/config/env.go b/internal/config/env.go similarity index 95% rename from pkg/config/env.go rename to internal/config/env.go index 9e6dd9d34..7f45e1360 100644 --- a/pkg/config/env.go +++ b/internal/config/env.go @@ -4,7 +4,7 @@ import ( "context" "github.com/apple/pkl-go/pkl" - hwayconfig "github.com/onsonr/sonr/pkg/config/hway" + hwayconfig "github.com/onsonr/sonr/internal/config/hway" ) // LoadFromBytes loads the environment from the given bytes diff --git a/pkg/config/hway/Hway.pkl.go b/internal/config/hway/Hway.pkl.go similarity index 100% rename from pkg/config/hway/Hway.pkl.go rename to internal/config/hway/Hway.pkl.go diff --git a/pkg/config/hway/init.pkl.go b/internal/config/hway/init.pkl.go similarity index 100% rename from pkg/config/hway/init.pkl.go rename to internal/config/hway/init.pkl.go diff --git a/pkg/config/motr/Config.pkl.go b/internal/config/motr/Config.pkl.go similarity index 100% rename from pkg/config/motr/Config.pkl.go rename to internal/config/motr/Config.pkl.go diff --git a/pkg/config/motr/Environment.pkl.go b/internal/config/motr/Environment.pkl.go similarity index 100% rename from pkg/config/motr/Environment.pkl.go rename to internal/config/motr/Environment.pkl.go diff --git a/pkg/config/motr/Motr.pkl.go b/internal/config/motr/Motr.pkl.go similarity index 100% rename from pkg/config/motr/Motr.pkl.go rename to internal/config/motr/Motr.pkl.go diff --git a/pkg/config/motr/Schema.pkl.go b/internal/config/motr/Schema.pkl.go similarity index 100% rename from pkg/config/motr/Schema.pkl.go rename to internal/config/motr/Schema.pkl.go diff --git a/pkg/config/motr/init.pkl.go b/internal/config/motr/init.pkl.go similarity index 100% rename from pkg/config/motr/init.pkl.go rename to internal/config/motr/init.pkl.go diff --git a/pkg/common/cookies.go b/internal/context/cookies.go similarity index 99% rename from pkg/common/cookies.go rename to internal/context/cookies.go index ad7e419eb..5178e4fd7 100644 --- a/pkg/common/cookies.go +++ b/internal/context/cookies.go @@ -1,4 +1,4 @@ -package common +package context import ( "encoding/base64" diff --git a/pkg/common/headers.go b/internal/context/headers.go similarity index 99% rename from pkg/common/headers.go rename to internal/context/headers.go index 68de391be..692cd0e0d 100644 --- a/pkg/common/headers.go +++ b/internal/context/headers.go @@ -1,4 +1,4 @@ -package common +package context import "github.com/labstack/echo/v4" diff --git a/internal/database/conn.go b/internal/database/conn.go new file mode 100644 index 000000000..678299973 --- /dev/null +++ b/internal/database/conn.go @@ -0,0 +1,25 @@ +package database + +import ( + "context" + "database/sql" + + _ "github.com/ncruces/go-sqlite3/driver" + _ "github.com/ncruces/go-sqlite3/embed" + config "github.com/onsonr/sonr/internal/config/hway" + "github.com/onsonr/sonr/internal/database/sink" +) + +// NewDB initializes and returns a configured database connection +func NewDB(env config.Hway) (*sql.DB, error) { + db, err := sql.Open("sqlite3", ":memory:") + if err != nil { + return nil, err + } + + // create tables + if _, err := db.ExecContext(context.Background(), sink.SchemaSQL); err != nil { + return nil, err + } + return db, nil +} diff --git a/internal/database/repository/db.go b/internal/database/repository/db.go new file mode 100644 index 000000000..555d019d5 --- /dev/null +++ b/internal/database/repository/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 + +package repository + +import ( + "context" + "database/sql" +) + +type DBTX interface { + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + PrepareContext(context.Context, string) (*sql.Stmt, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...interface{}) *sql.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/database/repository/models.go b/internal/database/repository/models.go new file mode 100644 index 000000000..d7d0b5da4 --- /dev/null +++ b/internal/database/repository/models.go @@ -0,0 +1,99 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 + +package repository + +import ( + "database/sql" + "time" +) + +type Account struct { + ID string + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt sql.NullTime + Number int64 + Sequence int64 + Address string + PublicKey string + ChainID string + Controller string + IsSubsidiary bool + IsValidator bool + IsDelegator bool + IsAccountable bool +} + +type Asset struct { + ID string + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt sql.NullTime + Name string + Symbol string + Decimals int64 + ChainID string + Channel string + AssetType string + CoingeckoID sql.NullString +} + +type Credential struct { + ID int64 + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt sql.NullTime + Handle string + CredentialID string + AuthenticatorAttachment string + Origin string + Type string + Transports string +} + +type Profile struct { + ID int64 + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt sql.NullTime + Address string + Handle string + Origin string + Name string +} + +type Session struct { + ID string + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt sql.NullTime + BrowserName string + BrowserVersion string + ClientIpaddr string + Platform string + IsDesktop bool + IsMobile bool + IsTablet bool + IsTv bool + IsBot bool + Challenge string + IsHumanFirst bool + IsHumanLast bool + ProfileID int64 +} + +type Vault struct { + ID int64 + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt sql.NullTime + Handle string + Origin string + Address string + Cid string + Config string + SessionID string + RedirectUri string +} diff --git a/internal/database/repository/query.sql.go b/internal/database/repository/query.sql.go new file mode 100644 index 000000000..f1401a4de --- /dev/null +++ b/internal/database/repository/query.sql.go @@ -0,0 +1,581 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 +// source: query.sql + +package repository + +import ( + "context" +) + +const checkHandleExists = `-- name: CheckHandleExists :one +SELECT COUNT(*) > 0 as handle_exists FROM profiles +WHERE handle = ? +AND deleted_at IS NULL +` + +func (q *Queries) CheckHandleExists(ctx context.Context, handle string) (bool, error) { + row := q.db.QueryRowContext(ctx, checkHandleExists, handle) + var handle_exists bool + err := row.Scan(&handle_exists) + return handle_exists, err +} + +const createSession = `-- name: CreateSession :one +INSERT INTO sessions ( + id, + browser_name, + browser_version, + client_ipaddr, + platform, + is_desktop, + is_mobile, + is_tablet, + is_tv, + is_bot, + challenge, + is_human_first, + is_human_last, + profile_id +) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) +RETURNING id, created_at, updated_at, deleted_at, browser_name, browser_version, client_ipaddr, platform, is_desktop, is_mobile, is_tablet, is_tv, is_bot, challenge, is_human_first, is_human_last, profile_id +` + +type CreateSessionParams struct { + ID string + BrowserName string + BrowserVersion string + ClientIpaddr string + Platform string + IsDesktop bool + IsMobile bool + IsTablet bool + IsTv bool + IsBot bool + Challenge string + IsHumanFirst bool + IsHumanLast bool + ProfileID int64 +} + +func (q *Queries) CreateSession(ctx context.Context, arg CreateSessionParams) (Session, error) { + row := q.db.QueryRowContext(ctx, createSession, + arg.ID, + arg.BrowserName, + arg.BrowserVersion, + arg.ClientIpaddr, + arg.Platform, + arg.IsDesktop, + arg.IsMobile, + arg.IsTablet, + arg.IsTv, + arg.IsBot, + arg.Challenge, + arg.IsHumanFirst, + arg.IsHumanLast, + arg.ProfileID, + ) + var i Session + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.BrowserName, + &i.BrowserVersion, + &i.ClientIpaddr, + &i.Platform, + &i.IsDesktop, + &i.IsMobile, + &i.IsTablet, + &i.IsTv, + &i.IsBot, + &i.Challenge, + &i.IsHumanFirst, + &i.IsHumanLast, + &i.ProfileID, + ) + return i, err +} + +const getChallengeBySessionID = `-- name: GetChallengeBySessionID :one +SELECT challenge FROM sessions +WHERE id = ? AND deleted_at IS NULL +LIMIT 1 +` + +func (q *Queries) GetChallengeBySessionID(ctx context.Context, id string) (string, error) { + row := q.db.QueryRowContext(ctx, getChallengeBySessionID, id) + var challenge string + err := row.Scan(&challenge) + return challenge, err +} + +const getCredentialByID = `-- name: GetCredentialByID :one +SELECT id, created_at, updated_at, deleted_at, handle, credential_id, authenticator_attachment, origin, type, transports FROM credentials +WHERE credential_id = ? +AND deleted_at IS NULL +LIMIT 1 +` + +func (q *Queries) GetCredentialByID(ctx context.Context, credentialID string) (Credential, error) { + row := q.db.QueryRowContext(ctx, getCredentialByID, credentialID) + var i Credential + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.Handle, + &i.CredentialID, + &i.AuthenticatorAttachment, + &i.Origin, + &i.Type, + &i.Transports, + ) + return i, err +} + +const getCredentialsByHandle = `-- name: GetCredentialsByHandle :many +SELECT id, created_at, updated_at, deleted_at, handle, credential_id, authenticator_attachment, origin, type, transports FROM credentials +WHERE handle = ? +AND deleted_at IS NULL +` + +func (q *Queries) GetCredentialsByHandle(ctx context.Context, handle string) ([]Credential, error) { + rows, err := q.db.QueryContext(ctx, getCredentialsByHandle, handle) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Credential + for rows.Next() { + var i Credential + if err := rows.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.Handle, + &i.CredentialID, + &i.AuthenticatorAttachment, + &i.Origin, + &i.Type, + &i.Transports, + ); 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 getHumanVerificationNumbers = `-- name: GetHumanVerificationNumbers :one +SELECT is_human_first, is_human_last FROM sessions +WHERE id = ? AND deleted_at IS NULL +LIMIT 1 +` + +type GetHumanVerificationNumbersRow struct { + IsHumanFirst bool + IsHumanLast bool +} + +func (q *Queries) GetHumanVerificationNumbers(ctx context.Context, id string) (GetHumanVerificationNumbersRow, error) { + row := q.db.QueryRowContext(ctx, getHumanVerificationNumbers, id) + var i GetHumanVerificationNumbersRow + err := row.Scan(&i.IsHumanFirst, &i.IsHumanLast) + return i, err +} + +const getProfileByAddress = `-- name: GetProfileByAddress :one +SELECT id, created_at, updated_at, deleted_at, address, handle, origin, name FROM profiles +WHERE address = ? AND deleted_at IS NULL +LIMIT 1 +` + +func (q *Queries) GetProfileByAddress(ctx context.Context, address string) (Profile, error) { + row := q.db.QueryRowContext(ctx, getProfileByAddress, address) + var i Profile + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.Address, + &i.Handle, + &i.Origin, + &i.Name, + ) + return i, err +} + +const getProfileByHandle = `-- name: GetProfileByHandle :one +SELECT id, created_at, updated_at, deleted_at, address, handle, origin, name FROM profiles +WHERE handle = ? +AND deleted_at IS NULL +LIMIT 1 +` + +func (q *Queries) GetProfileByHandle(ctx context.Context, handle string) (Profile, error) { + row := q.db.QueryRowContext(ctx, getProfileByHandle, handle) + var i Profile + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.Address, + &i.Handle, + &i.Origin, + &i.Name, + ) + return i, err +} + +const getProfileByID = `-- name: GetProfileByID :one +SELECT id, created_at, updated_at, deleted_at, address, handle, origin, name FROM profiles +WHERE id = ? AND deleted_at IS NULL +LIMIT 1 +` + +func (q *Queries) GetProfileByID(ctx context.Context, id int64) (Profile, error) { + row := q.db.QueryRowContext(ctx, getProfileByID, id) + var i Profile + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.Address, + &i.Handle, + &i.Origin, + &i.Name, + ) + return i, err +} + +const getSessionByClientIP = `-- name: GetSessionByClientIP :one +SELECT id, created_at, updated_at, deleted_at, browser_name, browser_version, client_ipaddr, platform, is_desktop, is_mobile, is_tablet, is_tv, is_bot, challenge, is_human_first, is_human_last, profile_id FROM sessions +WHERE client_ipaddr = ? AND deleted_at IS NULL +LIMIT 1 +` + +func (q *Queries) GetSessionByClientIP(ctx context.Context, clientIpaddr string) (Session, error) { + row := q.db.QueryRowContext(ctx, getSessionByClientIP, clientIpaddr) + var i Session + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.BrowserName, + &i.BrowserVersion, + &i.ClientIpaddr, + &i.Platform, + &i.IsDesktop, + &i.IsMobile, + &i.IsTablet, + &i.IsTv, + &i.IsBot, + &i.Challenge, + &i.IsHumanFirst, + &i.IsHumanLast, + &i.ProfileID, + ) + return i, err +} + +const getSessionByID = `-- name: GetSessionByID :one +SELECT id, created_at, updated_at, deleted_at, browser_name, browser_version, client_ipaddr, platform, is_desktop, is_mobile, is_tablet, is_tv, is_bot, challenge, is_human_first, is_human_last, profile_id FROM sessions +WHERE id = ? AND deleted_at IS NULL +LIMIT 1 +` + +func (q *Queries) GetSessionByID(ctx context.Context, id string) (Session, error) { + row := q.db.QueryRowContext(ctx, getSessionByID, id) + var i Session + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.BrowserName, + &i.BrowserVersion, + &i.ClientIpaddr, + &i.Platform, + &i.IsDesktop, + &i.IsMobile, + &i.IsTablet, + &i.IsTv, + &i.IsBot, + &i.Challenge, + &i.IsHumanFirst, + &i.IsHumanLast, + &i.ProfileID, + ) + return i, err +} + +const getVaultConfigByCID = `-- name: GetVaultConfigByCID :one +SELECT id, created_at, updated_at, deleted_at, handle, origin, address, cid, config, session_id, redirect_uri FROM vaults +WHERE cid = ? +AND deleted_at IS NULL +LIMIT 1 +` + +func (q *Queries) GetVaultConfigByCID(ctx context.Context, cid string) (Vault, error) { + row := q.db.QueryRowContext(ctx, getVaultConfigByCID, cid) + var i Vault + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.Handle, + &i.Origin, + &i.Address, + &i.Cid, + &i.Config, + &i.SessionID, + &i.RedirectUri, + ) + return i, err +} + +const getVaultRedirectURIBySessionID = `-- name: GetVaultRedirectURIBySessionID :one +SELECT redirect_uri FROM vaults +WHERE session_id = ? +AND deleted_at IS NULL +LIMIT 1 +` + +func (q *Queries) GetVaultRedirectURIBySessionID(ctx context.Context, sessionID string) (string, error) { + row := q.db.QueryRowContext(ctx, getVaultRedirectURIBySessionID, sessionID) + var redirect_uri string + err := row.Scan(&redirect_uri) + return redirect_uri, err +} + +const insertCredential = `-- name: InsertCredential :one +INSERT INTO credentials ( + handle, + credential_id, + origin, + type, + transports +) VALUES (?, ?, ?, ?, ?) +RETURNING id, created_at, updated_at, deleted_at, handle, credential_id, authenticator_attachment, origin, type, transports +` + +type InsertCredentialParams struct { + Handle string + CredentialID string + Origin string + Type string + Transports string +} + +func (q *Queries) InsertCredential(ctx context.Context, arg InsertCredentialParams) (Credential, error) { + row := q.db.QueryRowContext(ctx, insertCredential, + arg.Handle, + arg.CredentialID, + arg.Origin, + arg.Type, + arg.Transports, + ) + var i Credential + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.Handle, + &i.CredentialID, + &i.AuthenticatorAttachment, + &i.Origin, + &i.Type, + &i.Transports, + ) + return i, err +} + +const insertProfile = `-- name: InsertProfile :one +INSERT INTO profiles ( + address, + handle, + origin, + name +) VALUES (?, ?, ?, ?) +RETURNING id, created_at, updated_at, deleted_at, address, handle, origin, name +` + +type InsertProfileParams struct { + Address string + Handle string + Origin string + Name string +} + +func (q *Queries) InsertProfile(ctx context.Context, arg InsertProfileParams) (Profile, error) { + row := q.db.QueryRowContext(ctx, insertProfile, + arg.Address, + arg.Handle, + arg.Origin, + arg.Name, + ) + var i Profile + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.Address, + &i.Handle, + &i.Origin, + &i.Name, + ) + return i, err +} + +const softDeleteCredential = `-- name: SoftDeleteCredential :exec +UPDATE credentials +SET deleted_at = CURRENT_TIMESTAMP +WHERE credential_id = ? +` + +func (q *Queries) SoftDeleteCredential(ctx context.Context, credentialID string) error { + _, err := q.db.ExecContext(ctx, softDeleteCredential, credentialID) + return err +} + +const softDeleteProfile = `-- name: SoftDeleteProfile :exec +UPDATE profiles +SET deleted_at = CURRENT_TIMESTAMP +WHERE address = ? +` + +func (q *Queries) SoftDeleteProfile(ctx context.Context, address string) error { + _, err := q.db.ExecContext(ctx, softDeleteProfile, address) + return err +} + +const updateProfile = `-- name: UpdateProfile :one +UPDATE profiles +SET + name = ?, + handle = ?, + updated_at = CURRENT_TIMESTAMP +WHERE address = ? +AND deleted_at IS NULL +RETURNING id, created_at, updated_at, deleted_at, address, handle, origin, name +` + +type UpdateProfileParams struct { + Name string + Handle string + Address string +} + +func (q *Queries) UpdateProfile(ctx context.Context, arg UpdateProfileParams) (Profile, error) { + row := q.db.QueryRowContext(ctx, updateProfile, arg.Name, arg.Handle, arg.Address) + var i Profile + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.Address, + &i.Handle, + &i.Origin, + &i.Name, + ) + return i, err +} + +const updateSessionHumanVerification = `-- name: UpdateSessionHumanVerification :one +UPDATE sessions +SET + is_human_first = ?, + is_human_last = ?, + updated_at = CURRENT_TIMESTAMP +WHERE id = ? +RETURNING id, created_at, updated_at, deleted_at, browser_name, browser_version, client_ipaddr, platform, is_desktop, is_mobile, is_tablet, is_tv, is_bot, challenge, is_human_first, is_human_last, profile_id +` + +type UpdateSessionHumanVerificationParams struct { + IsHumanFirst bool + IsHumanLast bool + ID string +} + +func (q *Queries) UpdateSessionHumanVerification(ctx context.Context, arg UpdateSessionHumanVerificationParams) (Session, error) { + row := q.db.QueryRowContext(ctx, updateSessionHumanVerification, arg.IsHumanFirst, arg.IsHumanLast, arg.ID) + var i Session + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.BrowserName, + &i.BrowserVersion, + &i.ClientIpaddr, + &i.Platform, + &i.IsDesktop, + &i.IsMobile, + &i.IsTablet, + &i.IsTv, + &i.IsBot, + &i.Challenge, + &i.IsHumanFirst, + &i.IsHumanLast, + &i.ProfileID, + ) + return i, err +} + +const updateSessionWithProfileID = `-- name: UpdateSessionWithProfileID :one +UPDATE sessions +SET + profile_id = ?, + updated_at = CURRENT_TIMESTAMP +WHERE id = ? +RETURNING id, created_at, updated_at, deleted_at, browser_name, browser_version, client_ipaddr, platform, is_desktop, is_mobile, is_tablet, is_tv, is_bot, challenge, is_human_first, is_human_last, profile_id +` + +type UpdateSessionWithProfileIDParams struct { + ProfileID int64 + ID string +} + +func (q *Queries) UpdateSessionWithProfileID(ctx context.Context, arg UpdateSessionWithProfileIDParams) (Session, error) { + row := q.db.QueryRowContext(ctx, updateSessionWithProfileID, arg.ProfileID, arg.ID) + var i Session + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.DeletedAt, + &i.BrowserName, + &i.BrowserVersion, + &i.ClientIpaddr, + &i.Platform, + &i.IsDesktop, + &i.IsMobile, + &i.IsTablet, + &i.IsTv, + &i.IsBot, + &i.Challenge, + &i.IsHumanFirst, + &i.IsHumanLast, + &i.ProfileID, + ) + return i, err +} diff --git a/internal/database/session.go b/internal/database/session.go new file mode 100644 index 000000000..9e40a8800 --- /dev/null +++ b/internal/database/session.go @@ -0,0 +1,58 @@ +package database + +import ( + ctx "github.com/onsonr/sonr/internal/context" + + "github.com/go-webauthn/webauthn/protocol" + "github.com/labstack/echo/v4" + "github.com/medama-io/go-useragent" + "github.com/onsonr/sonr/internal/database/repository" + "github.com/segmentio/ksuid" +) + +func BaseSessionCreateParams(e echo.Context) repository.CreateSessionParams { + // f := rand.Intn(5) + 1 + // l := rand.Intn(4) + 1 + challenge, _ := protocol.CreateChallenge() + id := getOrCreateSessionID(e) + ua := useragent.NewParser() + s := ua.Parse(e.Request().UserAgent()) + + return repository.CreateSessionParams{ + ID: id, + BrowserName: s.GetBrowser(), + BrowserVersion: s.GetMajorVersion(), + ClientIpaddr: e.RealIP(), + Platform: s.GetOS(), + IsMobile: s.IsMobile(), + IsTablet: s.IsTablet(), + IsDesktop: s.IsDesktop(), + IsBot: s.IsBot(), + IsTv: s.IsTV(), + // IsHumanFirst: int64(f), + // IsHumanLast: int64(l), + Challenge: challenge.String(), + } +} + +func getOrCreateSessionID(c echo.Context) string { + if ok := ctx.CookieExists(c, ctx.SessionID); !ok { + sessionID := ksuid.New().String() + ctx.WriteCookie(c, ctx.SessionID, sessionID) + return sessionID + } + + sessionID, err := ctx.ReadCookie(c, ctx.SessionID) + if err != nil { + sessionID = ksuid.New().String() + ctx.WriteCookie(c, ctx.SessionID, sessionID) + } + return sessionID +} + +func boolToInt64(b bool) int64 { + if b { + return 1 + } + return 0 +} diff --git a/internal/database/sink/embed.go b/internal/database/sink/embed.go new file mode 100644 index 000000000..2e939f2a2 --- /dev/null +++ b/internal/database/sink/embed.go @@ -0,0 +1,8 @@ +package sink + +import ( + _ "embed" +) + +//go:embed schema.sql +var SchemaSQL string diff --git a/internal/database/sink/query.sql b/internal/database/sink/query.sql new file mode 100644 index 000000000..f4692ac71 --- /dev/null +++ b/internal/database/sink/query.sql @@ -0,0 +1,138 @@ +-- name: InsertCredential :one +INSERT INTO credentials ( + handle, + credential_id, + origin, + type, + transports +) VALUES (?, ?, ?, ?, ?) +RETURNING *; + +-- name: InsertProfile :one +INSERT INTO profiles ( + address, + handle, + origin, + name +) VALUES (?, ?, ?, ?) +RETURNING *; + +-- name: GetProfileByID :one +SELECT * FROM profiles +WHERE id = ? AND deleted_at IS NULL +LIMIT 1; + +-- name: GetProfileByAddress :one +SELECT * FROM profiles +WHERE address = ? AND deleted_at IS NULL +LIMIT 1; + +-- name: GetChallengeBySessionID :one +SELECT challenge FROM sessions +WHERE id = ? AND deleted_at IS NULL +LIMIT 1; + +-- name: GetHumanVerificationNumbers :one +SELECT is_human_first, is_human_last FROM sessions +WHERE id = ? AND deleted_at IS NULL +LIMIT 1; + +-- name: GetSessionByID :one +SELECT * FROM sessions +WHERE id = ? AND deleted_at IS NULL +LIMIT 1; + +-- name: GetSessionByClientIP :one +SELECT * FROM sessions +WHERE client_ipaddr = ? AND deleted_at IS NULL +LIMIT 1; + +-- name: UpdateSessionHumanVerification :one +UPDATE sessions +SET + is_human_first = ?, + is_human_last = ?, + updated_at = CURRENT_TIMESTAMP +WHERE id = ? +RETURNING *; + +-- name: UpdateSessionWithProfileID :one +UPDATE sessions +SET + profile_id = ?, + updated_at = CURRENT_TIMESTAMP +WHERE id = ? +RETURNING *; + +-- name: CheckHandleExists :one +SELECT COUNT(*) > 0 as handle_exists FROM profiles +WHERE handle = ? +AND deleted_at IS NULL; + +-- name: GetCredentialsByHandle :many +SELECT * FROM credentials +WHERE handle = ? +AND deleted_at IS NULL; + +-- name: GetCredentialByID :one +SELECT * FROM credentials +WHERE credential_id = ? +AND deleted_at IS NULL +LIMIT 1; + +-- name: SoftDeleteCredential :exec +UPDATE credentials +SET deleted_at = CURRENT_TIMESTAMP +WHERE credential_id = ?; + +-- name: SoftDeleteProfile :exec +UPDATE profiles +SET deleted_at = CURRENT_TIMESTAMP +WHERE address = ?; + +-- name: UpdateProfile :one +UPDATE profiles +SET + name = ?, + handle = ?, + updated_at = CURRENT_TIMESTAMP +WHERE address = ? +AND deleted_at IS NULL +RETURNING *; + +-- name: GetProfileByHandle :one +SELECT * FROM profiles +WHERE handle = ? +AND deleted_at IS NULL +LIMIT 1; + +-- name: GetVaultConfigByCID :one +SELECT * FROM vaults +WHERE cid = ? +AND deleted_at IS NULL +LIMIT 1; + +-- name: GetVaultRedirectURIBySessionID :one +SELECT redirect_uri FROM vaults +WHERE session_id = ? +AND deleted_at IS NULL +LIMIT 1; + +-- name: CreateSession :one +INSERT INTO sessions ( + id, + browser_name, + browser_version, + client_ipaddr, + platform, + is_desktop, + is_mobile, + is_tablet, + is_tv, + is_bot, + challenge, + is_human_first, + is_human_last, + profile_id +) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) +RETURNING *; diff --git a/internal/database/sink/schema.sql b/internal/database/sink/schema.sql new file mode 100644 index 000000000..912d465c3 --- /dev/null +++ b/internal/database/sink/schema.sql @@ -0,0 +1,121 @@ +-- Profiles represent user identities +CREATE TABLE profiles ( + id TEXT PRIMARY KEY, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP, + address TEXT NOT NULL, + handle TEXT NOT NULL UNIQUE, + origin TEXT NOT NULL, + name TEXT NOT NULL, + UNIQUE(address, origin) +); + +-- Accounts represent blockchain accounts +CREATE TABLE accounts ( + id TEXT PRIMARY KEY, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP, + number INTEGER NOT NULL, + sequence INTEGER NOT NULL DEFAULT 0, + address TEXT NOT NULL UNIQUE, + public_key TEXT NOT NULL CHECK(json_valid(public_key)), + chain_id TEXT NOT NULL, + controller TEXT NOT NULL, + is_subsidiary BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_subsidiary IN (0,1)), + is_validator BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_validator IN (0,1)), + is_delegator BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_delegator IN (0,1)), + is_accountable BOOLEAN NOT NULL DEFAULT TRUE CHECK(is_accountable IN (0,1)) +); + +-- Assets represent tokens and coins +CREATE TABLE assets ( + id TEXT PRIMARY KEY, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP, + name TEXT NOT NULL, + symbol TEXT NOT NULL, + decimals INTEGER NOT NULL CHECK(decimals >= 0), + chain_id TEXT NOT NULL, + channel TEXT NOT NULL, + asset_type TEXT NOT NULL, + coingecko_id TEXT, + UNIQUE(chain_id, symbol) +); + +-- Credentials store WebAuthn credentials +CREATE TABLE credentials ( + id TEXT PRIMARY KEY, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP, + handle TEXT NOT NULL, + credential_id TEXT NOT NULL UNIQUE, + authenticator_attachment TEXT NOT NULL, + origin TEXT NOT NULL, + type TEXT NOT NULL, + transports TEXT NOT NULL +); + +-- Sessions track user authentication state +CREATE TABLE sessions ( + id TEXT PRIMARY KEY, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP, + browser_name TEXT NOT NULL, + browser_version TEXT NOT NULL, + client_ipaddr TEXT NOT NULL, + platform TEXT NOT NULL, + is_desktop BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_desktop IN (0,1)), + is_mobile BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_mobile IN (0,1)), + is_tablet BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_tablet IN (0,1)), + is_tv BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_tv IN (0,1)), + is_bot BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_bot IN (0,1)), + challenge TEXT NOT NULL, + is_human_first BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_human_first IN (0,1)), + is_human_last BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_human_last IN (0,1)), + profile_id INTEGER NOT NULL +); + +-- Vaults store encrypted data +CREATE TABLE vaults ( + id TEXT PRIMARY KEY, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at TIMESTAMP, + handle TEXT NOT NULL, + origin TEXT NOT NULL, + address TEXT NOT NULL, + cid TEXT NOT NULL UNIQUE, + config TEXT NOT NULL CHECK(json_valid(config)), + session_id TEXT NOT NULL, + redirect_uri TEXT NOT NULL +); + +-- Indexes for common queries +CREATE INDEX idx_profiles_handle ON profiles(handle); +CREATE INDEX idx_profiles_address ON profiles(address); +CREATE INDEX idx_profiles_deleted_at ON profiles(deleted_at); + +CREATE INDEX idx_accounts_address ON accounts(address); +CREATE INDEX idx_accounts_chain_id ON accounts(chain_id); +CREATE INDEX idx_accounts_deleted_at ON accounts(deleted_at); + +CREATE INDEX idx_assets_symbol ON assets(symbol); +CREATE INDEX idx_assets_chain_id ON assets(chain_id); +CREATE INDEX idx_assets_deleted_at ON assets(deleted_at); + +CREATE INDEX idx_credentials_handle ON credentials(handle); +CREATE INDEX idx_credentials_origin ON credentials(origin); +CREATE INDEX idx_credentials_deleted_at ON credentials(deleted_at); + +CREATE INDEX idx_sessions_profile_id ON sessions(profile_id); +CREATE INDEX idx_sessions_client_ipaddr ON sessions(client_ipaddr); +CREATE INDEX idx_sessions_deleted_at ON sessions(deleted_at); + +CREATE INDEX idx_vaults_handle ON vaults(handle); +CREATE INDEX idx_vaults_session_id ON vaults(session_id); +CREATE INDEX idx_vaults_deleted_at ON vaults(deleted_at); diff --git a/internal/database/sqlc.yaml b/internal/database/sqlc.yaml new file mode 100644 index 000000000..4400f27b1 --- /dev/null +++ b/internal/database/sqlc.yaml @@ -0,0 +1,9 @@ +version: "2" +sql: + - engine: "sqlite" + queries: "./sink/query.sql" + schema: "./sink/schema.sql" + gen: + go: + package: "repository" + out: "repository" diff --git a/internal/gateway/context/context.go b/internal/gateway/context/context.go deleted file mode 100644 index 199aa5029..000000000 --- a/internal/gateway/context/context.go +++ /dev/null @@ -1,50 +0,0 @@ -package context - -import ( - "github.com/onsonr/sonr/internal/gateway/models" - "github.com/onsonr/sonr/pkg/common" - "github.com/segmentio/ksuid" -) - -// initSession initializes or loads an existing session -func (s *HTTPContext) initSession() error { - sessionID := s.getOrCreateSessionID() - - // Try to load existing session - var sess models.Session - result := s.db.Where("id = ?", sessionID).First(&sess) - if result.Error != nil { - // Create new session if not found - sess = models.Session{ - ID: sessionID, - BrowserName: s.GetBrowser(), - BrowserVersion: s.GetMajorVersion(), - Platform: s.GetOS(), - IsMobile: s.IsMobile(), - IsTablet: s.IsTablet(), - IsDesktop: s.IsDesktop(), - IsBot: s.IsBot(), - IsTV: s.IsTV(), - } - if err := s.db.Create(&sess).Error; err != nil { - return err - } - } - s.sess = &sess - return nil -} - -func (s *HTTPContext) getOrCreateSessionID() string { - if ok := common.CookieExists(s.Context, common.SessionID); !ok { - sessionID := ksuid.New().String() - common.WriteCookie(s.Context, common.SessionID, sessionID) - return sessionID - } - - sessionID, err := common.ReadCookie(s.Context, common.SessionID) - if err != nil { - sessionID = ksuid.New().String() - common.WriteCookie(s.Context, common.SessionID, sessionID) - } - return sessionID -} diff --git a/internal/gateway/context/middleware.go b/internal/gateway/context/middleware.go deleted file mode 100644 index c4377b688..000000000 --- a/internal/gateway/context/middleware.go +++ /dev/null @@ -1,63 +0,0 @@ -package context - -import ( - "net/http" - - "github.com/labstack/echo/v4" - "github.com/medama-io/go-useragent" - "github.com/onsonr/sonr/internal/gateway/models" - "github.com/onsonr/sonr/internal/gateway/services" - config "github.com/onsonr/sonr/pkg/config/hway" - "gorm.io/gorm" -) - -// Middleware creates a new session middleware -func Middleware(db *gorm.DB, env config.Hway) echo.MiddlewareFunc { - ua := useragent.NewParser() - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - agent := ua.Parse(c.Request().UserAgent()) - cc := NewHTTPContext(c, db, agent, env.GetSonrGrpcUrl()) - if err := cc.initSession(); err != nil { - return err - } - return next(cc) - } - } -} - -// HTTPContext is the context for HTTP endpoints. -type HTTPContext struct { - echo.Context - *services.ResolverService - db *gorm.DB - sess *models.Session - user *models.User - env config.Hway - useragent.UserAgent -} - -// Get returns the HTTPContext from the echo context -func Get(c echo.Context) (*HTTPContext, error) { - ctx, ok := c.(*HTTPContext) - if !ok { - return nil, echo.NewHTTPError(http.StatusInternalServerError, "Session Context not found") - } - return ctx, nil -} - -// NewHTTPContext creates a new session context -func NewHTTPContext(c echo.Context, db *gorm.DB, a useragent.UserAgent, grpcAddr string) *HTTPContext { - rsv := services.NewResolverService(grpcAddr) - return &HTTPContext{ - Context: c, - db: db, - ResolverService: rsv, - UserAgent: a, - } -} - -// Session returns the current session -func (s *HTTPContext) Session() *models.Session { - return s.sess -} diff --git a/internal/gateway/context/store.go b/internal/gateway/context/store.go deleted file mode 100644 index 14459c0d5..000000000 --- a/internal/gateway/context/store.go +++ /dev/null @@ -1,58 +0,0 @@ -package context - -import ( - "fmt" - - "github.com/labstack/echo/v4" - "github.com/onsonr/sonr/internal/gateway/models" -) - -func InsertCredential(c echo.Context, handle string, cred *models.CredentialDescriptor) error { - sess, err := Get(c) - if err != nil { - return err - } - return sess.db.Save(cred.ToDBModel(handle, c.Request().Host)).Error -} - -func InsertProfile(c echo.Context) error { - sess, err := Get(c) - if err != nil { - return err - } - handle := c.FormValue("handle") - firstName := c.FormValue("first_name") - lastName := c.FormValue("last_name") - return sess.db.Save(&models.User{ - Handle: handle, - Name: fmt.Sprintf("%s %s", firstName, lastName), - }).Error -} - -// ╭───────────────────────────────────────────────────────╮ -// │ DB Getter Functions │ -// ╰───────────────────────────────────────────────────────╯ - -// SessionID returns the session ID -func SessionID(c echo.Context) (string, error) { - sess, err := Get(c) - if err != nil { - return "", err - } - return sess.Session().ID, nil -} - -// HandleExists checks if a handle already exists in any session -func HandleExists(c echo.Context, handle string) (bool, error) { - sess, err := Get(c) - if err != nil { - return false, err - } - - var count int64 - if err := sess.db.Model(&models.User{}).Where("handle = ?", handle).Count(&count).Error; err != nil { - return false, err - } - - return count > 0, nil -} diff --git a/internal/gateway/handlers/render_index.go b/internal/gateway/handlers/render_index.go deleted file mode 100644 index 5fd086e77..000000000 --- a/internal/gateway/handlers/render_index.go +++ /dev/null @@ -1,21 +0,0 @@ -package handlers - -import ( - "github.com/labstack/echo/v4" - "github.com/onsonr/sonr/internal/gateway/context" - "github.com/onsonr/sonr/internal/gateway/views" - "github.com/onsonr/sonr/pkg/common/response" -) - -func RenderIndex(c echo.Context) error { - return response.TemplEcho(c, views.InitialView(isUnavailableDevice(c))) -} - -// isUnavailableDevice returns true if the device is unavailable -func isUnavailableDevice(c echo.Context) bool { - s, err := context.Get(c) - if err != nil { - return true - } - return s.IsBot() || s.IsTV() -} diff --git a/internal/gateway/handlers/render_register.go b/internal/gateway/handlers/render_register.go deleted file mode 100644 index 8b9e23ea4..000000000 --- a/internal/gateway/handlers/render_register.go +++ /dev/null @@ -1,54 +0,0 @@ -package handlers - -import ( - "fmt" - "net/http" - - "github.com/go-webauthn/webauthn/protocol" - "github.com/labstack/echo/v4" - "github.com/onsonr/sonr/crypto/mpc" - "github.com/onsonr/sonr/internal/gateway/models" - "github.com/onsonr/sonr/internal/gateway/views" - "github.com/onsonr/sonr/pkg/common/response" - "golang.org/x/exp/rand" -) - -func RenderProfileCreate(c echo.Context) error { - d := models.CreateProfileData{ - FirstNumber: rand.Intn(5) + 1, - LastNumber: rand.Intn(4) + 1, - } - return response.TemplEcho(c, views.CreateProfileForm(d)) -} - -func RenderPasskeyCreate(c echo.Context) error { - challenge, _ := protocol.CreateChallenge() - handle := c.FormValue("handle") - firstName := c.FormValue("first_name") - lastName := c.FormValue("last_name") - - ks, err := mpc.GenEnclave() - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) - } - dat := models.CreatePasskeyData{ - Address: ks.Address(), - Handle: handle, - Name: fmt.Sprintf("%s %s", firstName, lastName), - Challenge: challenge.String(), - CreationBlock: "00001", - } - return response.TemplEcho(c, views.CreatePasskeyForm(dat)) -} - -func RenderVaultLoading(c echo.Context) error { - credentialJSON := c.FormValue("credential") - if credentialJSON == "" { - return echo.NewHTTPError(http.StatusBadRequest, "missing credential data") - } - _, err := models.ExtractCredentialDescriptor(credentialJSON) - if err != nil { - return err - } - return response.TemplEcho(c, views.LoadingVaultView()) -} diff --git a/internal/gateway/handlers/validate_credential.go b/internal/gateway/handlers/validate_credential.go deleted file mode 100644 index 6e5090013..000000000 --- a/internal/gateway/handlers/validate_credential.go +++ /dev/null @@ -1,11 +0,0 @@ -package handlers - -import ( - "github.com/labstack/echo/v4" -) - -// ValidateCredentialSubmit finds the user credential and validates it against the -// session challenge -func ValidateCredentialSubmit(c echo.Context) error { - return nil -} diff --git a/internal/gateway/handlers/validate_profile.go b/internal/gateway/handlers/validate_profile.go deleted file mode 100644 index 44a00c8a3..000000000 --- a/internal/gateway/handlers/validate_profile.go +++ /dev/null @@ -1,8 +0,0 @@ -package handlers - -import "github.com/labstack/echo/v4" - -// ValidateProfileHandle finds the chosen handle and verifies it is unique -func ValidateProfileSubmit(c echo.Context) error { - return nil -} diff --git a/internal/gateway/models/db_orm.go b/internal/gateway/models/db_orm.go deleted file mode 100644 index dadac6a45..000000000 --- a/internal/gateway/models/db_orm.go +++ /dev/null @@ -1,37 +0,0 @@ -package models - -import ( - "gorm.io/gorm" -) - -type Credential struct { - gorm.Model - Handle string `json:"handle"` - ID string `json:"id"` - Origin string `json:"origin"` - Type string `json:"type"` - Transports string `json:"transports"` -} - -type Session struct { - gorm.Model - ID string `json:"id" gorm:"primaryKey"` - BrowserName string `json:"browserName"` - BrowserVersion string `json:"browserVersion"` - Platform string `json:"platform"` - IsDesktop bool `json:"isDesktop"` - IsMobile bool `json:"isMobile"` - IsTablet bool `json:"isTablet"` - IsTV bool `json:"isTV"` - IsBot bool `json:"isBot"` - Challenge string `json:"challenge"` -} - -type User struct { - gorm.Model - Address string `json:"address"` - Handle string `json:"handle"` - Origin string `json:"origin"` - Name string `json:"name"` - CID string `json:"cid"` -} diff --git a/internal/gateway/models/form_data.go b/internal/gateway/models/form_data.go deleted file mode 100644 index 6613755f7..000000000 --- a/internal/gateway/models/form_data.go +++ /dev/null @@ -1,15 +0,0 @@ -package models - -type CreatePasskeyData struct { - Address string - Handle string - Name string - Challenge string - CreationBlock string -} - -type CreateProfileData struct { - TurnstileSiteKey string - FirstNumber int - LastNumber int -} diff --git a/internal/gateway/models/resolver.go b/internal/gateway/models/resolver.go deleted file mode 100644 index 2640e7f93..000000000 --- a/internal/gateway/models/resolver.go +++ /dev/null @@ -1 +0,0 @@ -package models diff --git a/internal/gateway/models/webauthn.go b/internal/gateway/models/webauthn.go deleted file mode 100644 index c44ff2524..000000000 --- a/internal/gateway/models/webauthn.go +++ /dev/null @@ -1,64 +0,0 @@ -package models - -import ( - "encoding/json" - "fmt" -) - -// Define the credential structure matching our frontend data -type CredentialDescriptor struct { - ID string `json:"id"` - RawID string `json:"rawId"` - Type string `json:"type"` - AuthenticatorAttachment string `json:"authenticatorAttachment"` - Transports string `json:"transports"` - ClientExtensionResults map[string]string `json:"clientExtensionResults"` - Response struct { - AttestationObject string `json:"attestationObject"` - ClientDataJSON string `json:"clientDataJSON"` - } `json:"response"` -} - -func (c *CredentialDescriptor) ToDBModel(handle, origin string) *Credential { - return &Credential{ - Handle: handle, - Origin: origin, - ID: c.ID, - Type: c.Type, - Transports: c.Transports, - } -} - -func ExtractCredentialDescriptor(jsonString string) (*CredentialDescriptor, error) { - cred := &CredentialDescriptor{} - // Unmarshal the credential JSON - if err := json.Unmarshal([]byte(jsonString), cred); err != nil { - return nil, err - } - - // Validate required fields - if cred.ID == "" || cred.RawID == "" { - return nil, fmt.Errorf("missing credential ID") - } - if cred.Type != "public-key" { - return nil, fmt.Errorf("invalid credential type") - } - if cred.Response.AttestationObject == "" || cred.Response.ClientDataJSON == "" { - return nil, fmt.Errorf("missing attestation data") - } - - // Log detailed credential information - fmt.Printf("Credential Details:\n"+ - "ID: %s\n"+ - "Raw ID: %s\n"+ - "Type: %s\n"+ - "Authenticator Attachment: %s\n"+ - "Transports: %v\n"+ - cred.ID, - cred.RawID, - cred.Type, - cred.AuthenticatorAttachment, - cred.Transports, - ) - return cred, nil -} diff --git a/internal/gateway/routes.go b/internal/gateway/routes.go deleted file mode 100644 index 7bbee58fe..000000000 --- a/internal/gateway/routes.go +++ /dev/null @@ -1,81 +0,0 @@ -// Package gateway provides the default routes for the Sonr hway. -package gateway - -import ( - "os" - "path/filepath" - "strings" - - "github.com/labstack/echo/v4" - "github.com/onsonr/sonr/internal/gateway/context" - "github.com/onsonr/sonr/internal/gateway/handlers" - "github.com/onsonr/sonr/internal/gateway/models" - "github.com/onsonr/sonr/pkg/common/response" - config "github.com/onsonr/sonr/pkg/config/hway" - "gorm.io/driver/postgres" - "gorm.io/driver/sqlite" - "gorm.io/gorm" -) - -func RegisterRoutes(e *echo.Echo, env config.Hway, db *gorm.DB) error { - // Custom error handler for gateway - e.HTTPErrorHandler = response.RedirectOnError("http://localhost:3000") - - // Inject session middleware with database connection - e.Use(context.Middleware(db, env)) - - // Register View Handlers - e.GET("/", handlers.RenderIndex) - e.GET("/register", handlers.RenderProfileCreate) - e.POST("/register/passkey", handlers.RenderPasskeyCreate) - e.POST("/register/loading", handlers.RenderVaultLoading) - - // Register Validation Handlers - e.PUT("/register/profile/submit", handlers.ValidateProfileSubmit) - e.PUT("/register/passkey/submit", handlers.ValidateCredentialSubmit) - return nil -} - -// NewGormDB initializes and returns a configured database connection -func NewDB(env config.Hway) (*gorm.DB, error) { - // Try PostgreSQL first if DSN is provided - if dsn := env.GetPsqlDSN(); dsn != "" && !strings.Contains(dsn, "password= ") { - db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{}) - if err == nil { - // Test the connection - sqlDB, err := db.DB() - if err == nil { - if err = sqlDB.Ping(); err == nil { - // Successfully connected to PostgreSQL - db.AutoMigrate(&models.Credential{}) - db.AutoMigrate(&models.Session{}) - db.AutoMigrate(&models.User{}) - return db, nil - } - } - } - } - - // Fall back to SQLite - path := formatDBPath(env.GetSqliteFile()) - db, err := gorm.Open(sqlite.Open(path), &gorm.Config{}) - if err != nil { - return nil, err - } - - // Migrate the schema - db.AutoMigrate(&models.Credential{}) - db.AutoMigrate(&models.Session{}) - db.AutoMigrate(&models.User{}) - return db, nil -} - -func formatDBPath(fileName string) string { - configDir := filepath.Join(os.Getenv("XDG_CONFIG_HOME"), "hway") - if err := os.MkdirAll(configDir, 0o755); err != nil { - // If we can't create the directory, fall back to current directory - return configDir - } - - return filepath.Join(configDir, fileName) -} diff --git a/internal/gateway/services/resolver_service.go b/internal/gateway/services/resolver_service.go deleted file mode 100644 index 26fc8c96c..000000000 --- a/internal/gateway/services/resolver_service.go +++ /dev/null @@ -1,59 +0,0 @@ -package services - -import ( - bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1" - didv1 "github.com/onsonr/sonr/api/did/v1" - dwnv1 "github.com/onsonr/sonr/api/dwn/v1" - svcv1 "github.com/onsonr/sonr/api/svc/v1" - "google.golang.org/grpc" -) - -type ResolverService struct { - grpcAddr string -} - -func NewResolverService(grpcAddr string) *ResolverService { - return &ResolverService{ - grpcAddr: grpcAddr, - } -} - -func (s *ResolverService) getClientConn() (*grpc.ClientConn, error) { - grpcConn, err := grpc.NewClient(s.grpcAddr, grpc.WithInsecure()) - if err != nil { - return nil, err - } - return grpcConn, nil -} - -func (s *ResolverService) BankQuery() (bankv1beta1.QueryClient, error) { - conn, err := s.getClientConn() - if err != nil { - return nil, err - } - return bankv1beta1.NewQueryClient(conn), nil -} - -func (s *ResolverService) DIDQuery() (didv1.QueryClient, error) { - conn, err := s.getClientConn() - if err != nil { - return nil, err - } - return didv1.NewQueryClient(conn), nil -} - -func (s *ResolverService) DWNQuery() (dwnv1.QueryClient, error) { - conn, err := s.getClientConn() - if err != nil { - return nil, err - } - return dwnv1.NewQueryClient(conn), nil -} - -func (s *ResolverService) SVCQuery() (svcv1.QueryClient, error) { - conn, err := s.getClientConn() - if err != nil { - return nil, err - } - return svcv1.NewQueryClient(conn), nil -} diff --git a/internal/gateway/services/user_service.go b/internal/gateway/services/user_service.go deleted file mode 100644 index e2dacf460..000000000 --- a/internal/gateway/services/user_service.go +++ /dev/null @@ -1,7 +0,0 @@ -package services - -import "gorm.io/gorm" - -type UserService struct { - db *gorm.DB -} diff --git a/internal/gateway/services/vault_service.go b/internal/gateway/services/vault_service.go deleted file mode 100644 index f97d8befc..000000000 --- a/internal/gateway/services/vault_service.go +++ /dev/null @@ -1,18 +0,0 @@ -package services - -import ( - "github.com/onsonr/sonr/pkg/ipfsapi" - "gorm.io/gorm" -) - -type VaultService struct { - db *gorm.DB - tokenStore ipfsapi.IPFSTokenStore -} - -func NewVaultService(db *gorm.DB, ipc ipfsapi.Client) *VaultService { - return &VaultService{ - db: db, - tokenStore: ipfsapi.NewUCANStore(ipc), - } -} diff --git a/internal/gateway/views/register_view.templ b/internal/gateway/views/register_view.templ deleted file mode 100644 index 744badb4f..000000000 --- a/internal/gateway/views/register_view.templ +++ /dev/null @@ -1,69 +0,0 @@ -package views - -import ( - "github.com/onsonr/sonr/internal/gateway/models" - "github.com/onsonr/sonr/internal/nebula/card" - "github.com/onsonr/sonr/internal/nebula/form" - "github.com/onsonr/sonr/internal/nebula/hero" - "github.com/onsonr/sonr/internal/nebula/input" - "github.com/onsonr/sonr/internal/nebula/layout" -) - -templ CreateProfileForm(data models.CreateProfileData) { - @layout.View("New Profile | Sonr.ID") { - @layout.Container() { - @hero.TitleDesc("Basic Info", "Tell us a little about yourself.") - @formCreateProfile(data) - } - } -} - -templ CreatePasskeyForm(data models.CreatePasskeyData) { - @layout.View("Register | Sonr.ID") { - @layout.Container() { - @hero.TitleDesc("Link a PassKey", "This will be used to login to your vault.") - @formCreatePasskey(data) - } - } -} - -templ LoadingVaultView() { - @layout.View("Loading... | Sonr.ID") { - @layout.Container() { - @hero.TitleDesc("Loading Vault", "This will be used to login to your vault.") - } - } -} - -templ formCreatePasskey(data models.CreatePasskeyData) { - @form.Root("/register/finish", "POST", "passkey-form") { - - @form.Body() { - @form.Header() { - @card.SonrProfile(data.Address, data.Name, data.Handle, data.CreationBlock) - } - @input.CoinSelect() - @form.Footer() { - @input.Passkey(data.Address, data.Handle, data.Challenge) - @form.CancelButton() - } - } - } -} - -templ formCreateProfile(data models.CreateProfileData) { - @form.Root("/register/passkey", "POST", "create-profile") { - @form.Body() { - @form.Header() { - - } - @input.Name() - @input.Handle() - @input.HumanSlider(data.FirstNumber, data.LastNumber) - @form.Footer() { - @form.CancelButton() - @form.SubmitButton("Next") - } - } - } -} diff --git a/internal/gateway/views/register_view_templ.go b/internal/gateway/views/register_view_templ.go deleted file mode 100644 index dce3686cf..000000000 --- a/internal/gateway/views/register_view_templ.go +++ /dev/null @@ -1,501 +0,0 @@ -// Code generated by templ - DO NOT EDIT. - -// templ: version: v0.2.793 -package views - -//lint:file-ignore SA4006 This context is only used if a nested component is present. - -import "github.com/a-h/templ" -import templruntime "github.com/a-h/templ/runtime" - -import ( - "github.com/onsonr/sonr/internal/gateway/models" - "github.com/onsonr/sonr/internal/nebula/card" - "github.com/onsonr/sonr/internal/nebula/form" - "github.com/onsonr/sonr/internal/nebula/hero" - "github.com/onsonr/sonr/internal/nebula/input" - "github.com/onsonr/sonr/internal/nebula/layout" -) - -func CreateProfileForm(data models.CreateProfileData) templ.Component { - return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { - return templ_7745c5c3_CtxErr - } - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var1 := templ.GetChildren(ctx) - if templ_7745c5c3_Var1 == nil { - templ_7745c5c3_Var1 = templ.NopComponent - } - ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var3 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = hero.TitleDesc("Basic Info", "Tell us a little about yourself.").Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = formCreateProfile(data).Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) - templ_7745c5c3_Err = layout.Container().Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) - templ_7745c5c3_Err = layout.View("New Profile | Sonr.ID").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) -} - -func CreatePasskeyForm(data models.CreatePasskeyData) templ.Component { - return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { - return templ_7745c5c3_CtxErr - } - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var4 := templ.GetChildren(ctx) - if templ_7745c5c3_Var4 == nil { - templ_7745c5c3_Var4 = templ.NopComponent - } - ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Var5 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var6 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = hero.TitleDesc("Link a PassKey", "This will be used to login to your vault.").Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = formCreatePasskey(data).Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) - templ_7745c5c3_Err = layout.Container().Render(templ.WithChildren(ctx, templ_7745c5c3_Var6), templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) - templ_7745c5c3_Err = layout.View("Register | Sonr.ID").Render(templ.WithChildren(ctx, templ_7745c5c3_Var5), templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) -} - -func LoadingVaultView() templ.Component { - return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { - return templ_7745c5c3_CtxErr - } - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var7 := templ.GetChildren(ctx) - if templ_7745c5c3_Var7 == nil { - templ_7745c5c3_Var7 = templ.NopComponent - } - ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Var8 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var9 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = hero.TitleDesc("Loading Vault", "This will be used to login to your vault.").Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) - templ_7745c5c3_Err = layout.Container().Render(templ.WithChildren(ctx, templ_7745c5c3_Var9), templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) - templ_7745c5c3_Err = layout.View("Loading... | Sonr.ID").Render(templ.WithChildren(ctx, templ_7745c5c3_Var8), templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) -} - -func formCreatePasskey(data models.CreatePasskeyData) templ.Component { - return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { - return templ_7745c5c3_CtxErr - } - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var10 := templ.GetChildren(ctx) - if templ_7745c5c3_Var10 == nil { - templ_7745c5c3_Var10 = templ.NopComponent - } - ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Var11 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Var12 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var13 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = card.SonrProfile(data.Address, data.Name, data.Handle, data.CreationBlock).Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) - templ_7745c5c3_Err = form.Header().Render(templ.WithChildren(ctx, templ_7745c5c3_Var13), templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = input.CoinSelect().Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Var14 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = input.Passkey(data.Address, data.Handle, data.Challenge).Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = form.CancelButton().Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) - templ_7745c5c3_Err = form.Footer().Render(templ.WithChildren(ctx, templ_7745c5c3_Var14), templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) - templ_7745c5c3_Err = form.Body().Render(templ.WithChildren(ctx, templ_7745c5c3_Var12), templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) - templ_7745c5c3_Err = form.Root("/register/finish", "POST", "passkey-form").Render(templ.WithChildren(ctx, templ_7745c5c3_Var11), templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) -} - -func formCreateProfile(data models.CreateProfileData) templ.Component { - return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { - return templ_7745c5c3_CtxErr - } - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var15 := templ.GetChildren(ctx) - if templ_7745c5c3_Var15 == nil { - templ_7745c5c3_Var15 = templ.NopComponent - } - ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Var16 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var17 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var18 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) - templ_7745c5c3_Err = form.Header().Render(templ.WithChildren(ctx, templ_7745c5c3_Var18), templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = input.Name().Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = input.Handle().Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = input.HumanSlider(data.FirstNumber, data.LastNumber).Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Var19 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = form.CancelButton().Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = form.SubmitButton("Next").Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) - templ_7745c5c3_Err = form.Footer().Render(templ.WithChildren(ctx, templ_7745c5c3_Var19), templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) - templ_7745c5c3_Err = form.Body().Render(templ.WithChildren(ctx, templ_7745c5c3_Var17), templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) - templ_7745c5c3_Err = form.Root("/register/passkey", "POST", "create-profile").Render(templ.WithChildren(ctx, templ_7745c5c3_Var16), templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) -} - -var _ = templruntime.GeneratedTemplate diff --git a/pkg/common/models/Account.pkl.go b/internal/models/Account.pkl.go similarity index 100% rename from pkg/common/models/Account.pkl.go rename to internal/models/Account.pkl.go diff --git a/pkg/common/models/Asset.pkl.go b/internal/models/Asset.pkl.go similarity index 100% rename from pkg/common/models/Asset.pkl.go rename to internal/models/Asset.pkl.go diff --git a/pkg/common/models/Chain.pkl.go b/internal/models/Chain.pkl.go similarity index 100% rename from pkg/common/models/Chain.pkl.go rename to internal/models/Chain.pkl.go diff --git a/pkg/common/models/Credential.pkl.go b/internal/models/Credential.pkl.go similarity index 100% rename from pkg/common/models/Credential.pkl.go rename to internal/models/Credential.pkl.go diff --git a/pkg/common/models/DID.pkl.go b/internal/models/DID.pkl.go similarity index 62% rename from pkg/common/models/DID.pkl.go rename to internal/models/DID.pkl.go index ac3cf6a63..598868802 100644 --- a/pkg/common/models/DID.pkl.go +++ b/internal/models/DID.pkl.go @@ -2,11 +2,11 @@ package models import ( - "github.com/onsonr/sonr/pkg/common/models/keyalgorithm" - "github.com/onsonr/sonr/pkg/common/models/keycurve" - "github.com/onsonr/sonr/pkg/common/models/keyencoding" - "github.com/onsonr/sonr/pkg/common/models/keyrole" - "github.com/onsonr/sonr/pkg/common/models/keytype" + "github.com/onsonr/sonr/internal/models/keyalgorithm" + "github.com/onsonr/sonr/internal/models/keycurve" + "github.com/onsonr/sonr/internal/models/keyencoding" + "github.com/onsonr/sonr/internal/models/keyrole" + "github.com/onsonr/sonr/internal/models/keytype" ) type DID struct { diff --git a/pkg/common/models/Grant.pkl.go b/internal/models/Grant.pkl.go similarity index 100% rename from pkg/common/models/Grant.pkl.go rename to internal/models/Grant.pkl.go diff --git a/pkg/common/models/JWK.pkl.go b/internal/models/JWK.pkl.go similarity index 100% rename from pkg/common/models/JWK.pkl.go rename to internal/models/JWK.pkl.go diff --git a/pkg/common/models/Keyshare.pkl.go b/internal/models/Keyshare.pkl.go similarity index 100% rename from pkg/common/models/Keyshare.pkl.go rename to internal/models/Keyshare.pkl.go diff --git a/pkg/common/models/Models.pkl.go b/internal/models/Models.pkl.go similarity index 100% rename from pkg/common/models/Models.pkl.go rename to internal/models/Models.pkl.go diff --git a/pkg/common/models/Profile.pkl.go b/internal/models/Profile.pkl.go similarity index 100% rename from pkg/common/models/Profile.pkl.go rename to internal/models/Profile.pkl.go diff --git a/pkg/common/models/assettype/AssetType.pkl.go b/internal/models/assettype/AssetType.pkl.go similarity index 100% rename from pkg/common/models/assettype/AssetType.pkl.go rename to internal/models/assettype/AssetType.pkl.go diff --git a/pkg/common/models/didmethod/DIDMethod.pkl.go b/internal/models/didmethod/DIDMethod.pkl.go similarity index 100% rename from pkg/common/models/didmethod/DIDMethod.pkl.go rename to internal/models/didmethod/DIDMethod.pkl.go diff --git a/pkg/common/models/init.pkl.go b/internal/models/init.pkl.go similarity index 100% rename from pkg/common/models/init.pkl.go rename to internal/models/init.pkl.go diff --git a/pkg/common/models/keyalgorithm/KeyAlgorithm.pkl.go b/internal/models/keyalgorithm/KeyAlgorithm.pkl.go similarity index 100% rename from pkg/common/models/keyalgorithm/KeyAlgorithm.pkl.go rename to internal/models/keyalgorithm/KeyAlgorithm.pkl.go diff --git a/pkg/common/models/keycurve/KeyCurve.pkl.go b/internal/models/keycurve/KeyCurve.pkl.go similarity index 100% rename from pkg/common/models/keycurve/KeyCurve.pkl.go rename to internal/models/keycurve/KeyCurve.pkl.go diff --git a/pkg/common/models/keyencoding/KeyEncoding.pkl.go b/internal/models/keyencoding/KeyEncoding.pkl.go similarity index 100% rename from pkg/common/models/keyencoding/KeyEncoding.pkl.go rename to internal/models/keyencoding/KeyEncoding.pkl.go diff --git a/pkg/common/models/keyrole/KeyRole.pkl.go b/internal/models/keyrole/KeyRole.pkl.go similarity index 100% rename from pkg/common/models/keyrole/KeyRole.pkl.go rename to internal/models/keyrole/KeyRole.pkl.go diff --git a/pkg/common/models/keysharerole/KeyShareRole.pkl.go b/internal/models/keysharerole/KeyShareRole.pkl.go similarity index 100% rename from pkg/common/models/keysharerole/KeyShareRole.pkl.go rename to internal/models/keysharerole/KeyShareRole.pkl.go diff --git a/pkg/common/models/keytype/KeyType.pkl.go b/internal/models/keytype/KeyType.pkl.go similarity index 100% rename from pkg/common/models/keytype/KeyType.pkl.go rename to internal/models/keytype/KeyType.pkl.go diff --git a/pkg/common/models/permissiongrant/PermissionGrant.pkl.go b/internal/models/permissiongrant/PermissionGrant.pkl.go similarity index 100% rename from pkg/common/models/permissiongrant/PermissionGrant.pkl.go rename to internal/models/permissiongrant/PermissionGrant.pkl.go diff --git a/pkg/common/models/permissionscope/PermissionScope.pkl.go b/internal/models/permissionscope/PermissionScope.pkl.go similarity index 100% rename from pkg/common/models/permissionscope/PermissionScope.pkl.go rename to internal/models/permissionscope/PermissionScope.pkl.go diff --git a/pkg/config/embed/manifest.go b/internal/models/webworker.go similarity index 98% rename from pkg/config/embed/manifest.go rename to internal/models/webworker.go index 560ce1233..84691135e 100644 --- a/pkg/config/embed/manifest.go +++ b/internal/models/webworker.go @@ -1,8 +1,8 @@ -package embed +package models import "encoding/json" -func newWebManifestBytes() ([]byte, error) { +func NewWebManifest() ([]byte, error) { return json.Marshal(baseWebManifest) } diff --git a/internal/nebula/form/base.templ b/internal/nebula/form/base.templ index 555e26fcb..8601ea94f 100644 --- a/internal/nebula/form/base.templ +++ b/internal/nebula/form/base.templ @@ -1,7 +1,7 @@ package form -templ Root(action, method, id string) { -
+templ Root(action, id string) { + { children... }
} diff --git a/internal/nebula/form/base_templ.go b/internal/nebula/form/base_templ.go index 660988f9e..bc43d4d71 100644 --- a/internal/nebula/form/base_templ.go +++ b/internal/nebula/form/base_templ.go @@ -8,7 +8,7 @@ package form import "github.com/a-h/templ" import templruntime "github.com/a-h/templ/runtime" -func Root(action, method, id string) templ.Component { +func Root(action, id string) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { @@ -29,42 +29,20 @@ func Root(action, method, id string) templ.Component { templ_7745c5c3_Var1 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-target=\"#{ id }\" hx-swap=\"outerHTML\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -96,16 +74,16 @@ func Header() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var5 := templ.GetChildren(ctx) - if templ_7745c5c3_Var5 == nil { - templ_7745c5c3_Var5 = templ.NopComponent + templ_7745c5c3_Var3 := templ.GetChildren(ctx) + if templ_7745c5c3_Var3 == nil { + templ_7745c5c3_Var3 = templ.NopComponent } ctx = templ.ClearChildren(ctx) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templ_7745c5c3_Var5.Render(ctx, templ_7745c5c3_Buffer) + templ_7745c5c3_Err = templ_7745c5c3_Var3.Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -133,16 +111,16 @@ func Body() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var6 := templ.GetChildren(ctx) - if templ_7745c5c3_Var6 == nil { - templ_7745c5c3_Var6 = templ.NopComponent + templ_7745c5c3_Var4 := templ.GetChildren(ctx) + if templ_7745c5c3_Var4 == nil { + templ_7745c5c3_Var4 = templ.NopComponent } ctx = templ.ClearChildren(ctx) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templ_7745c5c3_Var6.Render(ctx, templ_7745c5c3_Buffer) + templ_7745c5c3_Err = templ_7745c5c3_Var4.Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -170,16 +148,16 @@ func Footer() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var7 := templ.GetChildren(ctx) - if templ_7745c5c3_Var7 == nil { - templ_7745c5c3_Var7 = templ.NopComponent + templ_7745c5c3_Var5 := templ.GetChildren(ctx) + if templ_7745c5c3_Var5 == nil { + templ_7745c5c3_Var5 = templ.NopComponent } ctx = templ.ClearChildren(ctx) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templ_7745c5c3_Var7.Render(ctx, templ_7745c5c3_Buffer) + templ_7745c5c3_Err = templ_7745c5c3_Var5.Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -207,9 +185,9 @@ func CancelButton() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var8 := templ.GetChildren(ctx) - if templ_7745c5c3_Var8 == nil { - templ_7745c5c3_Var8 = templ.NopComponent + templ_7745c5c3_Var6 := templ.GetChildren(ctx) + if templ_7745c5c3_Var6 == nil { + templ_7745c5c3_Var6 = templ.NopComponent } ctx = templ.ClearChildren(ctx) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" Cancel") @@ -236,21 +214,21 @@ func SubmitButton(text string) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var9 := templ.GetChildren(ctx) - if templ_7745c5c3_Var9 == nil { - templ_7745c5c3_Var9 = templ.NopComponent + templ_7745c5c3_Var7 := templ.GetChildren(ctx) + if templ_7745c5c3_Var7 == nil { + templ_7745c5c3_Var7 = templ.NopComponent } ctx = templ.ClearChildren(ctx) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var10 string - templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(text) + var templ_7745c5c3_Var8 string + templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(text) if templ_7745c5c3_Err != nil { return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/nebula/form/base.templ`, Line: 55, Col: 8} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/internal/nebula/input/input_handle.templ b/internal/nebula/input/input_handle.templ index 586a0dfc2..78bafc3be 100644 --- a/internal/nebula/input/input_handle.templ +++ b/internal/nebula/input/input_handle.templ @@ -13,8 +13,8 @@ func (s HandleState) string() string { } templ Handle() { -
- +
+
@@ -23,20 +23,20 @@ templ Handle() {
} -templ HandleError() { - +templ HandleError(value string) { +
- +

} -templ HandleValid() { - +templ HandleSuccess(value string) { +
diff --git a/internal/nebula/input/input_handle_templ.go b/internal/nebula/input/input_handle_templ.go index fb3496700..87c3bab24 100644 --- a/internal/nebula/input/input_handle_templ.go +++ b/internal/nebula/input/input_handle_templ.go @@ -41,7 +41,7 @@ func Handle() templ.Component { templ_7745c5c3_Var1 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -49,7 +49,7 @@ func Handle() templ.Component { }) } -func HandleError() templ.Component { +func HandleError(value string) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { @@ -70,7 +70,20 @@ func HandleError() templ.Component { templ_7745c5c3_Var2 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -78,7 +91,7 @@ func HandleError() templ.Component { }) } -func HandleValid() templ.Component { +func HandleSuccess(value string) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { @@ -94,12 +107,25 @@ func HandleValid() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var3 := templ.GetChildren(ctx) - if templ_7745c5c3_Var3 == nil { - templ_7745c5c3_Var3 = templ.NopComponent + templ_7745c5c3_Var4 := templ.GetChildren(ctx) + if templ_7745c5c3_Var4 == nil { + templ_7745c5c3_Var4 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/internal/nebula/input/input_ishuman.templ b/internal/nebula/input/input_ishuman.templ new file mode 100644 index 000000000..2b0ef1437 --- /dev/null +++ b/internal/nebula/input/input_ishuman.templ @@ -0,0 +1,25 @@ +package input + +import "fmt" + +templ HumanSlider(firstNumber int, lastNumber int) { +
+ +
+} + +templ HumanSliderError(firstNumber int, lastNumber int) { + +
+ + Invalid Human Sum +
+} + +templ HumanSliderSuccess() { + +} + +func humanLabel(firstNumber int, lastNumber int) string { + return fmt.Sprintf("What is %d + %d?", firstNumber, lastNumber) +} diff --git a/internal/nebula/input/input_ishuman_templ.go b/internal/nebula/input/input_ishuman_templ.go new file mode 100644 index 000000000..b7f71d3b9 --- /dev/null +++ b/internal/nebula/input/input_ishuman_templ.go @@ -0,0 +1,130 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.2.793 +package input + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" + +import "fmt" + +func HumanSlider(firstNumber int, lastNumber int) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var1 := templ.GetChildren(ctx) + if templ_7745c5c3_Var1 == nil { + templ_7745c5c3_Var1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +func HumanSliderError(firstNumber int, lastNumber int) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var3 := templ.GetChildren(ctx) + if templ_7745c5c3_Var3 == nil { + templ_7745c5c3_Var3 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
Invalid Human Sum
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +func HumanSliderSuccess() templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var5 := templ.GetChildren(ctx) + if templ_7745c5c3_Var5 == nil { + templ_7745c5c3_Var5 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +func humanLabel(firstNumber int, lastNumber int) string { + return fmt.Sprintf("What is %d + %d?", firstNumber, lastNumber) +} + +var _ = templruntime.GeneratedTemplate diff --git a/internal/nebula/input/input_name.templ b/internal/nebula/input/input_name.templ index 19eaae4f9..10caae91c 100644 --- a/internal/nebula/input/input_name.templ +++ b/internal/nebula/input/input_name.templ @@ -14,7 +14,7 @@ func (s NameState) string() string { templ Name() {
- +

diff --git a/internal/nebula/input/input_name_templ.go b/internal/nebula/input/input_name_templ.go index c036e18ba..cc2845d6d 100644 --- a/internal/nebula/input/input_name_templ.go +++ b/internal/nebula/input/input_name_templ.go @@ -41,7 +41,7 @@ func Name() templ.Component { templ_7745c5c3_Var1 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/internal/nebula/input/input_passkey.templ b/internal/nebula/input/input_passkey.templ index c9510c4e8..e2236289e 100644 --- a/internal/nebula/input/input_passkey.templ +++ b/internal/nebula/input/input_passkey.templ @@ -39,6 +39,9 @@ script navigatorCredentialsCreate(userId string, userHandle string, challenge st payment: { isPayment: true, }, + largeBlob: { + supported: "preferred", + }, }, }; diff --git a/internal/nebula/input/input_passkey_templ.go b/internal/nebula/input/input_passkey_templ.go index 7a2988adf..239116850 100644 --- a/internal/nebula/input/input_passkey_templ.go +++ b/internal/nebula/input/input_passkey_templ.go @@ -52,8 +52,8 @@ func Passkey(addr string, userHandle string, challenge string) templ.Component { func navigatorCredentialsCreate(userId string, userHandle string, challenge string) templ.ComponentScript { return templ.ComponentScript{ - Name: `__templ_navigatorCredentialsCreate_7340`, - Function: `function __templ_navigatorCredentialsCreate_7340(userId, userHandle, challenge){const publicKey = { + Name: `__templ_navigatorCredentialsCreate_63c0`, + Function: `function __templ_navigatorCredentialsCreate_63c0(userId, userHandle, challenge){const publicKey = { challenge: Uint8Array.from(challenge, (c) => c.charCodeAt(0)), rp: { name: "Sonr.ID", @@ -84,6 +84,9 @@ func navigatorCredentialsCreate(userId string, userHandle string, challenge stri payment: { isPayment: true, }, + largeBlob: { + supported: "preferred", + }, }, }; @@ -137,8 +140,8 @@ navigator.credentials alert(` + "`" + `Failed to create passkey: ${err.message || 'Unknown error'}` + "`" + `); }); }`, - Call: templ.SafeScript(`__templ_navigatorCredentialsCreate_7340`, userId, userHandle, challenge), - CallInline: templ.SafeScriptInline(`__templ_navigatorCredentialsCreate_7340`, userId, userHandle, challenge), + Call: templ.SafeScript(`__templ_navigatorCredentialsCreate_63c0`, userId, userHandle, challenge), + CallInline: templ.SafeScriptInline(`__templ_navigatorCredentialsCreate_63c0`, userId, userHandle, challenge), } } diff --git a/internal/nebula/input/slider_isHuman.templ b/internal/nebula/input/slider_isHuman.templ deleted file mode 100644 index 2d3d8baa7..000000000 --- a/internal/nebula/input/slider_isHuman.templ +++ /dev/null @@ -1,11 +0,0 @@ -package input - -import "fmt" - -templ HumanSlider(firstNumber int, lastNumber int) { - -} - -func formatHumanSliderLabel(firstNumber int, lastNumber int) string { - return fmt.Sprintf("What is %d + %d?", firstNumber, lastNumber) -} diff --git a/internal/nebula/input/slider_isHuman_templ.go b/internal/nebula/input/slider_isHuman_templ.go deleted file mode 100644 index 505ec86fa..000000000 --- a/internal/nebula/input/slider_isHuman_templ.go +++ /dev/null @@ -1,59 +0,0 @@ -// Code generated by templ - DO NOT EDIT. - -// templ: version: v0.2.793 -package input - -//lint:file-ignore SA4006 This context is only used if a nested component is present. - -import "github.com/a-h/templ" -import templruntime "github.com/a-h/templ/runtime" - -import "fmt" - -func HumanSlider(firstNumber int, lastNumber int) templ.Component { - return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { - return templ_7745c5c3_CtxErr - } - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var1 := templ.GetChildren(ctx) - if templ_7745c5c3_Var1 == nil { - templ_7745c5c3_Var1 = templ.NopComponent - } - ctx = templ.ClearChildren(ctx) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) -} - -func formatHumanSliderLabel(firstNumber int, lastNumber int) string { - return fmt.Sprintf("What is %d + %d?", firstNumber, lastNumber) -} - -var _ = templruntime.GeneratedTemplate diff --git a/internal/nebula/layout/head.templ b/internal/nebula/layout/head.templ index d42780a80..d2643c111 100644 --- a/internal/nebula/layout/head.templ +++ b/internal/nebula/layout/head.templ @@ -103,6 +103,8 @@ templ Htmx() { + + } } diff --git a/internal/nebula/layout/head_templ.go b/internal/nebula/layout/head_templ.go index fe6d98888..d14958299 100644 --- a/internal/nebula/layout/head_templ.go +++ b/internal/nebula/layout/head_templ.go @@ -431,6 +431,32 @@ func Htmx() templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"> ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err @@ -462,21 +488,21 @@ func Nebula(version string) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var21 := templ.GetChildren(ctx) - if templ_7745c5c3_Var21 == nil { - templ_7745c5c3_Var21 = templ.NopComponent + templ_7745c5c3_Var23 := templ.GetChildren(ctx) + if templ_7745c5c3_Var23 == nil { + templ_7745c5c3_Var23 = templ.NopComponent } ctx = templ.ClearChildren(ctx) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(":" that is used - // to extract token from the request. - // Optional. Default value "header:Authorization". - // Possible values: - // - "header:" - // - "query:" - // - "param:" - // - "cookie:" - TokenLookup string - - // AuthScheme to be used in the Authorization header. - // Optional. Default value "Bearer". - AuthScheme string -} - -// DefaultControllerConfig is the default UCAN middleware config -var DefaultControllerConfig = ControllerConfig{ - Skipper: nil, - TokenLookup: "header:Authorization", - AuthScheme: "Bearer", -} - -type Option func(c *ControllerConfig) - -func WithSkipper(skipper func(c echo.Context) bool) Option { - return func(c *ControllerConfig) { - c.Skipper = skipper - } -} - -func WithAuthScheme(scheme string) Option { - return func(c *ControllerConfig) { - c.AuthScheme = scheme - } -} - -// WithTokenLookup sets the token lookup strategy -func WithTokenLookup(lookup string) Option { - return func(c *ControllerConfig) { - c.TokenLookup = lookup - } -} diff --git a/pkg/didauth/controller/middleware.go b/pkg/didauth/controller/middleware.go deleted file mode 100644 index 3c88539a1..000000000 --- a/pkg/didauth/controller/middleware.go +++ /dev/null @@ -1,122 +0,0 @@ -//go:build js && wasm -// +build js,wasm - -package controller - -import ( - "fmt" - "strings" - - "github.com/labstack/echo/v4" - "github.com/onsonr/sonr/crypto/mpc/spec" -) - -// Middleware returns middleware to validate Middleware tokens -func Middleware(source spec.KeyshareSource, opts ...Option) echo.MiddlewareFunc { - c := DefaultControllerConfig - for _, opt := range opts { - opt(&c) - } - c.KeySource = source - return initWithConfig(c) -} - -// initWithConfig returns UCAN middleware with custom config -func initWithConfig(config ControllerConfig) echo.MiddlewareFunc { - // Defaults - if config.Skipper == nil { - config.Skipper = DefaultControllerConfig.Skipper - } - if config.TokenLookup == "" { - config.TokenLookup = DefaultControllerConfig.TokenLookup - } - if config.AuthScheme == "" { - config.AuthScheme = DefaultControllerConfig.AuthScheme - } - - // Initialize - parts := strings.Split(config.TokenLookup, ":") - extractor := tokenFromHeader(parts[1], config.AuthScheme) - switch parts[0] { - case "query": - extractor = tokenFromQuery(parts[1]) - case "param": - extractor = tokenFromParam(parts[1]) - case "cookie": - extractor = tokenFromCookie(parts[1]) - } - - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - if config.Skipper != nil && config.Skipper(c) { - return next(c) - } - - auth, err := extractor(c) - if err != nil { - return echo.NewHTTPError(401, err.Error()) - } - - parser := config.KeySource.UCANParser() - token, err := parser.ParseAndVerify(c.Request().Context(), auth) - if err != nil { - return echo.NewHTTPError(401, "invalid UCAN token") - } - - // Store token in context - c.Set("ucan", token) - return next(c) - } - } -} - -// tokenFromHeader extracts token from header -func tokenFromHeader(header string, authScheme string) func(echo.Context) (string, error) { - return func(c echo.Context) (string, error) { - auth := c.Request().Header.Get(header) - if auth == "" { - return "", fmt.Errorf("missing auth token") - } - if authScheme == "" { - return auth, nil - } - l := len(authScheme) - if len(auth) > l+1 && auth[:l] == authScheme { - return auth[l+1:], nil - } - return "", fmt.Errorf("invalid auth scheme") - } -} - -// tokenFromQuery extracts token from query string -func tokenFromQuery(param string) func(echo.Context) (string, error) { - return func(c echo.Context) (string, error) { - token := c.QueryParam(param) - if token == "" { - return "", fmt.Errorf("missing auth token") - } - return token, nil - } -} - -// tokenFromParam extracts token from url param -func tokenFromParam(param string) func(echo.Context) (string, error) { - return func(c echo.Context) (string, error) { - token := c.Param(param) - if token == "" { - return "", fmt.Errorf("missing auth token") - } - return token, nil - } -} - -// tokenFromCookie extracts token from cookie -func tokenFromCookie(name string) func(echo.Context) (string, error) { - return func(c echo.Context) (string, error) { - cookie, err := c.Cookie(name) - if err != nil { - return "", fmt.Errorf("missing auth token") - } - return cookie.Value, nil - } -} diff --git a/pkg/didauth/exports.go b/pkg/didauth/exports.go deleted file mode 100644 index 4789df4ca..000000000 --- a/pkg/didauth/exports.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package didauth provides middleware and utilities for DID-based authentication -package didauth diff --git a/pkg/didauth/producer/context.go b/pkg/didauth/producer/context.go deleted file mode 100644 index ba7311c1d..000000000 --- a/pkg/didauth/producer/context.go +++ /dev/null @@ -1,19 +0,0 @@ -package producer - -import ( - "github.com/labstack/echo/v4" - "github.com/onsonr/sonr/crypto/ucan" - "github.com/onsonr/sonr/pkg/ipfsapi" -) - -type ProducerContext struct { - echo.Context - // TokenParser is the attentuations assigned to the producer service - TokenParser *ucan.TokenParser - - // TokenStore is the token store used to store and retrieve tokens - TokenStore ipfsapi.IPFSTokenStore - - // IPFSClient is the IPFS client used to resolve the UCAN - IPFSClient ipfsapi.Client -} diff --git a/pkg/didauth/producer/middleware.go b/pkg/didauth/producer/middleware.go deleted file mode 100644 index 82f59eb92..000000000 --- a/pkg/didauth/producer/middleware.go +++ /dev/null @@ -1,37 +0,0 @@ -package producer - -import ( - "github.com/onsonr/sonr/crypto/mpc" - "github.com/onsonr/sonr/crypto/ucan" - "github.com/onsonr/sonr/pkg/ipfsapi" - - "github.com/labstack/echo/v4" -) - -// Middleware returns middleware to spawn controllers and validate UCAN tokens -func Middleware(ipc ipfsapi.Client, perms ucan.Permissions) echo.MiddlewareFunc { - // Setup token store and parser - store := ipfsapi.NewUCANStore(ipc) - parser := ucan.NewTokenParser(perms.GetConstructor(), store, store) - - // Return middleware - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - ctx := ProducerContext{ - Context: c, - IPFSClient: ipc, - TokenParser: parser, - TokenStore: store, - } - return next(ctx) - } - } -} - -func NewKeyset(c echo.Context) (mpc.Enclave, error) { - ks, err := mpc.GenEnclave() - if err != nil { - return nil, err - } - return ks, nil -} diff --git a/pkg/didauth/resolver/conn.go b/pkg/didauth/resolver/conn.go deleted file mode 100644 index 067bc3d90..000000000 --- a/pkg/didauth/resolver/conn.go +++ /dev/null @@ -1,35 +0,0 @@ -package resolver - -import ( - "net/http" - - "github.com/labstack/echo/v4" - config "github.com/onsonr/sonr/pkg/config/hway" - "google.golang.org/grpc" -) - -type ClientsContext struct { - echo.Context - addr string -} - -func GetClientConn(c echo.Context) (*grpc.ClientConn, error) { - cc, ok := c.(*ClientsContext) - if !ok { - return nil, echo.NewHTTPError(http.StatusInternalServerError, "ClientsContext not found") - } - grpcConn, err := grpc.NewClient(cc.addr, grpc.WithInsecure()) - if err != nil { - return nil, echo.NewHTTPError(http.StatusInternalServerError, "Failed to dial gRPC") - } - return grpcConn, nil -} - -func Middleware(env config.Hway) echo.MiddlewareFunc { - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - cc := &ClientsContext{Context: c, addr: env.GetSonrGrpcUrl()} - return next(cc) - } - } -} diff --git a/pkg/didauth/resolver/grpc.go b/pkg/didauth/resolver/grpc.go deleted file mode 100644 index ab743f42c..000000000 --- a/pkg/didauth/resolver/grpc.go +++ /dev/null @@ -1,41 +0,0 @@ -package resolver - -import ( - bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1" - "github.com/labstack/echo/v4" - didv1 "github.com/onsonr/sonr/api/did/v1" - dwnv1 "github.com/onsonr/sonr/api/dwn/v1" - svcv1 "github.com/onsonr/sonr/api/svc/v1" -) - -func BankQueryClient(c echo.Context) (bankv1beta1.QueryClient, error) { - conn, err := GetClientConn(c) - if err != nil { - return nil, err - } - return bankv1beta1.NewQueryClient(conn), nil -} - -func DIDQueryClient(c echo.Context) (didv1.QueryClient, error) { - conn, err := GetClientConn(c) - if err != nil { - return nil, err - } - return didv1.NewQueryClient(conn), nil -} - -func DWNQueryClient(c echo.Context) (dwnv1.QueryClient, error) { - conn, err := GetClientConn(c) - if err != nil { - return nil, err - } - return dwnv1.NewQueryClient(conn), nil -} - -func SVCQueryClient(c echo.Context) (svcv1.QueryClient, error) { - conn, err := GetClientConn(c) - if err != nil { - return nil, err - } - return svcv1.NewQueryClient(conn), nil -} diff --git a/pkg/gateway/gateway.go b/pkg/gateway/gateway.go new file mode 100644 index 000000000..368ecbd40 --- /dev/null +++ b/pkg/gateway/gateway.go @@ -0,0 +1,45 @@ +// Package gateway provides the default routes for the Sonr hway. +package gateway + +import ( + "github.com/labstack/echo-contrib/echoprometheus" + "github.com/labstack/echo/v4" + echomiddleware "github.com/labstack/echo/v4/middleware" + config "github.com/onsonr/sonr/internal/config/hway" + "github.com/onsonr/sonr/internal/database" + "github.com/onsonr/sonr/pkg/common" + "github.com/onsonr/sonr/pkg/gateway/middleware" + "github.com/onsonr/sonr/pkg/gateway/routes" +) + +type Gateway = *echo.Echo + +// New returns a new Gateway instance +func New(env config.Hway, ipc common.IPFS) (Gateway, error) { + db, err := database.NewDB(env) + if err != nil { + return nil, err + } + e := echo.New() + // Override default behaviors + e.IPExtractor = echo.ExtractIPDirect() + e.HTTPErrorHandler = redirectOnError("http://localhost:3000") + + // Built-in middleware + e.Use(echoprometheus.NewMiddleware("hway")) + e.Use(echomiddleware.Logger()) + e.Use(echomiddleware.Recover()) + e.Use(middleware.UseGateway(env, ipc, db)) + routes.Register(e) + return e, nil +} + +func redirectOnError(target string) echo.HTTPErrorHandler { + return func(err error, c echo.Context) { + if he, ok := err.(*echo.HTTPError); ok { + // Log the error if needed + c.Logger().Errorf("Error: %v", he.Message) + middleware.RenderError(c, he) + } + } +} diff --git a/pkg/gateway/handlers/check_handler.go b/pkg/gateway/handlers/check_handler.go new file mode 100644 index 000000000..7f08efc7c --- /dev/null +++ b/pkg/gateway/handlers/check_handler.go @@ -0,0 +1,33 @@ +package handlers + +import ( + "github.com/labstack/echo/v4" + + "github.com/onsonr/sonr/pkg/gateway/middleware" + "github.com/onsonr/sonr/internal/nebula/input" +) + +// ValidateProfileHandle finds the chosen handle and verifies it is unique +func ValidateProfileHandle(c echo.Context) error { + handle := c.FormValue("handle") + // + // if ok { + // return middleware.Render(c, input.HandleError(handle)) + // } + // + return middleware.Render(c, input.HandleSuccess(handle)) +} + +// ValidateProfileHandle finds the chosen handle and verifies it is unique +func ValidateIsHumanSum(c echo.Context) error { + // data := context.GetCreateProfileData(c) + // value := c.FormValue("is_human") + // intValue, err := strconv.Atoi(value) + // if err != nil { + // return middleware.Render(c, input.HumanSliderError(data.FirstNumber, data.LastNumber)) + // } + // if intValue != data.Sum() { + // return middleware.Render(c, input.HumanSliderError(data.FirstNumber, data.LastNumber)) + // } + return middleware.Render(c, input.HumanSliderSuccess()) +} diff --git a/pkg/gateway/handlers/initial_handler.go b/pkg/gateway/handlers/initial_handler.go new file mode 100644 index 000000000..7087d670e --- /dev/null +++ b/pkg/gateway/handlers/initial_handler.go @@ -0,0 +1,16 @@ +package handlers + +import ( + "github.com/labstack/echo/v4" + "github.com/onsonr/sonr/pkg/gateway/middleware" +) + +func RenderIndex(c echo.Context) error { + // Initialize the session + err := middleware.NewSession(c) + if err != nil { + return middleware.RenderError(c, err) + } + // Render the initial view + return middleware.RenderInitial(c) +} diff --git a/pkg/gateway/handlers/register_handler.go b/pkg/gateway/handlers/register_handler.go new file mode 100644 index 000000000..ecf785efc --- /dev/null +++ b/pkg/gateway/handlers/register_handler.go @@ -0,0 +1,25 @@ +package handlers + +import ( + "github.com/labstack/echo/v4" + "github.com/onsonr/sonr/pkg/gateway/middleware" + "github.com/onsonr/sonr/pkg/gateway/types" + "github.com/onsonr/sonr/pkg/gateway/views" +) + +func RenderProfileCreate(c echo.Context) error { + // numF, numL := middleware.GetHumanVerificationNumbers(c) + params := types.CreateProfileParams{ + FirstNumber: int(middleware.CurrentBlock(c)), + LastNumber: int(middleware.CurrentBlock(c)), + } + return middleware.Render(c, views.RegisterProfileView(params)) +} + +func RenderPasskeyCreate(c echo.Context) error { + return middleware.Render(c, views.RegisterPasskeyView(types.CreatePasskeyParams{})) +} + +func RenderVaultLoading(c echo.Context) error { + return middleware.Render(c, views.LoadingView()) +} diff --git a/pkg/gateway/handlers/vault_handler.go b/pkg/gateway/handlers/vault_handler.go new file mode 100644 index 000000000..6a213f8a8 --- /dev/null +++ b/pkg/gateway/handlers/vault_handler.go @@ -0,0 +1,29 @@ +package handlers + +import ( + "encoding/json" + + "github.com/labstack/echo/v4" + "github.com/onsonr/sonr/pkg/gateway/types" + "github.com/onsonr/sonr/pkg/gateway/middleware" +) + +// SubmitProfileHandle submits a profile handle +func SubmitProfileHandle(c echo.Context) error { + return nil +} + +// SubmitPublicKeyCredential submits a public key credential +func SubmitPublicKeyCredential(c echo.Context) error { + credentialJSON := c.FormValue("credential") + cred := &types.CredentialDescriptor{} + // Unmarshal the credential JSON + if err := json.Unmarshal([]byte(credentialJSON), cred); err != nil { + return middleware.RenderError(c, err) + } + err := middleware.SubmitCredential(c, cred) + if err != nil { + return middleware.RenderError(c, err) + } + return nil +} diff --git a/pkg/gateway/middleware/credentials.go b/pkg/gateway/middleware/credentials.go new file mode 100644 index 000000000..a0fe355f2 --- /dev/null +++ b/pkg/gateway/middleware/credentials.go @@ -0,0 +1,44 @@ +package middleware + +import ( + "net/http" + + "github.com/labstack/echo/v4" + "github.com/onsonr/sonr/internal/database/repository" + "github.com/onsonr/sonr/pkg/gateway/types" +) + +func ListCredentials(c echo.Context, handle string) ([]*types.CredentialDescriptor, error) { + cc, ok := c.(*GatewayContext) + if !ok { + return nil, echo.NewHTTPError(http.StatusInternalServerError, "Credentials Context not found") + } + creds, err := cc.dbq.GetCredentialsByHandle(bgCtx(), handle) + if err != nil { + return nil, err + } + return types.CredentialArrayToDescriptors(creds), nil +} + +func SubmitCredential(c echo.Context, cred *types.CredentialDescriptor) error { + origin := GetOrigin(c) + handle := GetHandle(c) + md := cred.ToModel(handle, origin) + + cc, ok := c.(*GatewayContext) + if !ok { + return echo.NewHTTPError(http.StatusInternalServerError, "Credentials Context not found") + } + + _, err := cc.dbq.InsertCredential(bgCtx(), repository.InsertCredentialParams{ + Handle: handle, + CredentialID: md.CredentialID, + Origin: origin, + Type: md.Type, + Transports: md.Transports, + }) + if err != nil { + return err + } + return nil +} diff --git a/pkg/gateway/middleware/middleware.go b/pkg/gateway/middleware/middleware.go new file mode 100644 index 000000000..9e5b9acb5 --- /dev/null +++ b/pkg/gateway/middleware/middleware.go @@ -0,0 +1,40 @@ +package middleware + +import ( + "database/sql" + + "github.com/labstack/echo/v4" + "github.com/medama-io/go-useragent" + "github.com/onsonr/sonr/crypto/mpc" + "github.com/onsonr/sonr/internal/config/hway" + "github.com/onsonr/sonr/internal/database/repository" + "github.com/onsonr/sonr/pkg/common" +) + +type GatewayContext struct { + echo.Context + agent useragent.UserAgent + id string + dbq *repository.Queries + ipfsClient common.IPFS + tokenStore common.IPFSTokenStore + stagedEnclaves map[string]mpc.Enclave + grpcAddr string +} + +func UseGateway(env hway.Hway, ipc common.IPFS, db *sql.DB) echo.MiddlewareFunc { + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + ua := useragent.NewParser() + ctx := &GatewayContext{ + agent: ua.Parse(c.Request().UserAgent()), + Context: c, + dbq: repository.New(db), + ipfsClient: ipc, + grpcAddr: env.GetSonrGrpcUrl(), + tokenStore: common.NewUCANStore(ipc), + } + return next(ctx) + } + } +} diff --git a/pkg/gateway/middleware/profiles.go b/pkg/gateway/middleware/profiles.go new file mode 100644 index 000000000..43e0d8a1d --- /dev/null +++ b/pkg/gateway/middleware/profiles.go @@ -0,0 +1,100 @@ +package middleware + +import ( + "net/http" + + "github.com/labstack/echo/v4" + "github.com/onsonr/sonr/internal/context" + "github.com/onsonr/sonr/internal/database/repository" +) + +func CheckHandleUnique(c echo.Context, handle string) bool { + ctx, ok := c.(*GatewayContext) + if !ok { + return false + } + ok, err := ctx.dbq.CheckHandleExists(bgCtx(), handle) + if err != nil { + return false + } + if ok { + return false + } + context.WriteCookie(c, context.UserHandle, handle) + return true +} + +func CreateProfile(c echo.Context) (*repository.Profile, error) { + ctx, ok := c.(*GatewayContext) + if !ok { + return nil, echo.NewHTTPError(http.StatusInternalServerError, "Profile Context not found") + } + address := c.FormValue("address") + handle := c.FormValue("handle") + origin := c.FormValue("origin") + name := c.FormValue("name") + profile, err := ctx.dbq.InsertProfile(bgCtx(), repository.InsertProfileParams{ + Address: address, + Handle: handle, + Origin: origin, + Name: name, + }) + if err != nil { + return nil, err + } + // Update session with profile id + sid := GetSessionID(c) + _, err = ctx.dbq.UpdateSessionWithProfileID(bgCtx(), repository.UpdateSessionWithProfileIDParams{ + ProfileID: profile.ID, + ID: sid, + }) + if err != nil { + return nil, err + } + return &profile, nil +} + +func UpdateProfile(c echo.Context) (*repository.Profile, error) { + ctx, ok := c.(*GatewayContext) + if !ok { + return nil, echo.NewHTTPError(http.StatusInternalServerError, "Profile Context not found") + } + address := c.FormValue("address") + handle := c.FormValue("handle") + name := c.FormValue("name") + profile, err := ctx.dbq.UpdateProfile(bgCtx(), repository.UpdateProfileParams{ + Address: address, + Handle: handle, + Name: name, + }) + if err != nil { + return nil, err + } + return &profile, nil +} + +func ReadProfile(c echo.Context) (*repository.Profile, error) { + ctx, ok := c.(*GatewayContext) + if !ok { + return nil, echo.NewHTTPError(http.StatusInternalServerError, "Profile Context not found") + } + handle := c.Param("handle") + profile, err := ctx.dbq.GetProfileByHandle(bgCtx(), handle) + if err != nil { + return nil, err + } + return &profile, nil +} + +func DeleteProfile(c echo.Context) error { + ctx, ok := c.(*GatewayContext) + if !ok { + return echo.NewHTTPError(http.StatusInternalServerError, "Profile Context not found") + } + address := c.Param("address") + err := ctx.dbq.SoftDeleteProfile(bgCtx(), address) + if err != nil { + return err + } + return nil +} diff --git a/pkg/gateway/middleware/renderer.go b/pkg/gateway/middleware/renderer.go new file mode 100644 index 000000000..c0759c614 --- /dev/null +++ b/pkg/gateway/middleware/renderer.go @@ -0,0 +1,42 @@ +package middleware + +import ( + "bytes" + + "github.com/a-h/templ" + "github.com/labstack/echo/v4" + "github.com/onsonr/sonr/pkg/gateway/views" +) + +func Render(c echo.Context, cmp templ.Component) error { + // Create a buffer to store the rendered HTML + buf := &bytes.Buffer{} + // Render the component to the buffer + err := cmp.Render(c.Request().Context(), buf) + if err != nil { + return err + } + + // Set the content type + c.Response().Header().Set(echo.HeaderContentType, echo.MIMETextHTML) + + // Write the buffered content to the response + _, err = c.Response().Write(buf.Bytes()) + if err != nil { + return err + } + c.Response().WriteHeader(200) + return nil +} + +func RenderError(c echo.Context, err error) error { + return Render(c, views.ErrorView(err.Error())) +} + +func RenderInitial(c echo.Context) error { + return Render(c, views.InitialView()) +} + +func RenderLoading(c echo.Context) error { + return Render(c, views.LoadingView()) +} diff --git a/pkg/gateway/middleware/resolver.go b/pkg/gateway/middleware/resolver.go new file mode 100644 index 000000000..83b5a931a --- /dev/null +++ b/pkg/gateway/middleware/resolver.go @@ -0,0 +1,113 @@ +package middleware + +import ( + "errors" + + "github.com/labstack/echo/v4" + "github.com/onsonr/sonr/pkg/common" +) + +// CurrentBlock returns the current block +func CurrentBlock(c echo.Context) uint64 { + cc, ok := c.(*GatewayContext) + if !ok { + return 0 + } + qc, err := common.NewNodeClient(cc.grpcAddr) + if err != nil { + return 0 + } + resp, err := qc.Status(bgCtx(), &common.StatusRequest{}) + if err != nil { + return 0 + } + return resp.GetHeight() +} + +// GetBankParams returns the bank params +func GetBankParams(c echo.Context) (*common.BankParamsResponse, error) { + cc, ok := c.(*GatewayContext) + if !ok { + return nil, errors.New("gateway context not found") + } + cl, err := common.NewBankClient(cc.grpcAddr) + if err != nil { + return nil, err + } + resp, err := cl.Params(bgCtx(), &common.BankParamsRequest{}) + if err != nil { + return nil, err + } + return resp, nil +} + +// GetDIDParams returns the DID params +func GetDIDParams(c echo.Context) (*common.DIDParamsResponse, error) { + cc, ok := c.(*GatewayContext) + if !ok { + return nil, errors.New("gateway context not found") + } + cl, err := common.NewDIDClient(cc.grpcAddr) + if err != nil { + return nil, err + } + resp, err := cl.Params(bgCtx(), &common.DIDParamsRequest{}) + if err != nil { + return nil, err + } + return resp, nil +} + +// GetDWNParams returns the DWN params +func GetDWNParams(c echo.Context) (*common.DWNParamsResponse, error) { + cc, ok := c.(*GatewayContext) + if !ok { + return nil, errors.New("gateway context not found") + } + + cl, err := common.NewDWNClient(cc.grpcAddr) + if err != nil { + return nil, err + } + resp, err := cl.Params(bgCtx(), &common.DWNParamsRequest{}) + if err != nil { + return nil, err + } + return resp, nil +} + +// GetNodeStatus returns the node status +func GetNodeStatus(c echo.Context) (*common.StatusResponse, error) { + cc, ok := c.(*GatewayContext) + if !ok { + return nil, errors.New("gateway context not found") + } + + cl, err := common.NewNodeClient(cc.grpcAddr) + if err != nil { + return nil, err + } + resp, err := cl.Status(bgCtx(), &common.StatusRequest{}) + if err != nil { + return nil, err + } + return resp, nil +} + +// GetSVCParams returns the SVC params +func GetSVCParams(c echo.Context) (*common.SVCParamsResponse, error) { + cc, ok := c.(*GatewayContext) + if !ok { + return nil, errors.New("gateway context not found") + } + + cl, err := common.NewSVCClient(cc.grpcAddr) + if err != nil { + return nil, err + } + resp, err := cl.Params(bgCtx(), &common.SVCParamsRequest{}) + if err != nil { + return nil, err + } + return resp, nil +} diff --git a/pkg/gateway/middleware/sessions.go b/pkg/gateway/middleware/sessions.go new file mode 100644 index 000000000..5ff3af37e --- /dev/null +++ b/pkg/gateway/middleware/sessions.go @@ -0,0 +1,109 @@ +package middleware + +import ( + gocontext "context" + + "github.com/labstack/echo/v4" + "github.com/onsonr/sonr/internal/context" + "github.com/onsonr/sonr/internal/database" +) + +func NewSession(c echo.Context) error { + cc, ok := c.(*GatewayContext) + if !ok { + return nil + } + baseSessionCreateParams := database.BaseSessionCreateParams(cc) + cc.id = baseSessionCreateParams.ID + if _, err := cc.dbq.CreateSession(bgCtx(), baseSessionCreateParams); err != nil { + return err + } + // Set Cookie + if err := context.WriteCookie(c, context.SessionID, cc.id); err != nil { + return err + } + return nil +} + +// ForbiddenDevice returns true if the device is unavailable +func ForbiddenDevice(c echo.Context) bool { + cc, ok := c.(*GatewayContext) + if !ok { + return true + } + return cc.agent.IsBot() || cc.agent.IsTV() +} + +func GetOrigin(c echo.Context) string { + return c.Request().Host +} + +func GetSessionID(c echo.Context) string { + // Check from context + cc, ok := c.(*GatewayContext) + if !ok { + return "" + } + // check from cookie + if cc.id == "" { + if ok := context.CookieExists(c, context.SessionID); !ok { + return "" + } + cc.id = context.ReadCookieUnsafe(c, context.SessionID) + } + return cc.id +} + +func GetSessionChallenge(c echo.Context) string { + cc, ok := c.(*GatewayContext) + if !ok { + return "" + } + s, err := cc.dbq.GetChallengeBySessionID(bgCtx(), cc.id) + if err != nil { + return "" + } + return s +} + +func GetHandle(c echo.Context) string { + // First check for the cookie + handle := context.ReadCookieUnsafe(c, context.UserHandle) + if handle != "" { + return handle + } + + // Then check the session + cc, ok := c.(*GatewayContext) + if !ok { + return "" + } + s, err := cc.dbq.GetSessionByID(bgCtx(), cc.id) + if err != nil { + return "" + } + profile, err := cc.dbq.GetProfileByID(bgCtx(), s.ProfileID) + if err != nil { + return "" + } + return profile.Handle +} + +// +// func GetHumanVerificationNumbers(c echo.Context) (int64, int64) { +// cc, ok := c.(*GatewayContext) +// if !ok { +// return 0, 0 +// } +// s, err := cc.dbq.GetHumanVerificationNumbers(bgCtx(), cc.id) +// if err != nil { +// return 0, 0 +// } +// return s.IsHumanFirst, s.IsHumanLast +// } + +// utility function to get a context +func bgCtx() gocontext.Context { + ctx := gocontext.Background() + return ctx +} diff --git a/pkg/gateway/middleware/vaults.go b/pkg/gateway/middleware/vaults.go new file mode 100644 index 000000000..2dfa675f0 --- /dev/null +++ b/pkg/gateway/middleware/vaults.go @@ -0,0 +1,57 @@ +package middleware + +import ( + "fmt" + + "github.com/labstack/echo/v4" + "github.com/onsonr/sonr/crypto/mpc" + "github.com/onsonr/sonr/internal/context" + "github.com/onsonr/sonr/pkg/gateway/types" + "lukechampine.com/blake3" +) + +func Spawn(c echo.Context) (types.CreatePasskeyParams, error) { + cc := c.(*GatewayContext) + block := fmt.Sprintf("%d", CurrentBlock(c)) + handle := GetHandle(c) + origin := GetOrigin(c) + challenge := GetSessionChallenge(c) + sid := GetSessionID(c) + nonce, err := calcNonce(sid) + if err != nil { + return types.DefaultCreatePasskeyParams(), err + } + encl, err := mpc.GenEnclave(nonce) + if err != nil { + return types.DefaultCreatePasskeyParams(), err + } + cc.stagedEnclaves[sid] = encl + context.WriteCookie(c, context.SonrAddress, encl.Address()) + return types.CreatePasskeyParams{ + Address: encl.Address(), + Handle: handle, + Name: origin, + Challenge: challenge, + CreationBlock: block, + }, nil +} + +func Claim() (types.CreatePasskeyParams, error) { + return types.CreatePasskeyParams{}, nil +} + +// Uses blake3 to hash the sessionID to generate a nonce of length 12 bytes +func calcNonce(sessionID string) ([]byte, error) { + hash := blake3.New(32, nil) + _, err := hash.Write([]byte(sessionID)) + if err != nil { + return nil, err + } + // Read the hash into a byte slice + nonce := make([]byte, 12) + _, err = hash.Write(nonce) + if err != nil { + return nil, err + } + return nonce, nil +} diff --git a/pkg/gateway/routes/routes.go b/pkg/gateway/routes/routes.go new file mode 100644 index 000000000..2cbf7092e --- /dev/null +++ b/pkg/gateway/routes/routes.go @@ -0,0 +1,21 @@ +package routes + +import ( + "github.com/labstack/echo/v4" + "github.com/onsonr/sonr/pkg/gateway/handlers" +) + +func Register(e *echo.Echo) error { + // Register View Handlers + e.GET("/", handlers.RenderIndex) + e.GET("/register", handlers.RenderProfileCreate) + e.POST("/register/passkey", handlers.RenderPasskeyCreate) + e.POST("/register/finish", handlers.RenderVaultLoading) + + // Register Validation Handlers + e.POST("/register/profile/handle", handlers.ValidateProfileHandle) + e.POST("/register/profile/is_human", handlers.ValidateIsHumanSum) + e.POST("/submit/profile/handle", handlers.SubmitProfileHandle) + e.POST("/submit/credential", handlers.SubmitPublicKeyCredential) + return nil +} diff --git a/pkg/config/embed/fs.go b/pkg/gateway/types/embed/codec.go similarity index 68% rename from pkg/config/embed/fs.go rename to pkg/gateway/types/embed/codec.go index 03256003f..16b2612d1 100644 --- a/pkg/config/embed/fs.go +++ b/pkg/gateway/types/embed/codec.go @@ -4,7 +4,8 @@ import ( "encoding/json" "github.com/ipfs/boxo/files" - config "github.com/onsonr/sonr/pkg/config/motr" + config "github.com/onsonr/sonr/internal/config/motr" + "github.com/onsonr/sonr/internal/models" ) const SchemaVersion = 1 @@ -18,7 +19,7 @@ const ( // spawnVaultDirectory creates a new directory with the default files func NewVaultFS(cfg *config.Config) (files.Directory, error) { - manifestBz, err := newWebManifestBytes() + manifestBz, err := models.NewWebManifest() if err != nil { return nil, err } @@ -47,3 +48,17 @@ func NewVaultConfig(addr string, ucanCID string) *config.Config { VaultSchema: DefaultSchema(), } } + +// DefaultSchema returns the default schema +func DefaultSchema() *config.Schema { + return &config.Schema{ + Version: SchemaVersion, + Account: getSchema(&models.Account{}), + Asset: getSchema(&models.Asset{}), + Chain: getSchema(&models.Chain{}), + Credential: getSchema(&models.Credential{}), + Grant: getSchema(&models.Grant{}), + Keyshare: getSchema(&models.Keyshare{}), + Profile: getSchema(&models.Profile{}), + } +} diff --git a/pkg/config/embed/index.html b/pkg/gateway/types/embed/index.html similarity index 100% rename from pkg/config/embed/index.html rename to pkg/gateway/types/embed/index.html diff --git a/pkg/config/embed/main.js b/pkg/gateway/types/embed/main.js similarity index 100% rename from pkg/config/embed/main.js rename to pkg/gateway/types/embed/main.js diff --git a/pkg/config/embed/sw.js b/pkg/gateway/types/embed/sw.js similarity index 100% rename from pkg/config/embed/sw.js rename to pkg/gateway/types/embed/sw.js diff --git a/pkg/config/embed/schema.go b/pkg/gateway/types/embed/utils.go similarity index 54% rename from pkg/config/embed/schema.go rename to pkg/gateway/types/embed/utils.go index 2697018fe..fac01a126 100644 --- a/pkg/config/embed/schema.go +++ b/pkg/gateway/types/embed/utils.go @@ -1,26 +1,19 @@ package embed import ( + _ "embed" "reflect" "strings" - - "github.com/onsonr/sonr/pkg/common/models" - config "github.com/onsonr/sonr/pkg/config/motr" ) -// DefaultSchema returns the default schema -func DefaultSchema() *config.Schema { - return &config.Schema{ - Version: SchemaVersion, - Account: getSchema(&models.Account{}), - Asset: getSchema(&models.Asset{}), - Chain: getSchema(&models.Chain{}), - Credential: getSchema(&models.Credential{}), - Grant: getSchema(&models.Grant{}), - Keyshare: getSchema(&models.Keyshare{}), - Profile: getSchema(&models.Profile{}), - } -} +//go:embed index.html +var IndexHTML []byte + +//go:embed main.js +var MainJS []byte + +//go:embed sw.js +var WorkerJS []byte func getSchema(structType interface{}) string { t := reflect.TypeOf(structType) diff --git a/pkg/gateway/types/forms.go b/pkg/gateway/types/forms.go new file mode 100644 index 000000000..63a3c0363 --- /dev/null +++ b/pkg/gateway/types/forms.go @@ -0,0 +1,50 @@ +package types + +// ╭───────────────────────────────────────────────────────────╮ +// │ Create Passkey (/register/passkey) │ +// ╰───────────────────────────────────────────────────────────╯ + +// DefaultCreatePasskeyParams returns a default CreatePasskeyParams +func DefaultCreatePasskeyParams() CreatePasskeyParams { + return CreatePasskeyParams{ + Address: "", + Handle: "", + Name: "", + Challenge: "", + CreationBlock: "", + } +} + +// CreatePasskeyParams represents the parameters for creating a passkey +type CreatePasskeyParams struct { + Address string + Handle string + Name string + Challenge string + CreationBlock string +} + +// ╭───────────────────────────────────────────────────────────╮ +// │ Create Profile (/register/profile) │ +// ╰───────────────────────────────────────────────────────────╯ + +// DefaultCreateProfileParams returns a default CreateProfileParams +func DefaultCreateProfileParams() CreateProfileParams { + return CreateProfileParams{ + TurnstileSiteKey: "", + FirstNumber: 0, + LastNumber: 0, + } +} + +// CreateProfileParams represents the parameters for creating a profile +type CreateProfileParams struct { + TurnstileSiteKey string + FirstNumber int + LastNumber int +} + +// Sum returns the sum of the first and last number +func (d CreateProfileParams) Sum() int { + return d.FirstNumber + d.LastNumber +} diff --git a/pkg/gateway/types/webauth.go b/pkg/gateway/types/webauth.go new file mode 100644 index 000000000..17e084dea --- /dev/null +++ b/pkg/gateway/types/webauth.go @@ -0,0 +1,43 @@ +package types + +import "github.com/onsonr/sonr/internal/database/repository" + +// Define the credential structure matching our frontend data +type CredentialDescriptor struct { + ID string `json:"id"` + RawID string `json:"rawId"` + Type string `json:"type"` + AuthenticatorAttachment string `json:"authenticatorAttachment"` + Transports string `json:"transports"` + ClientExtensionResults map[string]string `json:"clientExtensionResults"` + Response struct { + AttestationObject string `json:"attestationObject"` + ClientDataJSON string `json:"clientDataJSON"` + } `json:"response"` +} + +func (c *CredentialDescriptor) ToModel(handle, origin string) *repository.Credential { + return &repository.Credential{ + Handle: handle, + Origin: origin, + CredentialID: c.ID, + Type: c.Type, + Transports: c.Transports, + AuthenticatorAttachment: c.AuthenticatorAttachment, + } +} + +func CredentialArrayToDescriptors(credentials []repository.Credential) []*CredentialDescriptor { + var descriptors []*CredentialDescriptor + for _, cred := range credentials { + cd := &CredentialDescriptor{ + ID: cred.CredentialID, + RawID: cred.CredentialID, + Type: cred.Type, + AuthenticatorAttachment: cred.AuthenticatorAttachment, + Transports: cred.Transports, + } + descriptors = append(descriptors, cd) + } + return descriptors +} diff --git a/pkg/gateway/views/error.templ b/pkg/gateway/views/error.templ new file mode 100644 index 000000000..f04cc2a6c --- /dev/null +++ b/pkg/gateway/views/error.templ @@ -0,0 +1,19 @@ +package views + +import ( + "github.com/onsonr/sonr/internal/nebula/hero" + "github.com/onsonr/sonr/internal/nebula/layout" +) + +templ ErrorView(err string) { + @layout.View("Error | Sonr.ID") { + @layout.Container() { + @hero.TitleDesc("Uh oh!", "Something went wrong.") +
+

+ { err } +

+
+ } + } +} diff --git a/pkg/gateway/views/error_templ.go b/pkg/gateway/views/error_templ.go new file mode 100644 index 000000000..5ccc817d4 --- /dev/null +++ b/pkg/gateway/views/error_templ.go @@ -0,0 +1,98 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.2.793 +package views + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" + +import ( + "github.com/onsonr/sonr/internal/nebula/hero" + "github.com/onsonr/sonr/internal/nebula/layout" +) + +func ErrorView(err string) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var1 := templ.GetChildren(ctx) + if templ_7745c5c3_Var1 == nil { + templ_7745c5c3_Var1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var3 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Err = hero.TitleDesc("Uh oh!", "Something went wrong.").Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var4 string + templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(err) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/gateway/views/error.templ`, Line: 14, Col: 10} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = layout.Container().Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = layout.View("Error | Sonr.ID").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +var _ = templruntime.GeneratedTemplate diff --git a/internal/gateway/views/initial_view.templ b/pkg/gateway/views/initial.templ similarity index 77% rename from internal/gateway/views/initial_view.templ rename to pkg/gateway/views/initial.templ index d16d74d85..afba155ca 100644 --- a/internal/gateway/views/initial_view.templ +++ b/pkg/gateway/views/initial.templ @@ -5,15 +5,7 @@ import ( "github.com/onsonr/sonr/internal/nebula/layout" ) -templ InitialView(isUnavailable bool) { - if isUnavailable { - @layout.View("Sonr.ID") { - @layout.Container() { - @hero.TitleDesc("Sonr.ID", "The decentralized identity layer for the web.") - @hero.SocialButtonsRow() - } - } - } +templ InitialView() { @layout.View("Sonr.ID") { @layout.Container() { @hero.TitleDesc("Sonr.ID", "The decentralized identity layer for the web.") diff --git a/internal/gateway/views/initial_view_templ.go b/pkg/gateway/views/initial_templ.go similarity index 68% rename from internal/gateway/views/initial_view_templ.go rename to pkg/gateway/views/initial_templ.go index 019f002b3..693178bf4 100644 --- a/internal/gateway/views/initial_view_templ.go +++ b/pkg/gateway/views/initial_templ.go @@ -13,7 +13,7 @@ import ( "github.com/onsonr/sonr/internal/nebula/layout" ) -func InitialView(isUnavailable bool) templ.Component { +func InitialView() templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { @@ -34,57 +34,7 @@ func InitialView(isUnavailable bool) templ.Component { templ_7745c5c3_Var1 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - if isUnavailable { - templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var3 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = hero.TitleDesc("Sonr.ID", "The decentralized identity layer for the web.").Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = hero.SocialButtonsRow().Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) - templ_7745c5c3_Err = layout.Container().Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return templ_7745c5c3_Err - }) - templ_7745c5c3_Err = layout.View("Sonr.ID").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - templ_7745c5c3_Var4 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) if !templ_7745c5c3_IsBuffer { @@ -96,7 +46,7 @@ func InitialView(isUnavailable bool) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var5 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_Var3 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) if !templ_7745c5c3_IsBuffer { @@ -130,13 +80,13 @@ func InitialView(isUnavailable bool) templ.Component { } return templ_7745c5c3_Err }) - templ_7745c5c3_Err = layout.Container().Render(templ.WithChildren(ctx, templ_7745c5c3_Var5), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = layout.Container().Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } return templ_7745c5c3_Err }) - templ_7745c5c3_Err = layout.View("Sonr.ID").Render(templ.WithChildren(ctx, templ_7745c5c3_Var4), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = layout.View("Sonr.ID").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -160,12 +110,12 @@ func ReturningView() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var6 := templ.GetChildren(ctx) - if templ_7745c5c3_Var6 == nil { - templ_7745c5c3_Var6 = templ.NopComponent + templ_7745c5c3_Var4 := templ.GetChildren(ctx) + if templ_7745c5c3_Var4 == nil { + templ_7745c5c3_Var4 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Var7 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_Var5 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) if !templ_7745c5c3_IsBuffer { @@ -177,7 +127,7 @@ func ReturningView() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var8 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_Var6 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) if !templ_7745c5c3_IsBuffer { @@ -199,13 +149,13 @@ func ReturningView() templ.Component { } return templ_7745c5c3_Err }) - templ_7745c5c3_Err = layout.Container().Render(templ.WithChildren(ctx, templ_7745c5c3_Var8), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = layout.Container().Render(templ.WithChildren(ctx, templ_7745c5c3_Var6), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } return templ_7745c5c3_Err }) - templ_7745c5c3_Err = layout.View("Login | Sonr.ID").Render(templ.WithChildren(ctx, templ_7745c5c3_Var7), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = layout.View("Login | Sonr.ID").Render(templ.WithChildren(ctx, templ_7745c5c3_Var5), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/pkg/gateway/views/loading.templ b/pkg/gateway/views/loading.templ new file mode 100644 index 000000000..7f5463751 --- /dev/null +++ b/pkg/gateway/views/loading.templ @@ -0,0 +1,14 @@ +package views + +import ( + "github.com/onsonr/sonr/internal/nebula/hero" + "github.com/onsonr/sonr/internal/nebula/layout" +) + +templ LoadingView() { + @layout.View("Loading... | Sonr.ID") { + @layout.Container() { + @hero.TitleDesc("Loading Vault", "This will be used to login to your vault.") + } + } +} diff --git a/pkg/gateway/views/loading_templ.go b/pkg/gateway/views/loading_templ.go new file mode 100644 index 000000000..60a68b096 --- /dev/null +++ b/pkg/gateway/views/loading_templ.go @@ -0,0 +1,81 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.2.793 +package views + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" + +import ( + "github.com/onsonr/sonr/internal/nebula/hero" + "github.com/onsonr/sonr/internal/nebula/layout" +) + +func LoadingView() templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var1 := templ.GetChildren(ctx) + if templ_7745c5c3_Var1 == nil { + templ_7745c5c3_Var1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var3 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Err = hero.TitleDesc("Loading Vault", "This will be used to login to your vault.").Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = layout.Container().Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = layout.View("Loading... | Sonr.ID").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +var _ = templruntime.GeneratedTemplate diff --git a/pkg/gateway/views/register.templ b/pkg/gateway/views/register.templ new file mode 100644 index 000000000..56ff2228f --- /dev/null +++ b/pkg/gateway/views/register.templ @@ -0,0 +1,56 @@ +package views + +import ( + "github.com/onsonr/sonr/pkg/gateway/types" + "github.com/onsonr/sonr/internal/nebula/card" + "github.com/onsonr/sonr/internal/nebula/form" + "github.com/onsonr/sonr/internal/nebula/hero" + "github.com/onsonr/sonr/internal/nebula/input" + "github.com/onsonr/sonr/internal/nebula/layout" +) + +templ RegisterProfileView(data types.CreateProfileParams) { + @layout.View("New Profile | Sonr.ID") { + @layout.Container() { + @hero.TitleDesc("Basic Info", "Tell us a little about yourself.") + @form.Root("/register/passkey", "create-profile") { + @form.Body() { + @form.Header() { +
+ + +
+ } + @input.Handle() + @input.Name() + @input.HumanSlider(data.FirstNumber, data.LastNumber) + @form.Footer() { + @form.CancelButton() + @form.SubmitButton("Next") + } + } + } + } + } +} + +templ RegisterPasskeyView(data types.CreatePasskeyParams) { + @layout.View("Register | Sonr.ID") { + @layout.Container() { + @hero.TitleDesc("Link a PassKey", "This will be used to login to your vault.") + @form.Root("/register/finish", "passkey-form") { + + @form.Body() { + @form.Header() { + @card.SonrProfile(data.Address, data.Name, data.Handle, data.CreationBlock) + } + @input.CoinSelect() + @form.Footer() { + @input.Passkey(data.Address, data.Handle, data.Challenge) + @form.CancelButton() + } + } + } + } + } +} diff --git a/pkg/gateway/views/register_templ.go b/pkg/gateway/views/register_templ.go new file mode 100644 index 000000000..6e63dc5d0 --- /dev/null +++ b/pkg/gateway/views/register_templ.go @@ -0,0 +1,378 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.2.793 +package views + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" + +import ( + "github.com/onsonr/sonr/internal/nebula/card" + "github.com/onsonr/sonr/internal/nebula/form" + "github.com/onsonr/sonr/internal/nebula/hero" + "github.com/onsonr/sonr/internal/nebula/input" + "github.com/onsonr/sonr/internal/nebula/layout" + "github.com/onsonr/sonr/pkg/gateway/types" +) + +func RegisterProfileView(data types.CreateProfileParams) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var1 := templ.GetChildren(ctx) + if templ_7745c5c3_Var1 == nil { + templ_7745c5c3_Var1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var3 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Err = hero.TitleDesc("Basic Info", "Tell us a little about yourself.").Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Var4 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var5 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var6 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = form.Header().Render(templ.WithChildren(ctx, templ_7745c5c3_Var6), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = input.Handle().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = input.Name().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = input.HumanSlider(data.FirstNumber, data.LastNumber).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Var7 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Err = form.CancelButton().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = form.SubmitButton("Next").Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = form.Footer().Render(templ.WithChildren(ctx, templ_7745c5c3_Var7), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = form.Body().Render(templ.WithChildren(ctx, templ_7745c5c3_Var5), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = form.Root("/register/passkey", "create-profile").Render(templ.WithChildren(ctx, templ_7745c5c3_Var4), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = layout.Container().Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = layout.View("New Profile | Sonr.ID").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +func RegisterPasskeyView(data types.CreatePasskeyParams) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var8 := templ.GetChildren(ctx) + if templ_7745c5c3_Var8 == nil { + templ_7745c5c3_Var8 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Var9 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var10 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Err = hero.TitleDesc("Link a PassKey", "This will be used to login to your vault.").Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Var11 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Var12 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var13 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Err = card.SonrProfile(data.Address, data.Name, data.Handle, data.CreationBlock).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = form.Header().Render(templ.WithChildren(ctx, templ_7745c5c3_Var13), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = input.CoinSelect().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Var14 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Err = input.Passkey(data.Address, data.Handle, data.Challenge).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = form.CancelButton().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = form.Footer().Render(templ.WithChildren(ctx, templ_7745c5c3_Var14), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = form.Body().Render(templ.WithChildren(ctx, templ_7745c5c3_Var12), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = form.Root("/register/finish", "passkey-form").Render(templ.WithChildren(ctx, templ_7745c5c3_Var11), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = layout.Container().Render(templ.WithChildren(ctx, templ_7745c5c3_Var10), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = layout.View("Register | Sonr.ID").Render(templ.WithChildren(ctx, templ_7745c5c3_Var9), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +var _ = templruntime.GeneratedTemplate diff --git a/pkg/ipfsapi/iface.go b/pkg/ipfsapi/iface.go deleted file mode 100644 index 63f4c0ef1..000000000 --- a/pkg/ipfsapi/iface.go +++ /dev/null @@ -1,29 +0,0 @@ -package ipfsapi - -import "github.com/ipfs/boxo/files" - -type Client interface { - Add(data []byte) (string, error) - AddFile(file File) (string, error) - AddFolder(folder Folder) (string, error) - Get(cid string) ([]byte, error) - IsPublished(ipns string) (bool, error) - Exists(cid string) (bool, error) - Pin(cid string) error - Unpin(cid string) error - Publish(cid string, name string) (string, error) - Ls(cid string) ([]string, error) -} - -type File interface { - files.File - Name() string -} - -func convertFilesToMap(vs []File) map[string]files.Node { - m := make(map[string]files.Node) - for _, f := range vs { - m[f.Name()] = f - } - return m -} diff --git a/internal/vault/handlers/authz.go b/pkg/vault/handlers/authz.go similarity index 100% rename from internal/vault/handlers/authz.go rename to pkg/vault/handlers/authz.go diff --git a/internal/vault/handlers/auth.go b/pkg/vault/handlers/controller.go similarity index 100% rename from internal/vault/handlers/auth.go rename to pkg/vault/handlers/controller.go diff --git a/internal/vault/handlers/feeds.go b/pkg/vault/handlers/current.go similarity index 75% rename from internal/vault/handlers/feeds.go rename to pkg/vault/handlers/current.go index 7f4621b14..609bbb780 100644 --- a/internal/vault/handlers/feeds.go +++ b/pkg/vault/handlers/current.go @@ -6,6 +6,6 @@ import ( "github.com/labstack/echo/v4" ) -func RenderFeeds(c echo.Context) error { +func RenderIndex(c echo.Context) error { return c.Render(http.StatusOK, "index.templ", nil) } diff --git a/internal/vault/handlers/wallet.go b/pkg/vault/handlers/payment.go similarity index 100% rename from internal/vault/handlers/wallet.go rename to pkg/vault/handlers/payment.go diff --git a/internal/vault/handlers/profile.go b/pkg/vault/handlers/profile.go similarity index 100% rename from internal/vault/handlers/profile.go rename to pkg/vault/handlers/profile.go diff --git a/internal/vault/handlers/search.go b/pkg/vault/handlers/redirect.go similarity index 100% rename from internal/vault/handlers/search.go rename to pkg/vault/handlers/redirect.go diff --git a/pkg/vault/handlers/search.go b/pkg/vault/handlers/search.go new file mode 100644 index 000000000..5ac8282f4 --- /dev/null +++ b/pkg/vault/handlers/search.go @@ -0,0 +1 @@ +package handlers diff --git a/internal/vault/routes.go b/pkg/vault/routes/routes.go similarity index 71% rename from internal/vault/routes.go rename to pkg/vault/routes/routes.go index 30467938e..b5f14f341 100644 --- a/internal/vault/routes.go +++ b/pkg/vault/routes/routes.go @@ -7,11 +7,9 @@ package vault import ( "github.com/labstack/echo/v4" - "github.com/onsonr/sonr/internal/vault/context" - "github.com/onsonr/sonr/pkg/config/motr" + "github.com/onsonr/sonr/internal/config/motr" ) // RegisterRoutes registers the Decentralized Web Node API routes. func RegisterRoutes(e *echo.Echo, config *motr.Config) { - e.Use(context.Middleware(config)) } diff --git a/pkg/vault/vault.go b/pkg/vault/vault.go new file mode 100644 index 000000000..f7e34c8a3 --- /dev/null +++ b/pkg/vault/vault.go @@ -0,0 +1 @@ +package vault diff --git a/pkg/vault/views/initial.templ b/pkg/vault/views/initial.templ new file mode 100644 index 000000000..729477f3d --- /dev/null +++ b/pkg/vault/views/initial.templ @@ -0,0 +1,16 @@ +package views + +import ( + "github.com/onsonr/sonr/internal/nebula/hero" + "github.com/onsonr/sonr/internal/nebula/layout" +) + +templ InitialView() { + @layout.View("Sonr.ID") { + @layout.Container() { + @hero.TitleDesc("Sonr.ID", "The decentralized identity layer for the web.") + @hero.StartButton() + @hero.SocialButtonsRow() + } + } +} diff --git a/pkg/vault/views/initial_templ.go b/pkg/vault/views/initial_templ.go new file mode 100644 index 000000000..654167ee4 --- /dev/null +++ b/pkg/vault/views/initial_templ.go @@ -0,0 +1,97 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.2.793 +package views + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" + +import ( + "github.com/onsonr/sonr/internal/nebula/hero" + "github.com/onsonr/sonr/internal/nebula/layout" +) + +func InitialView() templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var1 := templ.GetChildren(ctx) + if templ_7745c5c3_Var1 == nil { + templ_7745c5c3_Var1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var3 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Err = hero.TitleDesc("Sonr.ID", "The decentralized identity layer for the web.").Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = hero.StartButton().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = hero.SocialButtonsRow().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = layout.Container().Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = layout.View("Sonr.ID").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +var _ = templruntime.GeneratedTemplate diff --git a/pkl/sonr.net/Hway.pkl b/pkl/sonr.net/Hway.pkl index a0a70b4a0..707c8eca7 100644 --- a/pkl/sonr.net/Hway.pkl +++ b/pkl/sonr.net/Hway.pkl @@ -1,4 +1,4 @@ -@go.Package { name = "github.com/onsonr/sonr/pkg/config/hway" } +@go.Package { name = "github.com/onsonr/sonr/internal/config/hway" } open module sonr.net.Hway diff --git a/pkl/sonr.net/Motr.pkl b/pkl/sonr.net/Motr.pkl index 2239e3e62..53b52ffee 100644 --- a/pkl/sonr.net/Motr.pkl +++ b/pkl/sonr.net/Motr.pkl @@ -1,4 +1,4 @@ -@go.Package { name = "github.com/onsonr/sonr/pkg/config/motr" } +@go.Package { name = "github.com/onsonr/sonr/internal/config/motr" } module sonr.net.Motr diff --git a/pkl/sonr.orm/Models.pkl b/pkl/sonr.orm/Models.pkl index 29ec55b74..1ffd4324a 100644 --- a/pkl/sonr.orm/Models.pkl +++ b/pkl/sonr.orm/Models.pkl @@ -1,4 +1,4 @@ -@go.Package { name = "github.com/onsonr/sonr/pkg/common/models" } +@go.Package { name = "github.com/onsonr/sonr/internal/models" } module sonr.orm.Models diff --git a/pkl/sonr.orm/UCAN.pkl b/pkl/sonr.orm/UCAN.pkl index 2458af38c..4c895e36e 100644 --- a/pkl/sonr.orm/UCAN.pkl +++ b/pkl/sonr.orm/UCAN.pkl @@ -4,30 +4,37 @@ module sonr.orm.UCAN import "package://pkg.pkl-lang.org/pkl-go/pkl.golang@0.5.0#/go.pkl" - // Capability hierarchy for smart account operations // ---------------------------------------------- -// OWNER -// └─ OPERATOR -// └─ AUTHENTICATE -// └─ AUTHORIZE -// └─ DELEGATE -// ├─ EXECUTE -// └─ INVOKE -// ├─ PROPOSE -// └─ SIGN -// └─ VOTE -// └─ SET_POLICY -// └─ SET_THRESHOLD -// └─ RECOVER -// └─ SOCIAL -// └─ VAULT +// VAULT +// └─ CRUD/ASSET +// └─ CRUD/AUTHZGRANT +// └─ CRUD/PROFILE +// └─ CRUD/RECORD +// └─ USE/RECOVERY +// └─ USE/SYNC +// └─ USE/SIGNER +// ACCOUNT +// └─ EXEC/BROADCAST +// └─ EXEC/QUERY +// └─ EXEC/SIMULATE +// └─ EXEC/VOTE +// └─ EXEC/DELEGATE +// └─ EXEC/INVOKE +// └─ EXEC/SEND +// INTERCHAIN +// └─ TRANSFER/SWAP +// └─ TRANSFER/SEND +// └─ TRANSFER/ATOMIC +// └─ TRANSFER/BATCH +// └─ TRANSFER/P2P +// └─ TRANSFER/SEND -typealias Capability = "CAP_OWNER"|"CAP_OPERATOR"|"CAP_OBSERVER"|"CAP_AUTHENTICATE"|"CAP_AUTHORIZE"|"CAP_DELEGATE"|"CAP_INVOKE"|"CAP_EXECUTE"|"CAP_PROPOSE"|"CAP_SIGN"|"CAP_SET_POLICY"|"CAP_SET_THRESHOLD"|"CAP_RECOVER"|"CAP_SOCIAL"|"CAP_VOTE" | "CAP_RESOLVER" | "CAP_PRODUCER" - -typealias ResourceType = "RES_ACCOUNT"|"RES_TRANSACTION"|"RES_POLICY"|"RES_RECOVERY"|"RES_VAULT" | "RES_IPFS" | "RES_IPNS" | "RES_KEYSHARE" - -typealias PolicyType = "POLICY_THRESHOLD"|"POLICY_TIMELOCK"|"POLICY_WHITELIST" | "POLICY_KEYGEN" - +typealias CapVault = "crud/asset" | "crud/authzgrant" | "crud/profile" | "crud/record" | "use/recovery" | "use/sync" | "use/signer" +typealias CapAccount = "exec/broadcast" | "exec/query" | "exec/simulate" | "exec/vote" | "exec/delegate" | "exec/invoke" | "exec/send" +typealias CapInterchain = "transfer/swap" | "transfer/send" | "transfer/atomic" | "transfer/batch" | "transfer/p2p" | "transfer/send" +typealias ResVault = "ks/enclave" | "loc/cid" | "loc/entity" | "loc/ipns" | "addr/sonr" | "chain/code" +typealias ResAccount = "acc/sequence" | "acc/number" | "chain/id" | "asset/code" | "authz/grant" +typealias ResInterchain = "channnel/port" | "chain/id" | "chain/name" | "acc/host" | "acc/controller" diff --git a/scripts/test_node.sh b/scripts/test_node.sh index 64f8e6cfc..5c8aa31d8 100644 --- a/scripts/test_node.sh +++ b/scripts/test_node.sh @@ -154,4 +154,4 @@ sed -i 's/address = ":8080"/address = "0.0.0.0:'$ROSETTA'"/g' $HOME_DIR/config/a sed -i 's/timeout_commit = "5s"/timeout_commit = "'$BLOCK_TIME'"/g' $HOME_DIR/config/config.toml # Start the node with 0 gas fees -BINARY start --pruning=nothing --minimum-gas-prices=0$DENOM --rpc.laddr="tcp://0.0.0.0:$RPC" +BINARY start --pruning=nothing --minimum-gas-prices=0$DENOM --rpc.laddr="tcp://0.0.0.0:$RPC" --grpc.address="0.0.0.0:$GRPC" --grpc-web.enable=true diff --git a/x/dwn/types/params.go b/x/dwn/types/params.go index a8fb759fd..9ad1d4278 100644 --- a/x/dwn/types/params.go +++ b/x/dwn/types/params.go @@ -3,11 +3,11 @@ package types import ( "encoding/json" - "github.com/onsonr/sonr/pkg/common/models" - "github.com/onsonr/sonr/pkg/common/models/keyalgorithm" - "github.com/onsonr/sonr/pkg/common/models/keycurve" - "github.com/onsonr/sonr/pkg/common/models/keyencoding" - "github.com/onsonr/sonr/pkg/common/models/keyrole" + "github.com/onsonr/sonr/internal/models" + "github.com/onsonr/sonr/internal/models/keyalgorithm" + "github.com/onsonr/sonr/internal/models/keycurve" + "github.com/onsonr/sonr/internal/models/keyencoding" + "github.com/onsonr/sonr/internal/models/keyrole" ) // DefaultParams returns default module parameters.