mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-02 17:31:39 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
354fdabdc4 | ||
|
|
7c4586ce90 | ||
|
|
6d27b926f6 | ||
|
|
f74bdc1d79 | ||
|
|
8522cb8d47 | ||
|
|
30475b9fbd | ||
|
|
1388a673e5 | ||
|
|
ac8c153b28 | ||
|
|
ee12bf684e |
@@ -2,7 +2,7 @@
|
||||
name = "cz_conventional_commits"
|
||||
tag_format = "v$version"
|
||||
version_scheme = "semver"
|
||||
version = "0.5.25"
|
||||
version = "0.5.27"
|
||||
update_changelog_on_bump = true
|
||||
major_version_zero = true
|
||||
changelog_file = "./docs/docs/changelog/index.md"
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
name: Run All Checks
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
merge_group:
|
||||
|
||||
jobs:
|
||||
test-builds:
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
name: Run Build Tests
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: onsonr/sonr
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.23"
|
||||
check-latest: true
|
||||
- name: Run Sonrd Build
|
||||
run: make build
|
||||
- name: Run Hway Build
|
||||
run: make build-hway
|
||||
- name: Run Motr Build
|
||||
run: make build-motr
|
||||
|
||||
test-unit:
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
name: Run Unit Tests
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: onsonr/sonr
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.23"
|
||||
check-latest: true
|
||||
- run: make test-unit
|
||||
|
||||
test-race:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'merge_group'
|
||||
name: Run Race Tests
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: onsonr/sonr
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.23"
|
||||
check-latest: true
|
||||
- run: make test-race
|
||||
|
||||
test-cover:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'merge_group'
|
||||
name: Run Coverage Tests
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: onsonr/sonr
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.23"
|
||||
check-latest: true
|
||||
- run: make test-cover
|
||||
|
||||
goreleaser-check:
|
||||
if: github.event_name == 'merge_group'
|
||||
runs-on: ubuntu-latest
|
||||
name: Check GoReleaser Config
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: onsonr/sonr
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.23"
|
||||
check-latest: true
|
||||
|
||||
- name: Check GoReleaser Config
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
distribution: goreleaser-pro
|
||||
version: latest
|
||||
args: check
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
|
||||
|
||||
- name: GoReleaser Dry Run
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
distribution: goreleaser-pro
|
||||
version: latest
|
||||
args: release --snapshot --clean --skip=publish
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
|
||||
@@ -1,7 +1,7 @@
|
||||
name: PR Labeler
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened]
|
||||
types: [opened, edited]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
name: Run Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
merge_group:
|
||||
|
||||
jobs:
|
||||
test-unit:
|
||||
runs-on: ubuntu-latest
|
||||
name: Run Unit Tests
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: onsonr/sonr
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.23"
|
||||
check-latest: true
|
||||
- run: make test-unit
|
||||
|
||||
test-race:
|
||||
runs-on: ubuntu-latest
|
||||
name: Run Race Tests
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: onsonr/sonr
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.23"
|
||||
check-latest: true
|
||||
- run: make test-race
|
||||
|
||||
test-cover:
|
||||
runs-on: ubuntu-latest
|
||||
name: Run Coverage Tests
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: onsonr/sonr
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.23"
|
||||
check-latest: true
|
||||
- run: make test-cover
|
||||
@@ -2,9 +2,11 @@ name: Versioned Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ["v*"]
|
||||
branches: ["develop"]
|
||||
tags:
|
||||
- "v[0-9]+.[0-9]+.[0-9]+"
|
||||
workflow_dispatch:
|
||||
repository_dispatch:
|
||||
types: [trigger-release]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Update Version
|
||||
name: Update Version and Release
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -10,30 +10,33 @@ permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
run-tests:
|
||||
name: "Run tests"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.23"
|
||||
check-latest: true
|
||||
- name: Run tests
|
||||
run: make test
|
||||
|
||||
bump-version:
|
||||
needs: [run-tests]
|
||||
runs-on: ubuntu-latest
|
||||
name: "Bump Version"
|
||||
if: ${{ !startsWith(github.event.head_commit.message, 'bump:') }}
|
||||
outputs:
|
||||
new_tag: ${{ steps.bump.outputs.new_tag }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
fetch-depth: 0
|
||||
- name: Create bump and changelog
|
||||
id: bump
|
||||
uses: commitizen-tools/commitizen-action@master
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
increment: "PATCH"
|
||||
|
||||
trigger-release:
|
||||
needs: [bump-version]
|
||||
if: needs.bump-version.outputs.new_tag != ''
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Trigger release workflow
|
||||
uses: peter-evans/repository-dispatch@v2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
event-type: trigger-release
|
||||
client-payload: '{"tag": "${{ needs.bump-version.outputs.new_tag }}"}'
|
||||
|
||||
+5
-3
@@ -1,4 +1,5 @@
|
||||
# Binaries
|
||||
no
|
||||
.data
|
||||
schemas
|
||||
*.db
|
||||
@@ -61,9 +62,6 @@ override.tf.json
|
||||
.terraformrc
|
||||
terraform.rc
|
||||
|
||||
Taskfile.yml
|
||||
|
||||
!deploy/**/Taskfile.yml
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
@@ -98,4 +96,8 @@ sonr.wiki
|
||||
!buf.lock
|
||||
|
||||
.air.toml
|
||||
mprocs.yaml
|
||||
mprocs.log
|
||||
tools-stamp
|
||||
sonr.log
|
||||
|
||||
|
||||
@@ -184,7 +184,6 @@ format: format-tools
|
||||
|
||||
mod-tidy:
|
||||
go mod tidy
|
||||
cd interchaintest && go mod tidy
|
||||
|
||||
.PHONY: format-tools lint format mod-tidy
|
||||
|
||||
@@ -307,16 +306,19 @@ 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
|
||||
pkl-gen-go pkl/sonr.orm/Models.pkl
|
||||
pkl-gen-go pkl/sonr.conf/Hway.pkl
|
||||
pkl-gen-go pkl/sonr.conf/Motr.pkl
|
||||
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
|
||||
|
||||
|
||||
###############################################################################
|
||||
@@ -343,9 +345,6 @@ logs-sonr: init-env
|
||||
.PHONY: deploy start start-tui start-uds stop stop-uds restart status
|
||||
|
||||
start: build-hway init-env
|
||||
bin/process-compose up --port $(PC_PORT_NUM) --log-file $(PC_LOG_FILE) --detached -f deploy/process-compose.yaml
|
||||
|
||||
start-tui: build-hway init-env
|
||||
bin/process-compose up --port $(PC_PORT_NUM) --log-file $(PC_LOG_FILE) -f deploy/process-compose.yaml
|
||||
|
||||
start-uds: build-hway init-env
|
||||
@@ -359,6 +358,9 @@ stop-uds: init-env
|
||||
|
||||
status: init-env
|
||||
bin/process-compose project state --port $(PC_PORT_NUM)
|
||||
|
||||
status-uds: init-env
|
||||
bin/process-compose project state --use-uds --unix-socket $(PC_SOCKET_PATH)
|
||||
###############################################################################
|
||||
### help ###
|
||||
###############################################################################
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
[](https://sonr.io)
|
||||
[](https://goreportcard.com/report/github.com/onsonr/sonr)
|
||||
[](https://sonarcloud.io/summary/new_code?id=sonr-io_sonr)
|
||||
[](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.
|
||||
|
||||
|
||||
+20
-48
@@ -6,27 +6,24 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
"github.com/onsonr/sonr/crypto/ucan"
|
||||
"github.com/onsonr/sonr/internal/gateway"
|
||||
"github.com/onsonr/sonr/pkg/common/ipfs"
|
||||
config "github.com/onsonr/sonr/pkg/config/hway"
|
||||
"github.com/onsonr/sonr/pkg/database/sessions"
|
||||
"github.com/onsonr/sonr/pkg/didauth/producer"
|
||||
"github.com/onsonr/sonr/pkg/common"
|
||||
"github.com/onsonr/sonr/pkg/gateway"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Command line flags
|
||||
var (
|
||||
servePort int // Gateway http entry point (default 3000)
|
||||
configDir string // Hway config directory (default hway)
|
||||
sqliteFile string // SQLite database file (default hway.db)
|
||||
chainID string // Current chain ID (default sonr-testnet-1)
|
||||
ipfsGatewayURL string // IPFS gateway URL (default localhost:8080)
|
||||
sonrAPIURL string // Sonr API URL (default localhost:1317)
|
||||
sonrGrpcURL string // Sonr gRPC URL (default localhost:9090)
|
||||
sonrRPCURL string // Sonr RPC URL (default localhost:26657)
|
||||
|
||||
psqlHost string // PostgresSQL Host Flag
|
||||
psqlUser string // PostgresSQL User Flag
|
||||
psqlPass string // PostgresSQL Password Flag
|
||||
psqlDB string // PostgresSQL Database Flag
|
||||
)
|
||||
|
||||
func rootCmd() *cobra.Command {
|
||||
@@ -38,7 +35,11 @@ func rootCmd() *cobra.Command {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
e, err := setupServer(env)
|
||||
ipc, err := common.NewIPFS()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
e, err := gateway.New(env, ipc)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -50,50 +51,21 @@ func rootCmd() *cobra.Command {
|
||||
},
|
||||
}
|
||||
cmd.Flags().IntVar(&servePort, "serve-port", 3000, "Port to serve the gateway on")
|
||||
cmd.Flags().StringVar(&configDir, "config-dir", "hway", "Directory to store config files")
|
||||
cmd.Flags().StringVar(&sqliteFile, "sqlite-file", "hway.db", "File to store sqlite database")
|
||||
cmd.Flags().StringVar(&chainID, "chain-id", "sonr-testnet-1", "Chain ID")
|
||||
cmd.Flags().StringVar(&ipfsGatewayURL, "ipfs-gateway-url", "localhost:8080", "IPFS gateway URL")
|
||||
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(&psqlHost, "psql-host", "", "PostgresSQL Host")
|
||||
cmd.Flags().StringVar(&psqlUser, "psql-user", "", "PostgresSQL User")
|
||||
cmd.Flags().StringVar(&psqlPass, "psql-pass", "", "PostgresSQL Password")
|
||||
cmd.Flags().StringVar(&psqlDB, "psql-db", "", "PostgresSQL Database")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func loadEnvImplFromArgs(args []string) (config.Hway, error) {
|
||||
cmd := rootCmd()
|
||||
if err := cmd.ParseFlags(args); err != nil {
|
||||
return nil, err
|
||||
func formatPsqlDSN() string {
|
||||
if psqlHost == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
env := &config.HwayImpl{
|
||||
ServePort: servePort,
|
||||
ConfigDir: configDir,
|
||||
SqliteFile: sqliteFile,
|
||||
ChainId: chainID,
|
||||
IpfsGatewayUrl: ipfsGatewayURL,
|
||||
SonrApiUrl: sonrAPIURL,
|
||||
SonrGrpcUrl: sonrGrpcURL,
|
||||
SonrRpcUrl: sonrRPCURL,
|
||||
}
|
||||
return env, nil
|
||||
}
|
||||
|
||||
// setupServer sets up the server
|
||||
func setupServer(env config.Hway) (*echo.Echo, error) {
|
||||
ipc, err := ipfs.NewClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
db, err := sessions.NewGormDB(env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
e := echo.New()
|
||||
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
|
||||
return fmt.Sprintf("host=%s user=%s password=%s dbname=%s sslmode=disable", psqlHost, psqlUser, psqlPass, psqlDB)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
config "github.com/onsonr/sonr/internal/config/hway"
|
||||
)
|
||||
|
||||
// main is the entry point for the application
|
||||
@@ -15,3 +17,21 @@ func main() {
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func loadEnvImplFromArgs(args []string) (config.Hway, error) {
|
||||
cmd := rootCmd()
|
||||
if err := cmd.ParseFlags(args); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
env := &config.HwayImpl{
|
||||
ServePort: servePort,
|
||||
ChainId: chainID,
|
||||
IpfsGatewayUrl: ipfsGatewayURL,
|
||||
SonrApiUrl: sonrAPIURL,
|
||||
SonrGrpcUrl: sonrGrpcURL,
|
||||
SonrRpcUrl: sonrRPCURL,
|
||||
PsqlDSN: formatPsqlDSN(),
|
||||
}
|
||||
return env, nil
|
||||
}
|
||||
|
||||
+6
-7
@@ -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,11 +44,11 @@ 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)
|
||||
e.Use(controller.Middleware(nil))
|
||||
// e.Use(controller.Middleware(nil))
|
||||
vault.RegisterRoutes(e, config)
|
||||
wasm.ServeFetch(e)
|
||||
}
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
func main() {
|
||||
rootCmd := NewRootCmd()
|
||||
|
||||
rootCmd.AddCommand(newPklInitCmd())
|
||||
if err := svrcmd.Execute(rootCmd, "", app.DefaultNodeHome); err != nil {
|
||||
log.NewLogger(rootCmd.OutOrStderr()).Error("failure when running app", "err", err)
|
||||
os.Exit(1)
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/apple/pkl-go/pkl"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var configDir string
|
||||
|
||||
func newPklInitCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "init-pkl",
|
||||
Short: "Initialize the Sonrd configuration using PKL",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ctx := context.Background()
|
||||
evaluator, err := pkl.NewEvaluator(ctx, pkl.PreconfiguredOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer evaluator.Close()
|
||||
|
||||
appPath := formatConfigPath(cmd, "app.toml")
|
||||
configPath := formatConfigPath(cmd, "config.toml")
|
||||
|
||||
// Create app.toml
|
||||
if err := createAppToml(evaluator, appPath); err != nil {
|
||||
cmd.PrintErrf("Failed to create app.toml: %v\n", err)
|
||||
return err
|
||||
}
|
||||
cmd.Printf("Successfully created %s\n", appPath)
|
||||
|
||||
// Create config.toml
|
||||
if err := createConfigToml(evaluator, configPath); err != nil {
|
||||
cmd.PrintErrf("Failed to create config.toml: %v\n", err)
|
||||
return err
|
||||
}
|
||||
cmd.Printf("Successfully created %s\n", configPath)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVar(&configDir, "config-dir", "~/.sonr/config", "Path to where pkl files should be output")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func createAppToml(evaluator pkl.Evaluator, path string) error {
|
||||
appSource := pkl.UriSource("https://pkl.sh/sonr.chain/0.0.2/App.pkl")
|
||||
res, err := evaluator.EvaluateOutputText(context.Background(), appSource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("res: %s", res)
|
||||
return writeConfigFile(path, res)
|
||||
}
|
||||
|
||||
func createConfigToml(evaluator pkl.Evaluator, path string) error {
|
||||
configSource := pkl.UriSource("https://pkl.sh/sonr.chain/0.0.2/Config.pkl")
|
||||
res, err := evaluator.EvaluateOutputText(context.Background(), configSource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("res: %s", res)
|
||||
return writeConfigFile(path, res)
|
||||
}
|
||||
|
||||
func formatConfigPath(cmd *cobra.Command, fileName string) string {
|
||||
configDir := cmd.Flag("config-dir").Value.String()
|
||||
// Expand home directory if needed
|
||||
if configDir[:2] == "~/" {
|
||||
home, err := os.UserHomeDir()
|
||||
if err == nil {
|
||||
configDir = filepath.Join(home, configDir[2:])
|
||||
}
|
||||
}
|
||||
return filepath.Join(configDir, fileName)
|
||||
}
|
||||
|
||||
func writeConfigFile(path string, content string) error {
|
||||
// Create the directory path if it doesn't exist
|
||||
dir := filepath.Dir(path)
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if file already exists
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
// File exists, create backup
|
||||
backupPath := path + ".backup"
|
||||
if err := os.Rename(path, backupPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Write the new config file
|
||||
return os.WriteFile(path, []byte(content), 0o644)
|
||||
}
|
||||
@@ -601,11 +601,11 @@ func (p *PointBls12377G1) UnmarshalText(input []byte) error {
|
||||
}
|
||||
|
||||
func (p *PointBls12377G1) MarshalJSON() ([]byte, error) {
|
||||
return pointMarshalJson(p)
|
||||
return pointMarshalJSON(p)
|
||||
}
|
||||
|
||||
func (p *PointBls12377G1) UnmarshalJSON(input []byte) error {
|
||||
pt, err := pointUnmarshalJson(input)
|
||||
pt, err := pointUnmarshalJSON(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -874,11 +874,11 @@ func (p *PointBls12377G2) UnmarshalText(input []byte) error {
|
||||
}
|
||||
|
||||
func (p *PointBls12377G2) MarshalJSON() ([]byte, error) {
|
||||
return pointMarshalJson(p)
|
||||
return pointMarshalJSON(p)
|
||||
}
|
||||
|
||||
func (p *PointBls12377G2) UnmarshalJSON(input []byte) error {
|
||||
pt, err := pointUnmarshalJson(input)
|
||||
pt, err := pointUnmarshalJSON(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -573,11 +573,11 @@ func (p *PointBls12381G1) UnmarshalText(input []byte) error {
|
||||
}
|
||||
|
||||
func (p *PointBls12381G1) MarshalJSON() ([]byte, error) {
|
||||
return pointMarshalJson(p)
|
||||
return pointMarshalJSON(p)
|
||||
}
|
||||
|
||||
func (p *PointBls12381G1) UnmarshalJSON(input []byte) error {
|
||||
pt, err := pointUnmarshalJson(input)
|
||||
pt, err := pointUnmarshalJSON(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -823,11 +823,11 @@ func (p *PointBls12381G2) UnmarshalText(input []byte) error {
|
||||
}
|
||||
|
||||
func (p *PointBls12381G2) MarshalJSON() ([]byte, error) {
|
||||
return pointMarshalJson(p)
|
||||
return pointMarshalJSON(p)
|
||||
}
|
||||
|
||||
func (p *PointBls12381G2) UnmarshalJSON(input []byte) error {
|
||||
pt, err := pointUnmarshalJson(input)
|
||||
pt, err := pointUnmarshalJSON(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -336,14 +336,14 @@ func pointUnmarshalText(input []byte) (Point, error) {
|
||||
return curve.Point.FromAffineCompressed(buffer)
|
||||
}
|
||||
|
||||
func pointMarshalJson(point Point) ([]byte, error) {
|
||||
func pointMarshalJSON(point Point) ([]byte, error) {
|
||||
m := make(map[string]string, 2)
|
||||
m["type"] = point.CurveName()
|
||||
m["value"] = hex.EncodeToString(point.ToAffineCompressed())
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
func pointUnmarshalJson(input []byte) (Point, error) {
|
||||
func pointUnmarshalJSON(input []byte) (Point, error) {
|
||||
var m map[string]string
|
||||
|
||||
err := json.Unmarshal(input, &m)
|
||||
|
||||
@@ -682,11 +682,11 @@ func (p *PointEd25519) UnmarshalText(input []byte) error {
|
||||
}
|
||||
|
||||
func (p *PointEd25519) MarshalJSON() ([]byte, error) {
|
||||
return pointMarshalJson(p)
|
||||
return pointMarshalJSON(p)
|
||||
}
|
||||
|
||||
func (p *PointEd25519) UnmarshalJSON(input []byte) error {
|
||||
pt, err := pointUnmarshalJson(input)
|
||||
pt, err := pointUnmarshalJSON(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -653,11 +653,11 @@ func (p *PointK256) UnmarshalText(input []byte) error {
|
||||
}
|
||||
|
||||
func (p *PointK256) MarshalJSON() ([]byte, error) {
|
||||
return pointMarshalJson(p)
|
||||
return pointMarshalJSON(p)
|
||||
}
|
||||
|
||||
func (p *PointK256) UnmarshalJSON(input []byte) error {
|
||||
pt, err := pointUnmarshalJson(input)
|
||||
pt, err := pointUnmarshalJSON(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -701,11 +701,11 @@ func (p *BenchPointP256) UnmarshalText(input []byte) error {
|
||||
}
|
||||
|
||||
func (p *BenchPointP256) MarshalJSON() ([]byte, error) {
|
||||
return pointMarshalJson(p)
|
||||
return pointMarshalJSON(p)
|
||||
}
|
||||
|
||||
func (p *BenchPointP256) UnmarshalJSON(input []byte) error {
|
||||
pt, err := pointUnmarshalJson(input)
|
||||
pt, err := pointUnmarshalJSON(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -651,11 +651,11 @@ func (p *PointP256) UnmarshalText(input []byte) error {
|
||||
}
|
||||
|
||||
func (p *PointP256) MarshalJSON() ([]byte, error) {
|
||||
return pointMarshalJson(p)
|
||||
return pointMarshalJSON(p)
|
||||
}
|
||||
|
||||
func (p *PointP256) UnmarshalJSON(input []byte) error {
|
||||
pt, err := pointUnmarshalJson(input)
|
||||
pt, err := pointUnmarshalJSON(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -669,11 +669,11 @@ func (p *PointPallas) UnmarshalText(input []byte) error {
|
||||
}
|
||||
|
||||
func (p *PointPallas) MarshalJSON() ([]byte, error) {
|
||||
return pointMarshalJson(p)
|
||||
return pointMarshalJSON(p)
|
||||
}
|
||||
|
||||
func (p *PointPallas) UnmarshalJSON(input []byte) error {
|
||||
pt, err := pointUnmarshalJson(input)
|
||||
pt, err := pointUnmarshalJSON(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
package keys
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/crypto"
|
||||
mb "github.com/multiformats/go-multibase"
|
||||
varint "github.com/multiformats/go-varint"
|
||||
)
|
||||
|
||||
const (
|
||||
// KeyPrefix indicates a decentralized identifier that uses the key method
|
||||
KeyPrefix = "did:key"
|
||||
// MulticodecKindRSAPubKey rsa-x509-pub https://github.com/multiformats/multicodec/pull/226
|
||||
MulticodecKindRSAPubKey = 0x1205
|
||||
// MulticodecKindEd25519PubKey ed25519-pub
|
||||
MulticodecKindEd25519PubKey = 0xed
|
||||
// MulticodecKindSecp256k1PubKey secp256k1-pub
|
||||
MulticodecKindSecp256k1PubKey = 0x1206
|
||||
)
|
||||
|
||||
// DID is a DID:key identifier
|
||||
type DID struct {
|
||||
crypto.PubKey
|
||||
}
|
||||
|
||||
// NewDID constructs an Identifier from a public key
|
||||
func NewDID(pub crypto.PubKey) (DID, error) {
|
||||
switch pub.Type() {
|
||||
case crypto.Ed25519, crypto.RSA, crypto.Secp256k1:
|
||||
return DID{PubKey: pub}, nil
|
||||
default:
|
||||
return DID{}, fmt.Errorf("unsupported key type: %s", pub.Type())
|
||||
}
|
||||
}
|
||||
|
||||
// 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() {
|
||||
case crypto.RSA:
|
||||
return MulticodecKindRSAPubKey
|
||||
case crypto.Ed25519:
|
||||
return MulticodecKindEd25519PubKey
|
||||
case crypto.Secp256k1:
|
||||
return MulticodecKindSecp256k1PubKey
|
||||
default:
|
||||
panic("unexpected crypto type")
|
||||
}
|
||||
}
|
||||
|
||||
// String returns this did:key formatted as a string
|
||||
func (id DID) String() string {
|
||||
raw, err := id.Raw()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
t := id.MulticodecType()
|
||||
size := varint.UvarintSize(t)
|
||||
data := make([]byte, size+len(raw))
|
||||
n := varint.PutUvarint(data, t)
|
||||
copy(data[n:], raw)
|
||||
|
||||
b58BKeyStr, err := mb.Encode(mb.Base58BTC, data)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s:%s", KeyPrefix, b58BKeyStr)
|
||||
}
|
||||
|
||||
// VerifyKey returns the backing implementation for a public key, one of:
|
||||
// *rsa.PublicKey, ed25519.PublicKey
|
||||
func (id DID) VerifyKey() (interface{}, error) {
|
||||
rawPubBytes, err := id.Raw()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch id.Type() {
|
||||
case crypto.RSA:
|
||||
verifyKeyiface, err := x509.ParsePKIXPublicKey(rawPubBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
verifyKey, ok := verifyKeyiface.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("public key is not an RSA key. got type: %T", verifyKeyiface)
|
||||
}
|
||||
return verifyKey, nil
|
||||
case crypto.Ed25519:
|
||||
return ed25519.PublicKey(rawPubBytes), nil
|
||||
case crypto.Secp256k1:
|
||||
// Handle both compressed and uncompressed Secp256k1 public keys
|
||||
if len(rawPubBytes) == 65 || len(rawPubBytes) == 33 {
|
||||
return rawPubBytes, nil
|
||||
}
|
||||
return nil, fmt.Errorf("invalid Secp256k1 public key length: %d", len(rawPubBytes))
|
||||
default:
|
||||
return nil, fmt.Errorf("unrecognized Public Key type: %s", id.Type())
|
||||
}
|
||||
}
|
||||
|
||||
// Parse turns a string into a key method ID
|
||||
func Parse(keystr string) (DID, error) {
|
||||
var id DID
|
||||
if !strings.HasPrefix(keystr, KeyPrefix) {
|
||||
return id, fmt.Errorf("decentralized identifier is not a 'key' type")
|
||||
}
|
||||
|
||||
keystr = strings.TrimPrefix(keystr, KeyPrefix+":")
|
||||
|
||||
enc, data, err := mb.Decode(keystr)
|
||||
if err != nil {
|
||||
return id, fmt.Errorf("decoding multibase: %w", err)
|
||||
}
|
||||
|
||||
if enc != mb.Base58BTC {
|
||||
return id, fmt.Errorf("unexpected multibase encoding: %s", mb.EncodingToStr[enc])
|
||||
}
|
||||
|
||||
keyType, n, err := varint.FromUvarint(data)
|
||||
if err != nil {
|
||||
return id, err
|
||||
}
|
||||
|
||||
switch keyType {
|
||||
case MulticodecKindRSAPubKey:
|
||||
pub, err := crypto.UnmarshalRsaPublicKey(data[n:])
|
||||
if err != nil {
|
||||
return id, err
|
||||
}
|
||||
return DID{pub}, nil
|
||||
case MulticodecKindEd25519PubKey:
|
||||
pub, err := crypto.UnmarshalEd25519PublicKey(data[n:])
|
||||
if err != nil {
|
||||
return id, err
|
||||
}
|
||||
return DID{pub}, nil
|
||||
case MulticodecKindSecp256k1PubKey:
|
||||
// Handle both compressed and uncompressed formats
|
||||
keyData := data[n:]
|
||||
if len(keyData) != 33 && len(keyData) != 65 {
|
||||
return id, fmt.Errorf("invalid Secp256k1 public key length: %d", len(keyData))
|
||||
}
|
||||
pub, err := crypto.UnmarshalSecp256k1PublicKey(keyData)
|
||||
if err != nil {
|
||||
return id, fmt.Errorf("failed to unmarshal Secp256k1 key: %w", err)
|
||||
}
|
||||
return DID{pub}, nil
|
||||
}
|
||||
|
||||
return id, fmt.Errorf("unrecognized key type multicodec prefix: %x", data[0])
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package keys
|
||||
|
||||
type DIDMethod string
|
||||
|
||||
const (
|
||||
DIDMethodKey DIDMethod = "key"
|
||||
DIDMethodSonr DIDMethod = "sonr"
|
||||
DIDMehthodBitcoin DIDMethod = "btcr"
|
||||
DIDMethodEthereum DIDMethod = "ethr"
|
||||
DIDMethodCbor DIDMethod = "cbor"
|
||||
DIDMethodCID DIDMethod = "cid"
|
||||
DIDMethodIPFS DIDMethod = "ipfs"
|
||||
)
|
||||
|
||||
func (d DIDMethod) String() string {
|
||||
return string(d)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package parsers
|
||||
@@ -0,0 +1,12 @@
|
||||
package parsers
|
||||
|
||||
type CosmosPrefix string
|
||||
|
||||
const (
|
||||
ATOMPrefix CosmosPrefix = "cosmos"
|
||||
AXELARPrefix CosmosPrefix = "axelar"
|
||||
EVMOSPrefix CosmosPrefix = "evmos"
|
||||
OSMOPrefix CosmosPrefix = "osmo"
|
||||
SONRPrefix CosmosPrefix = "idx"
|
||||
STARSPrefix CosmosPrefix = "stars"
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
package parsers
|
||||
@@ -0,0 +1 @@
|
||||
package parsers
|
||||
@@ -1,4 +1,4 @@
|
||||
package didkey
|
||||
package parsers
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
@@ -23,23 +23,23 @@ const (
|
||||
MulticodecKindSecp256k1PubKey = 0x1206
|
||||
)
|
||||
|
||||
// ID is a DID:key identifier
|
||||
type ID struct {
|
||||
// DIDKey is a DID:key identifier
|
||||
type DIDKey struct {
|
||||
crypto.PubKey
|
||||
}
|
||||
|
||||
// NewID constructs an Identifier from a public key
|
||||
func NewID(pub crypto.PubKey) (ID, error) {
|
||||
// NewKeyDID constructs an Identifier from a public key
|
||||
func NewKeyDID(pub crypto.PubKey) (DIDKey, error) {
|
||||
switch pub.Type() {
|
||||
case crypto.Ed25519, crypto.RSA, crypto.Secp256k1:
|
||||
return ID{PubKey: pub}, nil
|
||||
return DIDKey{PubKey: pub}, nil
|
||||
default:
|
||||
return ID{}, fmt.Errorf("unsupported key type: %s", pub.Type())
|
||||
return DIDKey{}, fmt.Errorf("unsupported key type: %s", pub.Type())
|
||||
}
|
||||
}
|
||||
|
||||
// MulticodecType indicates the type for this multicodec
|
||||
func (id ID) MulticodecType() uint64 {
|
||||
func (id DIDKey) MulticodecType() uint64 {
|
||||
switch id.Type() {
|
||||
case crypto.RSA:
|
||||
return MulticodecKindRSAPubKey
|
||||
@@ -53,7 +53,7 @@ func (id ID) MulticodecType() uint64 {
|
||||
}
|
||||
|
||||
// String returns this did:key formatted as a string
|
||||
func (id ID) String() string {
|
||||
func (id DIDKey) String() string {
|
||||
raw, err := id.Raw()
|
||||
if err != nil {
|
||||
return ""
|
||||
@@ -75,7 +75,7 @@ func (id ID) String() string {
|
||||
|
||||
// VerifyKey returns the backing implementation for a public key, one of:
|
||||
// *rsa.PublicKey, ed25519.PublicKey
|
||||
func (id ID) VerifyKey() (interface{}, error) {
|
||||
func (id DIDKey) VerifyKey() (interface{}, error) {
|
||||
rawPubBytes, err := id.PubKey.Raw()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -100,13 +100,13 @@ func (id ID) VerifyKey() (interface{}, error) {
|
||||
}
|
||||
return nil, fmt.Errorf("invalid Secp256k1 public key length: %d", len(rawPubBytes))
|
||||
default:
|
||||
return nil, fmt.Errorf("unrecognized Public Key type: %s", id.PubKey.Type())
|
||||
return nil, fmt.Errorf("unrecognized Public Key type: %s", id.Type())
|
||||
}
|
||||
}
|
||||
|
||||
// Parse turns a string into a key method ID
|
||||
func Parse(keystr string) (ID, error) {
|
||||
var id ID
|
||||
func Parse(keystr string) (DIDKey, error) {
|
||||
var id DIDKey
|
||||
if !strings.HasPrefix(keystr, KeyPrefix) {
|
||||
return id, fmt.Errorf("decentralized identifier is not a 'key' type")
|
||||
}
|
||||
@@ -133,13 +133,13 @@ func Parse(keystr string) (ID, error) {
|
||||
if err != nil {
|
||||
return id, err
|
||||
}
|
||||
return ID{pub}, nil
|
||||
return DIDKey{pub}, nil
|
||||
case MulticodecKindEd25519PubKey:
|
||||
pub, err := crypto.UnmarshalEd25519PublicKey(data[n:])
|
||||
if err != nil {
|
||||
return id, err
|
||||
}
|
||||
return ID{pub}, nil
|
||||
return DIDKey{pub}, nil
|
||||
case MulticodecKindSecp256k1PubKey:
|
||||
// Handle both compressed and uncompressed formats
|
||||
keyData := data[n:]
|
||||
@@ -150,7 +150,7 @@ func Parse(keystr string) (ID, error) {
|
||||
if err != nil {
|
||||
return id, fmt.Errorf("failed to unmarshal Secp256k1 key: %w", err)
|
||||
}
|
||||
return ID{pub}, nil
|
||||
return DIDKey{pub}, nil
|
||||
}
|
||||
|
||||
return id, fmt.Errorf("unrecognized key type multicodec prefix: %x", data[0])
|
||||
@@ -0,0 +1 @@
|
||||
package parsers
|
||||
@@ -0,0 +1 @@
|
||||
package parsers
|
||||
@@ -0,0 +1,86 @@
|
||||
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
|
||||
Raw() ([]byte, error)
|
||||
Equals(b p2pcrypto.Key) bool
|
||||
Type() p2ppb.KeyType
|
||||
Hex() string
|
||||
Verify(msg []byte, sig []byte) (bool, error)
|
||||
}
|
||||
|
||||
type pubKey struct {
|
||||
publicPoint curves.Point
|
||||
method string
|
||||
}
|
||||
|
||||
func NewPubKey(pk curves.Point) PubKey {
|
||||
return &pubKey{
|
||||
publicPoint: pk,
|
||||
}
|
||||
}
|
||||
|
||||
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() p2ppb.KeyType {
|
||||
return p2ppb.KeyType_Secp256k1
|
||||
}
|
||||
|
||||
func (p pubKey) Verify(data []byte, sigBz []byte) (bool, error) {
|
||||
sig, err := deserializeSignature(sigBz)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
pp, err := getEcdsaPoint(p.Bytes())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
pk := &ecdsa.PublicKey{
|
||||
Curve: pp.Curve,
|
||||
X: pp.X,
|
||||
Y: pp.Y,
|
||||
}
|
||||
|
||||
// Hash the message using SHA3-256
|
||||
hash := sha3.New256()
|
||||
hash.Write(data)
|
||||
digest := hash.Sum(nil)
|
||||
|
||||
return ecdsa.Verify(pk, digest, sig.R, sig.S), nil
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package keys
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/onsonr/sonr/crypto/core/curves"
|
||||
)
|
||||
|
||||
// getEcdsaPoint builds an elliptic curve point from a compressed byte slice
|
||||
func getEcdsaPoint(pubKey []byte) (*curves.EcPoint, error) {
|
||||
crv := curves.K256()
|
||||
x := new(big.Int).SetBytes(pubKey[1:33])
|
||||
y := new(big.Int).SetBytes(pubKey[33:])
|
||||
ecCurve, err := crv.ToEllipticCurve()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error converting curve: %v", err)
|
||||
}
|
||||
return &curves.EcPoint{X: x, Y: y, Curve: ecCurve}, nil
|
||||
}
|
||||
|
||||
// SerializeSecp256k1Signature serializes an ECDSA signature into a byte slice
|
||||
func serializeSignature(sig *curves.EcdsaSignature) ([]byte, error) {
|
||||
rBytes := sig.R.Bytes()
|
||||
sBytes := sig.S.Bytes()
|
||||
|
||||
sigBytes := make([]byte, 66) // V (1 byte) + R (32 bytes) + S (32 bytes)
|
||||
sigBytes[0] = byte(sig.V)
|
||||
copy(sigBytes[33-len(rBytes):33], rBytes)
|
||||
copy(sigBytes[66-len(sBytes):66], sBytes)
|
||||
return sigBytes, nil
|
||||
}
|
||||
|
||||
// DeserializeSecp256k1Signature deserializes an ECDSA signature from a byte slice
|
||||
func deserializeSignature(sigBytes []byte) (*curves.EcdsaSignature, error) {
|
||||
if len(sigBytes) != 66 {
|
||||
return nil, errors.New("malformed signature: not the correct size")
|
||||
}
|
||||
sig := &curves.EcdsaSignature{
|
||||
V: int(sigBytes[0]),
|
||||
R: new(big.Int).SetBytes(sigBytes[1:33]),
|
||||
S: new(big.Int).SetBytes(sigBytes[33:66]),
|
||||
}
|
||||
return sig, nil
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
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"
|
||||
)
|
||||
|
||||
// ╭───────────────────────────────────────────────────────────╮
|
||||
// │ Exported Generics │
|
||||
// ╰───────────────────────────────────────────────────────────╯
|
||||
|
||||
type (
|
||||
AliceOut *dkg.AliceOutput
|
||||
BobOut *dkg.BobOutput
|
||||
Point curves.Point
|
||||
Role string // Role is the type for the role
|
||||
Message *protocol.Message // Message is the protocol.Message that is used for MPC
|
||||
Signature *curves.EcdsaSignature // Signature is the type for the signature
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package mpc
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"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(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(nonce)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test signing
|
||||
testData := []byte("test message")
|
||||
signature, err := enclave.Sign(testData)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, signature)
|
||||
|
||||
// Verify the signature
|
||||
valid, err := enclave.Verify(testData, signature)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, valid)
|
||||
|
||||
// Test invalid data verification
|
||||
invalidData := []byte("wrong message")
|
||||
valid, err = enclave.Verify(invalidData, signature)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, valid)
|
||||
})
|
||||
|
||||
t.Run("Address and Public Key", func(t *testing.T) {
|
||||
nonce := randNonce()
|
||||
enclave, err := GenEnclave(nonce)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test Address
|
||||
addr := enclave.Address()
|
||||
assert.NotEmpty(t, addr)
|
||||
assert.True(t, strings.HasPrefix(addr, "idx"))
|
||||
|
||||
// Test Public Key
|
||||
pubKey := enclave.PubKey()
|
||||
assert.NotNil(t, pubKey)
|
||||
assert.NotEmpty(t, pubKey.Bytes())
|
||||
})
|
||||
|
||||
t.Run("Refresh Operation", func(t *testing.T) {
|
||||
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())
|
||||
})
|
||||
}
|
||||
|
||||
func TestEnclaveSerialization(t *testing.T) {
|
||||
t.Run("Marshal and Unmarshal", func(t *testing.T) {
|
||||
nonce := randNonce()
|
||||
// Generate original enclave
|
||||
original, err := GenEnclave(nonce)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, original)
|
||||
|
||||
// Marshal
|
||||
keyclave, ok := original.(*keyEnclave)
|
||||
require.True(t, ok)
|
||||
|
||||
data, err := keyclave.Serialize()
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, data)
|
||||
|
||||
// Unmarshal
|
||||
restored := &keyEnclave{}
|
||||
err = restored.Unmarshal(data)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify restored enclave
|
||||
assert.Equal(t, keyclave.Addr, restored.Addr)
|
||||
assert.True(t, keyclave.PubPoint.Equal(restored.PubPoint))
|
||||
assert.True(t, restored.IsValid())
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package mpc
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/ecdsa"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/onsonr/sonr/crypto/core/curves"
|
||||
"github.com/onsonr/sonr/crypto/keys"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// 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"`
|
||||
|
||||
// Extra fields
|
||||
nonce []byte
|
||||
}
|
||||
|
||||
func newEnclave(valShare, userShare Message, nonce []byte) (Enclave, error) {
|
||||
pubPoint, err := getAlicePubPoint(valShare)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addr, err := computeSonrAddr(pubPoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &keyEnclave{
|
||||
Addr: addr,
|
||||
PubPoint: pubPoint,
|
||||
ValShare: valShare,
|
||||
UserShare: userShare,
|
||||
nonce: nonce,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Address returns the Sonr address of the keyEnclave
|
||||
func (k *keyEnclave) Address() string {
|
||||
return k.Addr
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// Refresh returns a new keyEnclave
|
||||
func (k *keyEnclave) Refresh() (Enclave, error) {
|
||||
refreshFuncVal, err := valRefreshFunc(k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
refreshFuncUser, err := userRefreshFunc(k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ExecuteRefresh(refreshFuncVal, refreshFuncUser, k.nonce)
|
||||
}
|
||||
|
||||
// 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 := valSignFunc(k, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ExecuteSigning(valSign, userSign)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
ePub, err := getEcdsaPoint(k.PubPoint.ToAffineUncompressed())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
pk := &ecdsa.PublicKey{
|
||||
Curve: ePub.Curve,
|
||||
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
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package mpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/types/bech32"
|
||||
)
|
||||
|
||||
type (
|
||||
ExportedKeyset = []byte
|
||||
)
|
||||
|
||||
type Keyset interface {
|
||||
Address() string
|
||||
Val() *ValKeyshare
|
||||
ValJSON() string
|
||||
User() *UserKeyshare
|
||||
UserJSON() string
|
||||
}
|
||||
|
||||
type keyset struct {
|
||||
val *ValKeyshare
|
||||
user *UserKeyshare
|
||||
addr string
|
||||
}
|
||||
|
||||
func (k keyset) Address() string {
|
||||
return k.addr
|
||||
}
|
||||
|
||||
func (k keyset) Val() *ValKeyshare {
|
||||
return k.val
|
||||
}
|
||||
|
||||
func (k keyset) User() *UserKeyshare {
|
||||
return k.user
|
||||
}
|
||||
|
||||
func (k keyset) ValJSON() string {
|
||||
return k.val.String()
|
||||
}
|
||||
|
||||
func (k keyset) UserJSON() string {
|
||||
return k.user.String()
|
||||
}
|
||||
|
||||
func ComputeIssuerDID(pk []byte) (string, string, error) {
|
||||
addr, err := ComputeSonrAddr(pk)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return fmt.Sprintf("did:sonr:%s", addr), addr, nil
|
||||
}
|
||||
|
||||
func ComputeSonrAddr(pk []byte) (string, error) {
|
||||
sonrAddr, err := bech32.ConvertAndEncode("idx", pk)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return sonrAddr, nil
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package mpc
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
|
||||
"github.com/onsonr/sonr/crypto/core/protocol"
|
||||
"github.com/onsonr/sonr/crypto/tecdsa/dklsv1/dkg"
|
||||
)
|
||||
|
||||
// BaseKeyshare contains common fields and methods for both validator and user keyshares
|
||||
type BaseKeyshare struct {
|
||||
Message *protocol.Message `json:"message"`
|
||||
Role int `json:"role"`
|
||||
UncompressedPubKey []byte `json:"public_key"`
|
||||
CompressedPubKey []byte `json:"compressed_public_key"`
|
||||
}
|
||||
|
||||
func initFromAlice(aliceOut *dkg.AliceOutput, originalMsg *protocol.Message) BaseKeyshare {
|
||||
return BaseKeyshare{
|
||||
Message: originalMsg,
|
||||
Role: 1,
|
||||
UncompressedPubKey: aliceOut.PublicKey.ToAffineUncompressed(),
|
||||
CompressedPubKey: aliceOut.PublicKey.ToAffineCompressed(),
|
||||
}
|
||||
}
|
||||
|
||||
func initFromBob(bobOut *dkg.BobOutput, originalMsg *protocol.Message) BaseKeyshare {
|
||||
return BaseKeyshare{
|
||||
Message: originalMsg,
|
||||
Role: 2,
|
||||
UncompressedPubKey: bobOut.PublicKey.ToAffineUncompressed(),
|
||||
CompressedPubKey: bobOut.PublicKey.ToAffineCompressed(),
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BaseKeyshare) GetPayloads() map[string][]byte {
|
||||
return b.Message.Payloads
|
||||
}
|
||||
|
||||
func (b *BaseKeyshare) GetMetadata() map[string]string {
|
||||
return b.Message.Metadata
|
||||
}
|
||||
|
||||
func (b *BaseKeyshare) GetPublicKey() []byte {
|
||||
return b.UncompressedPubKey
|
||||
}
|
||||
|
||||
func (b *BaseKeyshare) GetProtocol() string {
|
||||
return b.Message.Protocol
|
||||
}
|
||||
|
||||
func (b *BaseKeyshare) GetRole() int32 {
|
||||
return int32(b.Role)
|
||||
}
|
||||
|
||||
func (b *BaseKeyshare) GetVersion() uint32 {
|
||||
return uint32(b.Message.Version)
|
||||
}
|
||||
|
||||
func (b *BaseKeyshare) ECDSAPublicKey() (*ecdsa.PublicKey, error) {
|
||||
return ComputeEcdsaPublicKey(b.UncompressedPubKey)
|
||||
}
|
||||
|
||||
func (b *BaseKeyshare) ExtractMessage() *protocol.Message {
|
||||
return &protocol.Message{
|
||||
Payloads: b.GetPayloads(),
|
||||
Metadata: b.GetMetadata(),
|
||||
Protocol: b.GetProtocol(),
|
||||
Version: uint(b.GetVersion()),
|
||||
}
|
||||
}
|
||||
+15
-124
@@ -1,20 +1,13 @@
|
||||
package mpc
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
genericecdsa "crypto/ecdsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// NewKeyshareSource generates a new MPC keyshare
|
||||
func NewKeyset() (Keyset, error) {
|
||||
// GenEnclave generates a new MPC keyshare
|
||||
func GenEnclave(nonce []byte) (Enclave, error) {
|
||||
curve := curves.K256()
|
||||
valKs := dklsv1.NewAliceDkg(curve, protocol.Version1)
|
||||
userKs := dklsv1.NewBobDkg(curve, protocol.Version1)
|
||||
@@ -26,27 +19,15 @@ func NewKeyset() (Keyset, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
valShare, err := NewValKeyshare(valRes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userRes, err := userKs.Result(protocol.Version1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userShare, err := NewUserKeyshare(userRes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addr, err := computeSonrAddr(valShare.CompressedPublicKey())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return keyset{val: valShare, user: userShare, addr: addr}, nil
|
||||
return newEnclave(valRes, userRes, nonce)
|
||||
}
|
||||
|
||||
// ExecuteSigning runs the MPC signing protocol
|
||||
func ExecuteSigning(signFuncVal SignFunc, signFuncUser SignFunc) (Signature, error) {
|
||||
func ExecuteSigning(signFuncVal SignFunc, signFuncUser SignFunc) ([]byte, error) {
|
||||
aErr, bErr := RunProtocol(signFuncVal, signFuncUser)
|
||||
if err := checkIteratedErrors(aErr, bErr); err != nil {
|
||||
return nil, err
|
||||
@@ -55,11 +36,19 @@ func ExecuteSigning(signFuncVal SignFunc, signFuncUser SignFunc) (Signature, err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dklsv1.DecodeSignature(out)
|
||||
s, err := dklsv1.DecodeSignature(out)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sig, err := serializeSignature(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sig, nil
|
||||
}
|
||||
|
||||
// ExecuteRefresh runs the MPC refresh protocol
|
||||
func ExecuteRefresh(refreshFuncVal RefreshFunc, refreshFuncUser RefreshFunc) (Keyset, 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
|
||||
@@ -68,53 +57,11 @@ func ExecuteRefresh(refreshFuncVal RefreshFunc, refreshFuncUser RefreshFunc) (Ke
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
valShare, err := NewValKeyshare(valRefreshResult)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userRefreshResult, err := refreshFuncUser.Result(protocol.Version1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userShare, err := NewUserKeyshare(userRefreshResult)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addr, err := computeSonrAddr(valShare.CompressedPublicKey())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return keyset{val: valShare, user: userShare, addr: addr}, nil
|
||||
}
|
||||
|
||||
// SerializeSecp256k1Signature serializes an ECDSA signature into a byte slice
|
||||
func SerializeSignature(sig Signature) ([]byte, error) {
|
||||
rBytes := sig.R.Bytes()
|
||||
sBytes := sig.S.Bytes()
|
||||
|
||||
sigBytes := make([]byte, 66) // V (1 byte) + R (32 bytes) + S (32 bytes)
|
||||
sigBytes[0] = byte(sig.V)
|
||||
copy(sigBytes[33-len(rBytes):33], rBytes)
|
||||
copy(sigBytes[66-len(sBytes):66], sBytes)
|
||||
return sigBytes, nil
|
||||
}
|
||||
|
||||
// DeserializeSecp256k1Signature deserializes an ECDSA signature from a byte slice
|
||||
func DeserializeSignature(sigBytes []byte) (Signature, error) {
|
||||
if len(sigBytes) != 66 {
|
||||
return nil, errors.New("malformed signature: not the correct size")
|
||||
}
|
||||
sig := &curves.EcdsaSignature{
|
||||
V: int(sigBytes[0]),
|
||||
R: new(big.Int).SetBytes(sigBytes[1:33]),
|
||||
S: new(big.Int).SetBytes(sigBytes[33:66]),
|
||||
}
|
||||
return sig, nil
|
||||
}
|
||||
|
||||
// VerifyMPCSignature verifies an MPC signature
|
||||
func VerifyMPCSignature(sig Signature, msg []byte, publicKey *ecdsa.PublicKey) bool {
|
||||
return ecdsa.Verify(publicKey, msg, sig.R, sig.S)
|
||||
return newEnclave(valRefreshResult, userRefreshResult, nonce)
|
||||
}
|
||||
|
||||
// For DKG bob starts first. For refresh and sign, Alice starts first.
|
||||
@@ -139,59 +86,3 @@ func RunProtocol(firstParty protocol.Iterator, secondParty protocol.Iterator) (e
|
||||
}
|
||||
return aErr, bErr
|
||||
}
|
||||
|
||||
// ComputeEcPoint builds an elliptic curve point from a compressed byte slice
|
||||
func ComputeEcPoint(pubKey []byte) (*curves.EcPoint, error) {
|
||||
crv := curves.K256()
|
||||
x := new(big.Int).SetBytes(pubKey[1:33])
|
||||
y := new(big.Int).SetBytes(pubKey[33:])
|
||||
ecCurve, err := crv.ToEllipticCurve()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error converting curve: %v", err)
|
||||
}
|
||||
return &curves.EcPoint{X: x, Y: y, Curve: ecCurve}, nil
|
||||
}
|
||||
|
||||
func ComputeEcdsaPublicKey(pubKey []byte) (*genericecdsa.PublicKey, error) {
|
||||
pk, err := ComputeEcPoint(pubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &genericecdsa.PublicKey{
|
||||
Curve: pk.Curve,
|
||||
X: pk.X,
|
||||
Y: pk.Y,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// VerifySignature verifies the signature of a message
|
||||
func VerifySignature(pk []byte, msg []byte, sig []byte) (bool, error) {
|
||||
pp, err := ComputeEcPoint(pk)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
sigEd, err := DeserializeSignature(sig)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
hash := sha3.New256()
|
||||
_, err = hash.Write(msg)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
digest := hash.Sum(nil)
|
||||
return curves.VerifyEcdsa(pp, digest[:], sigEd), nil
|
||||
}
|
||||
|
||||
func checkIteratedErrors(aErr, bErr error) error {
|
||||
if aErr == protocol.ErrProtocolFinished && bErr == protocol.ErrProtocolFinished {
|
||||
return nil
|
||||
}
|
||||
if aErr != protocol.ErrProtocolFinished {
|
||||
return aErr
|
||||
}
|
||||
if bErr != protocol.ErrProtocolFinished {
|
||||
return bErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,150 +0,0 @@
|
||||
package mpc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/types/bech32"
|
||||
"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"
|
||||
)
|
||||
|
||||
var ErrInvalidKeyshareRole = errors.New("invalid keyshare role")
|
||||
|
||||
type Role int
|
||||
|
||||
const (
|
||||
RoleUnknown Role = iota
|
||||
RoleUser
|
||||
RoleValidator
|
||||
)
|
||||
|
||||
func (r Role) IsUser() bool {
|
||||
return r == RoleUser
|
||||
}
|
||||
|
||||
func (r Role) IsValidator() bool {
|
||||
return r == RoleValidator
|
||||
}
|
||||
|
||||
// Message is the protocol.Message that is used for MPC
|
||||
type Message *protocol.Message
|
||||
|
||||
type Signature *curves.EcdsaSignature
|
||||
|
||||
// RefreshFunc is the type for the refresh function
|
||||
type RefreshFunc interface {
|
||||
protocol.Iterator
|
||||
}
|
||||
|
||||
// SignFunc is the type for the sign function
|
||||
type SignFunc interface {
|
||||
protocol.Iterator
|
||||
}
|
||||
|
||||
type ValKeyshare struct {
|
||||
BaseKeyshare
|
||||
encoded string
|
||||
}
|
||||
|
||||
func computeSonrAddr(pk []byte) (string, error) {
|
||||
sonrAddr, err := bech32.ConvertAndEncode("idx", pk)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return sonrAddr, nil
|
||||
}
|
||||
|
||||
func NewValKeyshare(msg *protocol.Message) (*ValKeyshare, error) {
|
||||
vks := new(ValKeyshare)
|
||||
encoded, err := protocol.EncodeMessage(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
valShare, err := dklsv1.DecodeAliceDkgResult(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vks.BaseKeyshare = initFromAlice(valShare, msg)
|
||||
vks.encoded = encoded
|
||||
return vks, nil
|
||||
}
|
||||
|
||||
func (v *ValKeyshare) RefreshFunc() (RefreshFunc, error) {
|
||||
curve := curves.K256()
|
||||
return dklsv1.NewAliceRefresh(curve, v.ExtractMessage(), protocol.Version1)
|
||||
}
|
||||
|
||||
func (v *ValKeyshare) SignFunc(msg []byte) (SignFunc, error) {
|
||||
curve := curves.K256()
|
||||
return dklsv1.NewAliceSign(curve, sha3.New256(), msg, v.ExtractMessage(), protocol.Version1)
|
||||
}
|
||||
|
||||
func (v *ValKeyshare) String() string {
|
||||
return v.encoded
|
||||
}
|
||||
|
||||
// PublicKey returns the uncompressed public key (65 bytes)
|
||||
func (v *ValKeyshare) PublicKey() []byte {
|
||||
return v.UncompressedPubKey
|
||||
}
|
||||
|
||||
// CompressedPublicKey returns the compressed public key (33 bytes)
|
||||
func (v *ValKeyshare) CompressedPublicKey() []byte {
|
||||
return v.CompressedPubKey
|
||||
}
|
||||
|
||||
type UserKeyshare struct {
|
||||
BaseKeyshare
|
||||
encoded string
|
||||
}
|
||||
|
||||
func NewUserKeyshare(msg *protocol.Message) (*UserKeyshare, error) {
|
||||
uks := new(UserKeyshare)
|
||||
encoded, err := protocol.EncodeMessage(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := dklsv1.DecodeBobDkgResult(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
uks.BaseKeyshare = initFromBob(out, msg)
|
||||
uks.encoded = encoded
|
||||
return uks, nil
|
||||
}
|
||||
|
||||
func (u *UserKeyshare) RefreshFunc() (RefreshFunc, error) {
|
||||
curve := curves.K256()
|
||||
return dklsv1.NewBobRefresh(curve, u.ExtractMessage(), protocol.Version1)
|
||||
}
|
||||
|
||||
func (u *UserKeyshare) SignFunc(msg []byte) (SignFunc, error) {
|
||||
curve := curves.K256()
|
||||
return dklsv1.NewBobSign(curve, sha3.New256(), msg, u.ExtractMessage(), protocol.Version1)
|
||||
}
|
||||
|
||||
func (u *UserKeyshare) String() string {
|
||||
return u.encoded
|
||||
}
|
||||
|
||||
// PublicKey returns the uncompressed public key (65 bytes)
|
||||
func (u *UserKeyshare) PublicKey() []byte {
|
||||
return u.UncompressedPubKey
|
||||
}
|
||||
|
||||
// CompressedPublicKey returns the compressed public key (33 bytes)
|
||||
func (u *UserKeyshare) CompressedPublicKey() []byte {
|
||||
return u.CompressedPubKey
|
||||
}
|
||||
|
||||
func encodeMessage(m *protocol.Message) (string, error) {
|
||||
return protocol.EncodeMessage(m)
|
||||
}
|
||||
|
||||
func decodeMessage(s string) (*protocol.Message, error) {
|
||||
return protocol.DecodeMessage(s)
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package spec
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
|
||||
"github.com/golang-jwt/jwt"
|
||||
)
|
||||
|
||||
// MPCSigningMethod implements the SigningMethod interface for MPC-based signing
|
||||
type MPCSigningMethod struct {
|
||||
Name string
|
||||
ks ucanKeyshare
|
||||
}
|
||||
|
||||
// NewJWTSigningMethod creates a new MPC signing method with the given keyshare source
|
||||
func NewJWTSigningMethod(name string, ks ucanKeyshare) *MPCSigningMethod {
|
||||
return &MPCSigningMethod{
|
||||
Name: name,
|
||||
ks: ks,
|
||||
}
|
||||
}
|
||||
|
||||
// Alg returns the signing method's name
|
||||
func (m *MPCSigningMethod) Alg() string {
|
||||
return m.Name
|
||||
}
|
||||
|
||||
// Verify verifies the signature using the MPC public key
|
||||
func (m *MPCSigningMethod) Verify(signingString, signature string, key interface{}) error {
|
||||
// // Decode the signature
|
||||
// sig, err := base64.RawURLEncoding.DecodeString(signature)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// // Hash the signing string
|
||||
// hasher := sha256.New()
|
||||
// hasher.Write([]byte(signingString))
|
||||
// digest := hasher.Sum(nil)
|
||||
// valid, err := m.ks.valShare.PublicKey().Verify(digest, sig)
|
||||
// if !valid || err != nil {
|
||||
// return fmt.Errorf("invalid signature")
|
||||
// }
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sign signs the data using MPC
|
||||
func (m *MPCSigningMethod) Sign(signingString string, key interface{}) (string, error) {
|
||||
// Hash the signing string
|
||||
hasher := sha256.New()
|
||||
hasher.Write([]byte(signingString))
|
||||
// digest := hasher.Sum(nil)
|
||||
//
|
||||
// // Create signing functions
|
||||
// signFunc, err := m.ks.userShare.SignFunc(digest)
|
||||
// if err != nil {
|
||||
// return "", fmt.Errorf("failed to create sign function: %w", err)
|
||||
// }
|
||||
//
|
||||
// valSignFunc, err := m.ks.valShare.SignFunc(digest)
|
||||
// if err != nil {
|
||||
// return "", fmt.Errorf("failed to create validator sign function: %w", err)
|
||||
// }
|
||||
|
||||
// // Run the signing protocol
|
||||
// sig, err := mpc.ExecuteSigning(valSignFunc, signFunc)
|
||||
// if err != nil {
|
||||
// return "", fmt.Errorf("failed to run sign protocol: %w", err)
|
||||
// }
|
||||
|
||||
// Encode the signature
|
||||
// encoded := base64.RawURLEncoding.EncodeToString(sig)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Register the MPC signing method
|
||||
jwt.RegisterSigningMethod("MPC256", func() jwt.SigningMethod {
|
||||
return &MPCSigningMethod{
|
||||
Name: "MPC256",
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -5,9 +5,8 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/onsonr/sonr/crypto/mpc"
|
||||
"github.com/onsonr/sonr/crypto/keys"
|
||||
"github.com/onsonr/sonr/crypto/ucan"
|
||||
"github.com/onsonr/sonr/crypto/ucan/didkey"
|
||||
"lukechampine.com/blake3"
|
||||
)
|
||||
|
||||
@@ -23,21 +22,18 @@ type KeyshareSource interface {
|
||||
UCANParser() *ucan.TokenParser
|
||||
}
|
||||
|
||||
func NewSource(ks mpc.Keyset) (KeyshareSource, error) {
|
||||
val := ks.Val()
|
||||
user := ks.User()
|
||||
iss, addr, err := ComputeIssuerDID(val.GetPublicKey())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ucanKeyshare{
|
||||
userShare: user,
|
||||
valShare: val,
|
||||
addr: addr,
|
||||
issuerDID: iss,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// func NewSource(ks mpc.KeyEnclave) (KeyshareSource, error) {
|
||||
// iss, addr, err := getIssuerDID(val.PublicKey())
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
//
|
||||
// return ucanKeyshare{
|
||||
// issuerDID: iss,
|
||||
// addr: addr,
|
||||
// }, nil
|
||||
// }
|
||||
//
|
||||
// Address returns the address of the keyshare
|
||||
func (k ucanKeyshare) Address() string {
|
||||
return k.addr
|
||||
@@ -61,33 +57,32 @@ 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) {
|
||||
// Create signing functions
|
||||
signFunc, err := k.userShare.SignFunc(data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create sign function: %w", err)
|
||||
}
|
||||
|
||||
valSignFunc, err := k.valShare.SignFunc(data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create validator sign function: %w", err)
|
||||
}
|
||||
// // Create signing functions
|
||||
// signFunc, err := k.userShare.SignFunc(data)
|
||||
// if err != nil {
|
||||
// return nil, fmt.Errorf("failed to create sign function: %w", err)
|
||||
// }
|
||||
//
|
||||
// valSignFunc, err := k.valShare.SignFunc(data)
|
||||
// if err != nil {
|
||||
// return nil, fmt.Errorf("failed to create validator sign function: %w", err)
|
||||
// }
|
||||
|
||||
// Run the signing protocol
|
||||
sig, err := mpc.ExecuteSigning(valSignFunc, signFunc)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to run sign protocol: %w", err)
|
||||
}
|
||||
return mpc.SerializeSignature(sig)
|
||||
// return mpc.ExecuteSigning(valSignFunc, signFunc)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (k ucanKeyshare) VerifyData(data []byte, sig []byte) (bool, error) {
|
||||
return mpc.VerifySignature(k.userShare.PublicKey(), data, sig)
|
||||
return false, nil
|
||||
// return k.valShare.PublicKey().Verify(data, sig)
|
||||
}
|
||||
|
||||
// TokenParser returns a token parser that can be used to parse tokens
|
||||
@@ -107,7 +102,6 @@ func (k ucanKeyshare) UCANParser() *ucan.TokenParser {
|
||||
if key == ucan.CapKey {
|
||||
cap = val
|
||||
} else {
|
||||
rsc = ucan.NewStringLengthResource(key, val)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,6 +121,6 @@ func (k ucanKeyshare) UCANParser() *ucan.TokenParser {
|
||||
type customDIDPubKeyResolver struct{}
|
||||
|
||||
// ResolveDIDKey extracts a public key from a did:key string
|
||||
func (customDIDPubKeyResolver) ResolveDIDKey(ctx context.Context, didStr string) (didkey.ID, error) {
|
||||
return didkey.Parse(didStr)
|
||||
func (customDIDPubKeyResolver) ResolveDIDKey(ctx context.Context, didStr string) (keys.DID, error) {
|
||||
return keys.Parse(didStr)
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/types/bech32"
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/onsonr/sonr/crypto/mpc"
|
||||
"github.com/onsonr/sonr/crypto/keys"
|
||||
"github.com/onsonr/sonr/crypto/ucan"
|
||||
)
|
||||
|
||||
@@ -30,9 +30,6 @@ var (
|
||||
)
|
||||
|
||||
type ucanKeyshare struct {
|
||||
userShare *mpc.UserKeyshare
|
||||
valShare *mpc.ValKeyshare
|
||||
|
||||
addr string
|
||||
issuerDID string
|
||||
}
|
||||
@@ -97,20 +94,10 @@ func (k ucanKeyshare) newToken(audienceDID string, prf []Proof, att Attenuations
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ComputeIssuerDID computes the issuer DID from a public key
|
||||
func ComputeIssuerDID(pk []byte) (string, string, error) {
|
||||
addr, err := ComputeSonrAddr(pk)
|
||||
func getIssuerDID(pk keys.PubKey) (string, string, error) {
|
||||
addr, err := bech32.ConvertAndEncode("idx", pk.Bytes())
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return fmt.Sprintf("did:sonr:%s", addr), addr, nil
|
||||
}
|
||||
|
||||
// ComputeSonrAddr computes the Sonr address from a public key
|
||||
func ComputeSonrAddr(pk []byte) (string, error) {
|
||||
sonrAddr, err := bech32.ConvertAndEncode("idx", pk)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return sonrAddr, nil
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
package mpc
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/types/bech32"
|
||||
"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 checkIteratedErrors(aErr, bErr error) error {
|
||||
if aErr == protocol.ErrProtocolFinished && bErr == protocol.ErrProtocolFinished {
|
||||
return nil
|
||||
}
|
||||
if aErr != protocol.ErrProtocolFinished {
|
||||
return aErr
|
||||
}
|
||||
if bErr != protocol.ErrProtocolFinished {
|
||||
return bErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func computeSonrAddr(pp Point) (string, error) {
|
||||
pk := pp.ToAffineCompressed()
|
||||
sonrAddr, err := bech32.ConvertAndEncode("idx", pk)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
func getAlicePubPoint(msg *protocol.Message) (Point, error) {
|
||||
out, err := dklsv1.DecodeAliceDkgResult(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out.PublicKey, nil
|
||||
}
|
||||
|
||||
func getBobOut(msg *protocol.Message) (BobOut, error) {
|
||||
return dklsv1.DecodeBobDkgResult(msg)
|
||||
}
|
||||
|
||||
func getBobPubPoint(msg *protocol.Message) (Point, error) {
|
||||
out, err := dklsv1.DecodeBobDkgResult(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out.PublicKey, nil
|
||||
}
|
||||
|
||||
// getEcdsaPoint builds an elliptic curve point from a compressed byte slice
|
||||
func getEcdsaPoint(pubKey []byte) (*curves.EcPoint, error) {
|
||||
crv := curves.K256()
|
||||
x := new(big.Int).SetBytes(pubKey[1:33])
|
||||
y := new(big.Int).SetBytes(pubKey[33:])
|
||||
ecCurve, err := crv.ToEllipticCurve()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error converting curve: %v", err)
|
||||
}
|
||||
return &curves.EcPoint{X: x, Y: y, Curve: ecCurve}, nil
|
||||
}
|
||||
|
||||
func serializeSignature(sig *curves.EcdsaSignature) ([]byte, error) {
|
||||
if sig == nil {
|
||||
return nil, errors.New("nil signature")
|
||||
}
|
||||
|
||||
rBytes := sig.R.Bytes()
|
||||
sBytes := sig.S.Bytes()
|
||||
|
||||
// Ensure both components are 32 bytes
|
||||
rPadded := make([]byte, 32)
|
||||
sPadded := make([]byte, 32)
|
||||
copy(rPadded[32-len(rBytes):], rBytes)
|
||||
copy(sPadded[32-len(sBytes):], sBytes)
|
||||
|
||||
// Concatenate R and S
|
||||
result := make([]byte, 64)
|
||||
copy(result[0:32], rPadded)
|
||||
copy(result[32:64], sPadded)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func deserializeSignature(sigBytes []byte) (*curves.EcdsaSignature, error) {
|
||||
if len(sigBytes) != 64 {
|
||||
return nil, fmt.Errorf("invalid signature length: expected 64 bytes, got %d", len(sigBytes))
|
||||
}
|
||||
|
||||
r := new(big.Int).SetBytes(sigBytes[:32])
|
||||
s := new(big.Int).SetBytes(sigBytes[32:])
|
||||
|
||||
return &curves.EcdsaSignature{
|
||||
R: r,
|
||||
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)
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
package capability
|
||||
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package resourcetype
|
||||
|
||||
// Resource is a unique identifier for a thing, usually stored state. Resources
|
||||
// are organized by string types
|
||||
type Resource interface {
|
||||
Type() ResourceType
|
||||
Value() string
|
||||
Contains(b Resource) bool
|
||||
}
|
||||
|
||||
type stringResource struct {
|
||||
t ResourceType
|
||||
v string
|
||||
}
|
||||
|
||||
func (r stringResource) Type() ResourceType {
|
||||
return r.t
|
||||
}
|
||||
|
||||
func (r stringResource) Value() string {
|
||||
return r.v
|
||||
}
|
||||
|
||||
func (r stringResource) Contains(b Resource) bool {
|
||||
return r.Type() == b.Type() && len(r.Value()) <= len(b.Value())
|
||||
}
|
||||
|
||||
func NewResource(typ ResourceType, val string) Resource {
|
||||
return stringResource{
|
||||
t: typ,
|
||||
v: val,
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
@@ -1,153 +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.Keyset,
|
||||
) Attenuations {
|
||||
accountAddr, err := mpc.ComputeSonrAddr(kss.User().GetPublicKey())
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
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(),
|
||||
}
|
||||
}
|
||||
+57
-18
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
package didkey
|
||||
|
||||
import (
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/crypto"
|
||||
"github.com/onsonr/sonr/crypto/mpc"
|
||||
)
|
||||
|
||||
func TestID_Parse(t *testing.T) {
|
||||
keyStrED := "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH"
|
||||
id, err := Parse(keyStrED)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if id.String() != keyStrED {
|
||||
t.Errorf("string mismatch.\nwant: %q\ngot: %q", keyStrED, id.String())
|
||||
}
|
||||
|
||||
keyStrRSA := "did:key:z2MGw4gk84USotaWf4AkJ83DcnrfgGaceF86KQXRYMfQ7xqnUFp38UZ6Le8JPfkb4uCLGjHBzKpjEXb9hx9n2ftecQWCHXKtKszkke4FmENdTZ7i9sqRmL3pLnEEJ774r3HMuuC7tNRQ6pqzrxatXx2WinCibdhUmvh3FobnA9ygeqkSGtV6WLa7NVFw9cAvnv8Y6oHcaoZK7fNP4ASGs6AHmSC6ydSR676aKYMe95QmEAj4xJptDsSxG7zLAGzAdwCgm56M4fTno8GdWNmU6Pdghnuf6fWyYus9ASwdfwyaf3SDf4uo5T16PRJssHkQh6DJHfK4Rka7RNQLjzfGBPjFLHbUSvmf4EdbHasbVaveAArD68ZfazRCCvjdovQjWr6uyLCwSAQLPUFZBTT8mW"
|
||||
|
||||
id, err = Parse(keyStrRSA)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if id.String() != keyStrRSA {
|
||||
t.Errorf("string mismatch.\nwant: %q\ngot: %q", keyStrRSA, id.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestID_FromMPCKey(t *testing.T) {
|
||||
// Generate new MPC keyset
|
||||
ks, err := mpc.NewKeyset()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate MPC keyset: %v", err)
|
||||
}
|
||||
|
||||
// Get public key from validator share
|
||||
pubKey := ks.Val().PublicKey()
|
||||
if len(pubKey) != 65 {
|
||||
t.Fatalf("expected 65-byte uncompressed public key, got %d bytes", len(pubKey))
|
||||
}
|
||||
|
||||
// Create crypto.PubKey from raw bytes
|
||||
cryptoPubKey, err := crypto.UnmarshalSecp256k1PublicKey(pubKey)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to unmarshal public key: %v", err)
|
||||
}
|
||||
|
||||
// Create DID Key ID
|
||||
id, err := NewID(cryptoPubKey)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create DID Key ID: %v", err)
|
||||
}
|
||||
log.Printf("%s\n", id.String())
|
||||
|
||||
// Verify the key can be parsed back
|
||||
parsed, err := Parse(id.String())
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse DID Key string: %v", err)
|
||||
}
|
||||
|
||||
// Verify the parsed key matches original
|
||||
if parsed.String() != id.String() {
|
||||
t.Errorf("parsed key doesn't match original.\nwant: %q\ngot: %q",
|
||||
id.String(), parsed.String())
|
||||
}
|
||||
|
||||
// Verify we can get back a valid verify key
|
||||
verifyKey, err := id.VerifyKey()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get verify key: %v", err)
|
||||
}
|
||||
|
||||
// Verify the key is the right type and length
|
||||
rawKey, ok := verifyKey.([]byte)
|
||||
if !ok {
|
||||
t.Fatalf("expected []byte verify key, got %T", verifyKey)
|
||||
}
|
||||
if len(rawKey) != 65 && len(rawKey) != 33 {
|
||||
t.Errorf("invalid key length %d, expected 65 or 33 bytes", len(rawKey))
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
package spec
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/onsonr/sonr/crypto/mpc"
|
||||
)
|
||||
|
||||
// MPCSigningMethod implements the SigningMethod interface for MPC-based signing
|
||||
type MPCSigningMethod struct {
|
||||
Name string
|
||||
ks ucanKeyshare
|
||||
}
|
||||
|
||||
// NewJWTSigningMethod creates a new MPC signing method with the given keyshare source
|
||||
func NewJWTSigningMethod(name string, ks ucanKeyshare) *MPCSigningMethod {
|
||||
return &MPCSigningMethod{
|
||||
Name: name,
|
||||
ks: ks,
|
||||
}
|
||||
}
|
||||
|
||||
// Alg returns the signing method's name
|
||||
func (m *MPCSigningMethod) Alg() string {
|
||||
return m.Name
|
||||
}
|
||||
|
||||
// Verify verifies the signature using the MPC public key
|
||||
func (m *MPCSigningMethod) Verify(signingString, signature string, key interface{}) error {
|
||||
// Decode the signature
|
||||
sig, err := base64.RawURLEncoding.DecodeString(signature)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Hash the signing string
|
||||
hasher := sha256.New()
|
||||
hasher.Write([]byte(signingString))
|
||||
digest := hasher.Sum(nil)
|
||||
|
||||
// Verify using the keyshare's public key
|
||||
valid, err := mpc.VerifySignature(m.ks.valShare.PublicKey(), digest, sig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to verify signature: %w", err)
|
||||
}
|
||||
if !valid {
|
||||
return fmt.Errorf("invalid signature")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sign signs the data using MPC
|
||||
func (m *MPCSigningMethod) Sign(signingString string, key interface{}) (string, error) {
|
||||
// Hash the signing string
|
||||
hasher := sha256.New()
|
||||
hasher.Write([]byte(signingString))
|
||||
digest := hasher.Sum(nil)
|
||||
|
||||
// Create signing functions
|
||||
signFunc, err := m.ks.userShare.SignFunc(digest)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create sign function: %w", err)
|
||||
}
|
||||
|
||||
valSignFunc, err := m.ks.valShare.SignFunc(digest)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create validator sign function: %w", err)
|
||||
}
|
||||
|
||||
// Run the signing protocol
|
||||
sig, err := mpc.ExecuteSigning(valSignFunc, signFunc)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to run sign protocol: %w", err)
|
||||
}
|
||||
|
||||
// Serialize the signature
|
||||
sigBytes, err := mpc.SerializeSignature(sig)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to serialize signature: %w", err)
|
||||
}
|
||||
|
||||
// Encode the signature
|
||||
encoded := base64.RawURLEncoding.EncodeToString(sigBytes)
|
||||
return encoded, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Register the MPC signing method
|
||||
jwt.RegisterSigningMethod("MPC256", func() jwt.SigningMethod {
|
||||
return &MPCSigningMethod{
|
||||
Name: "MPC256",
|
||||
}
|
||||
})
|
||||
}
|
||||
+11
-11
@@ -21,7 +21,7 @@ import (
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/libp2p/go-libp2p/core/crypto"
|
||||
mh "github.com/multiformats/go-multihash"
|
||||
"github.com/onsonr/sonr/crypto/ucan/didkey"
|
||||
"github.com/onsonr/sonr/crypto/keys"
|
||||
)
|
||||
|
||||
// ErrInvalidToken indicates an access token is invalid
|
||||
@@ -47,8 +47,8 @@ const (
|
||||
type Token struct {
|
||||
// Entire UCAN as a signed JWT string
|
||||
Raw string
|
||||
Issuer didkey.ID
|
||||
Audience didkey.ID
|
||||
Issuer keys.DID
|
||||
Audience keys.DID
|
||||
// the "inputs" to this token, a chain UCAN tokens with broader scopes &
|
||||
// deadlines than this token
|
||||
Proofs []Proof `json:"prf,omitempty"`
|
||||
@@ -261,12 +261,12 @@ func (a *pkSource) newToken(audienceDID string, prf []Proof, att Attenuations, f
|
||||
// DIDPubKeyResolver turns did:key Decentralized IDentifiers into a public key,
|
||||
// possibly using a network request
|
||||
type DIDPubKeyResolver interface {
|
||||
ResolveDIDKey(ctx context.Context, did string) (didkey.ID, error)
|
||||
ResolveDIDKey(ctx context.Context, did string) (keys.DID, error)
|
||||
}
|
||||
|
||||
// DIDStringFromPublicKey creates a did:key identifier string from a public key
|
||||
func DIDStringFromPublicKey(pub crypto.PubKey) (string, error) {
|
||||
id, err := didkey.NewID(pub)
|
||||
id, err := keys.NewDID(pub)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -279,8 +279,8 @@ func DIDStringFromPublicKey(pub crypto.PubKey) (string, error) {
|
||||
type StringDIDPubKeyResolver struct{}
|
||||
|
||||
// ResolveDIDKey extracts a public key from a did:key string
|
||||
func (StringDIDPubKeyResolver) ResolveDIDKey(ctx context.Context, didStr string) (didkey.ID, error) {
|
||||
return didkey.Parse(didStr)
|
||||
func (StringDIDPubKeyResolver) ResolveDIDKey(ctx context.Context, didStr string) (keys.DID, error) {
|
||||
return keys.Parse(didStr)
|
||||
}
|
||||
|
||||
// TokenParser parses a raw string into a Token
|
||||
@@ -315,11 +315,11 @@ func (p *TokenParser) parseAndVerify(ctx context.Context, raw string, child *Tok
|
||||
return nil, fmt.Errorf("parser fail")
|
||||
}
|
||||
|
||||
var iss didkey.ID
|
||||
var iss keys.DID
|
||||
// TODO(b5): we're double parsing here b/c the jwt lib we're using doesn't expose
|
||||
// an API (that I know of) for storing parsed issuer / audience
|
||||
if issStr, ok := mc["iss"].(string); ok {
|
||||
iss, err = didkey.Parse(issStr)
|
||||
iss, err = keys.Parse(issStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -327,11 +327,11 @@ func (p *TokenParser) parseAndVerify(ctx context.Context, raw string, child *Tok
|
||||
return nil, fmt.Errorf(`"iss" key is not in claims`)
|
||||
}
|
||||
|
||||
var aud didkey.ID
|
||||
var aud keys.DID
|
||||
// TODO(b5): we're double parsing here b/c the jwt lib we're using doesn't expose
|
||||
// an API (that I know of) for storing parsed issuer / audience
|
||||
if audStr, ok := mc["aud"].(string); ok {
|
||||
aud, err = didkey.Parse(audStr)
|
||||
aud, err = keys.Parse(audStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
## v0.5.27 (2024-12-16)
|
||||
|
||||
## v0.5.26 (2024-12-13)
|
||||
|
||||
### Fix
|
||||
|
||||
- Correct regular expression for version tags in release workflow
|
||||
|
||||
## v0.5.25 (2024-12-11)
|
||||
|
||||
### Feat
|
||||
|
||||
@@ -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
|
||||
```
|
||||
@@ -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:
|
||||
|
||||

|
||||
|
||||
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.
|
||||
+2
-2
@@ -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
|
||||
|
||||
@@ -52,7 +52,7 @@ require (
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.4
|
||||
github.com/bwesterb/go-ristretto v1.2.3
|
||||
github.com/cometbft/cometbft v0.38.12
|
||||
github.com/consensys/gnark-crypto v0.12.1
|
||||
github.com/consensys/gnark-crypto v0.14.0
|
||||
github.com/cosmos/btcutil v1.0.5
|
||||
github.com/cosmos/cosmos-db v1.0.2
|
||||
github.com/cosmos/cosmos-proto v1.0.0-beta.5
|
||||
@@ -74,13 +74,16 @@ require (
|
||||
github.com/ipfs/go-cid v0.4.1
|
||||
github.com/ipfs/kubo v0.32.1
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/labstack/echo/v4 v4.10.2
|
||||
github.com/labstack/echo-contrib v0.17.1
|
||||
github.com/labstack/echo/v4 v4.12.0
|
||||
github.com/libp2p/go-libp2p v0.37.2
|
||||
github.com/medama-io/go-useragent v1.0.1
|
||||
github.com/mr-tron/base58 v1.2.0
|
||||
github.com/multiformats/go-multibase v0.2.0
|
||||
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
|
||||
@@ -91,13 +94,10 @@ require (
|
||||
github.com/strangelove-ventures/poa v0.50.0
|
||||
github.com/strangelove-ventures/tokenfactory v0.50.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
golang.org/x/crypto v0.30.0
|
||||
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d
|
||||
golang.org/x/crypto v0.31.0
|
||||
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/sqlite v1.5.6
|
||||
gorm.io/gorm v1.25.12
|
||||
lukechampine.com/blake3 v1.3.0
|
||||
)
|
||||
|
||||
@@ -116,9 +116,10 @@ require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
|
||||
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.13.0 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.19.1 // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/btcsuite/btcd v0.20.1-beta // indirect
|
||||
github.com/boyter/go-string v1.0.5 // indirect
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect
|
||||
github.com/caddyserver/certmagic v0.21.4 // indirect
|
||||
github.com/caddyserver/zerossl v0.1.3 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
@@ -133,7 +134,7 @@ require (
|
||||
github.com/cockroachdb/redact v1.1.5 // indirect
|
||||
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
|
||||
github.com/cometbft/cometbft-db v0.11.0 // indirect
|
||||
github.com/consensys/bavard v0.1.13 // indirect
|
||||
github.com/consensys/bavard v0.1.24 // indirect
|
||||
github.com/cosmos/go-bip39 v1.0.0 // indirect
|
||||
github.com/cosmos/gogogateway v1.2.0 // indirect
|
||||
github.com/cosmos/iavl v1.1.2 // indirect
|
||||
@@ -145,6 +146,7 @@ require (
|
||||
github.com/danieljoos/wincred v1.1.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
|
||||
github.com/decred/dcrd/crypto/blake256 v1.1.0 // indirect
|
||||
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
|
||||
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.1 // indirect
|
||||
@@ -152,7 +154,7 @@ require (
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/dvsekhvalnov/jose2go v1.6.0 // indirect
|
||||
github.com/emicklei/dot v1.6.1 // indirect
|
||||
github.com/ethereum/go-ethereum v1.14.6 // indirect
|
||||
github.com/ethereum/go-ethereum v1.14.12 // indirect
|
||||
github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
@@ -233,8 +235,6 @@ require (
|
||||
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
|
||||
@@ -242,7 +242,7 @@ require (
|
||||
github.com/koron/go-ssdp v0.0.4 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/labstack/gommon v0.4.0 // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
github.com/lib/pq v1.10.7 // indirect
|
||||
github.com/libdns/libdns v0.2.2 // indirect
|
||||
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
|
||||
@@ -261,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
|
||||
@@ -278,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
|
||||
@@ -325,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
|
||||
@@ -353,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
|
||||
|
||||
@@ -885,7 +885,6 @@ github.com/aclements/go-gg v0.0.0-20170118225347-6dbb4e4fefb0/go.mod h1:55qNq4vc
|
||||
github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794/go.mod h1:7e+I0LQFUI9AXWxOfsQROs9xPhoJtbsyWcjJqDd4KPY=
|
||||
github.com/adlio/schema v1.3.3 h1:oBJn8I02PyTB466pZO1UZEn1TV5XLlifBSyMrmHl/1I=
|
||||
github.com/adlio/schema v1.3.3/go.mod h1:1EsRssiv9/Ce2CMzq5DoL7RiMshhuigQxrR4DMV9fHg=
|
||||
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY=
|
||||
github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk=
|
||||
@@ -958,30 +957,25 @@ github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2
|
||||
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
||||
github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
||||
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
|
||||
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/bits-and-blooms/bitset v1.19.1 h1:mv2yVhy96D2CuskLPXnc58oJNMs5PCWjAZuyYU0p12M=
|
||||
github.com/bits-and-blooms/bitset v1.19.1/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c=
|
||||
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
||||
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/boyter/go-string v1.0.5 h1:/xcOlWdgelLYLVkUU0xBLfioGjZ9KIMUMI/RXG138YY=
|
||||
github.com/boyter/go-string v1.0.5/go.mod h1:Mww9cDld2S2cdJ0tQffBhsZFMQRA2OJdcjWYZXvZ4Ss=
|
||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||
github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
|
||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.6 h1:zFL2+c3Lb9gEgqKNzowKUPQNb8jV7v5Oaodi/AYFd6c=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.6/go.mod h1:9dFymx8HpuLqBnsPELrImQeTQfKBQqzqGbbV3jK55aE=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
|
||||
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
|
||||
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
|
||||
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
||||
github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY=
|
||||
github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE=
|
||||
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||
@@ -1073,12 +1067,14 @@ github.com/cometbft/cometbft v0.38.12/go.mod h1:GPHp3/pehPqgX1930HmK1BpBLZPxB75v
|
||||
github.com/cometbft/cometbft-db v0.11.0 h1:M3Lscmpogx5NTbb1EGyGDaFRdsoLWrUWimFEyf7jej8=
|
||||
github.com/cometbft/cometbft-db v0.11.0/go.mod h1:GDPJAC/iFHNjmZZPN8V8C1yr/eyityhi2W1hz2MGKSc=
|
||||
github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ=
|
||||
github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ=
|
||||
github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI=
|
||||
github.com/consensys/bavard v0.1.24 h1:Lfe+bjYbpaoT7K5JTFoMi5wo9V4REGLvQQbHmatoN2I=
|
||||
github.com/consensys/bavard v0.1.24/go.mod h1:k/zVjHHC4B+PQy1Pg7fgvG3ALicQw540Crag8qx+dZs=
|
||||
github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q=
|
||||
github.com/consensys/gnark-crypto v0.10.0/go.mod h1:Iq/P3HHl0ElSjsg2E1gsMwhAyxnxoKK5nVyZKd+/KhU=
|
||||
github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M=
|
||||
github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY=
|
||||
github.com/consensys/gnark-crypto v0.14.0 h1:DDBdl4HaBtdQsq/wfMwJvZNE80sHidrK3Nfrefatm0E=
|
||||
github.com/consensys/gnark-crypto v0.14.0/go.mod h1:CU4UijNPsHawiVGNxe9co07FkzCeWHHrb1li/n1XoU0=
|
||||
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
|
||||
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
|
||||
github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg=
|
||||
@@ -1142,7 +1138,6 @@ github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV
|
||||
github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
|
||||
github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
|
||||
github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
|
||||
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
@@ -1152,8 +1147,9 @@ github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6Uh
|
||||
github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo=
|
||||
github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
|
||||
@@ -1232,8 +1228,8 @@ github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHj
|
||||
github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
|
||||
github.com/ethereum/go-ethereum v1.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg=
|
||||
github.com/ethereum/go-ethereum v1.13.5/go.mod h1:yMTu38GSuyxaYzQMViqNmQ1s3cE84abZexQmTgenWk0=
|
||||
github.com/ethereum/go-ethereum v1.14.6 h1:ZTxnErSopkDyxdvB8zW/KcK+/AVrdil/TzoWXVKaaC8=
|
||||
github.com/ethereum/go-ethereum v1.14.6/go.mod h1:hglUZo/5pVIYXNyYjWzsAUDpT/zI+WbWo/Nih7ot+G0=
|
||||
github.com/ethereum/go-ethereum v1.14.12 h1:8hl57x77HSUo+cXExrURjU/w1VhL+ShCTJrTwcCQSe4=
|
||||
github.com/ethereum/go-ethereum v1.14.12/go.mod h1:RAC2gVMWJ6FkxSPESfbshrcKpIokgQKsVKmAuqdekDY=
|
||||
github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 h1:BBso6MBKW8ncyZLv37o+KNyy0HrrHgfnOaGQC2qvN+A=
|
||||
github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5/go.mod h1:JpoxHjuQauoxiFMl1ie8Xc/7TfLuMZ5eOCONd1sUBHg=
|
||||
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
|
||||
@@ -1780,13 +1776,8 @@ github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZl
|
||||
github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU=
|
||||
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267/go.mod h1:h1nSAbGFqGVzn6Jyl1R/iCcBUHN4g+gW1u9CoBTrb9E=
|
||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
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=
|
||||
@@ -1796,7 +1787,6 @@ github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
@@ -1823,7 +1813,6 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:C
|
||||
github.com/kilic/bls12-381 v0.1.0/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||
github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE=
|
||||
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
@@ -1863,15 +1852,18 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v0.0.0-20170224010052-a616ab194758/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/labstack/echo-contrib v0.17.1 h1:7I/he7ylVKsDUieaGRZ9XxxTYOjfQwVzHzUYrNykfCU=
|
||||
github.com/labstack/echo-contrib v0.17.1/go.mod h1:SnsCZtwHBAZm5uBSAtQtXQHI3wqEA73hvTn0bYMKnZA=
|
||||
github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=
|
||||
github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
|
||||
github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M=
|
||||
github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
|
||||
github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0=
|
||||
github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM=
|
||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
||||
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
|
||||
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||
github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
|
||||
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
|
||||
github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
|
||||
github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4=
|
||||
github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
|
||||
@@ -1942,7 +1934,6 @@ github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope
|
||||
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
@@ -1968,13 +1959,13 @@ 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=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
|
||||
github.com/medama-io/go-useragent v1.0.1 h1:staHGaZKIRpGI7sPVnyjYVT0SMRlQmp0L21rKYusi8Y=
|
||||
github.com/medama-io/go-useragent v1.0.1/go.mod h1:H9GYWth4IN8vAFZh5LeARza7VwM4jK9uk7Tb9huVzLw=
|
||||
github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg=
|
||||
github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ=
|
||||
github.com/mholt/acmez/v2 v2.0.3 h1:CgDBlEwg3QBp6s45tPQmFIBrkRIkBT4rW4orMM6p4sw=
|
||||
@@ -2068,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=
|
||||
@@ -2081,7 +2076,6 @@ github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DV
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
|
||||
@@ -2090,7 +2084,6 @@ github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
|
||||
github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8=
|
||||
@@ -2387,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=
|
||||
@@ -2559,7 +2554,6 @@ go4.org v0.0.0-20200411211856-f5505b9728dd/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZM
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc=
|
||||
go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU=
|
||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
@@ -2597,8 +2591,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY=
|
||||
golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -2910,7 +2904,6 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -3484,10 +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/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=
|
||||
|
||||
@@ -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
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated from Pkl module `sonr.conf.Hway`. DO NOT EDIT.
|
||||
// Code generated from Pkl module `sonr.net.Hway`. DO NOT EDIT.
|
||||
package hway
|
||||
|
||||
import (
|
||||
@@ -10,8 +10,6 @@ import (
|
||||
type Hway interface {
|
||||
GetServePort() int
|
||||
|
||||
GetConfigDir() string
|
||||
|
||||
GetSqliteFile() string
|
||||
|
||||
GetChainId() string
|
||||
@@ -23,6 +21,8 @@ type Hway interface {
|
||||
GetSonrGrpcUrl() string
|
||||
|
||||
GetSonrRpcUrl() string
|
||||
|
||||
GetPsqlDSN() string
|
||||
}
|
||||
|
||||
var _ Hway = (*HwayImpl)(nil)
|
||||
@@ -30,8 +30,6 @@ var _ Hway = (*HwayImpl)(nil)
|
||||
type HwayImpl struct {
|
||||
ServePort int `pkl:"servePort"`
|
||||
|
||||
ConfigDir string `pkl:"configDir"`
|
||||
|
||||
SqliteFile string `pkl:"sqliteFile"`
|
||||
|
||||
ChainId string `pkl:"chainId"`
|
||||
@@ -43,16 +41,14 @@ type HwayImpl struct {
|
||||
SonrGrpcUrl string `pkl:"sonrGrpcUrl"`
|
||||
|
||||
SonrRpcUrl string `pkl:"sonrRpcUrl"`
|
||||
|
||||
PsqlDSN string `pkl:"psqlDSN"`
|
||||
}
|
||||
|
||||
func (rcv *HwayImpl) GetServePort() int {
|
||||
return rcv.ServePort
|
||||
}
|
||||
|
||||
func (rcv *HwayImpl) GetConfigDir() string {
|
||||
return rcv.ConfigDir
|
||||
}
|
||||
|
||||
func (rcv *HwayImpl) GetSqliteFile() string {
|
||||
return rcv.SqliteFile
|
||||
}
|
||||
@@ -77,6 +73,10 @@ func (rcv *HwayImpl) GetSonrRpcUrl() string {
|
||||
return rcv.SonrRpcUrl
|
||||
}
|
||||
|
||||
func (rcv *HwayImpl) GetPsqlDSN() string {
|
||||
return rcv.PsqlDSN
|
||||
}
|
||||
|
||||
// LoadFromPath loads the pkl module at the given path and evaluates it into a Hway
|
||||
func LoadFromPath(ctx context.Context, path string) (ret Hway, err error) {
|
||||
evaluator, err := pkl.NewEvaluator(ctx, pkl.PreconfiguredOptions)
|
||||
@@ -0,0 +1,8 @@
|
||||
// Code generated from Pkl module `sonr.net.Hway`. DO NOT EDIT.
|
||||
package hway
|
||||
|
||||
import "github.com/apple/pkl-go/pkl"
|
||||
|
||||
func init() {
|
||||
pkl.RegisterMapping("sonr.net.Hway", HwayImpl{})
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated from Pkl module `sonr.conf.Motr`. DO NOT EDIT.
|
||||
// Code generated from Pkl module `sonr.net.Motr`. DO NOT EDIT.
|
||||
package motr
|
||||
|
||||
type Config struct {
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated from Pkl module `sonr.conf.Motr`. DO NOT EDIT.
|
||||
// Code generated from Pkl module `sonr.net.Motr`. DO NOT EDIT.
|
||||
package motr
|
||||
|
||||
type Environment struct {
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated from Pkl module `sonr.conf.Motr`. DO NOT EDIT.
|
||||
// Code generated from Pkl module `sonr.net.Motr`. DO NOT EDIT.
|
||||
package motr
|
||||
|
||||
import (
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated from Pkl module `sonr.conf.Motr`. DO NOT EDIT.
|
||||
// Code generated from Pkl module `sonr.net.Motr`. DO NOT EDIT.
|
||||
package motr
|
||||
|
||||
type Schema struct {
|
||||
@@ -0,0 +1,11 @@
|
||||
// Code generated from Pkl module `sonr.net.Motr`. DO NOT EDIT.
|
||||
package motr
|
||||
|
||||
import "github.com/apple/pkl-go/pkl"
|
||||
|
||||
func init() {
|
||||
pkl.RegisterMapping("sonr.net.Motr", Motr{})
|
||||
pkl.RegisterMapping("sonr.net.Motr#Config", Config{})
|
||||
pkl.RegisterMapping("sonr.net.Motr#Schema", Schema{})
|
||||
pkl.RegisterMapping("sonr.net.Motr#Environment", Environment{})
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user