From 21b0318a9b037e7f04b61887eea44528bc8b4baf Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 10 Oct 2025 14:17:04 -0300 Subject: [PATCH 01/10] Add cubesigner keychain implementation --- evm/evm.go | 5 +- evm/evm_test.go | 10 + go.mod | 116 +++--- go.sum | 248 +++++++------ keychain/cubesigner/cubesigner_client.go | 25 ++ keychain/cubesigner/cubesigner_keychain.go | 273 ++++++++++++++ .../cubesigner/cubesigner_keychain_test.go | 335 ++++++++++++++++++ .../cubesignermock/cubesigner_client.go | 97 +++++ keychain/cubesigner/mocks_generate_test.go | 6 + 9 files changed, 964 insertions(+), 151 deletions(-) create mode 100644 keychain/cubesigner/cubesigner_client.go create mode 100644 keychain/cubesigner/cubesigner_keychain.go create mode 100644 keychain/cubesigner/cubesigner_keychain_test.go create mode 100644 keychain/cubesigner/cubesignermock/cubesigner_client.go create mode 100644 keychain/cubesigner/mocks_generate_test.go diff --git a/evm/evm.go b/evm/evm.go index 1e8f3f7..e647c5a 100644 --- a/evm/evm.go +++ b/evm/evm.go @@ -12,6 +12,7 @@ import ( "strings" "time" + "github.com/ava-labs/avalanchego/vms/evm/predicate" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/crypto" @@ -20,7 +21,6 @@ import ( "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/plugin/evm/upgrade/legacy" "github.com/ava-labs/subnet-evm/precompile/contracts/warp" - "github.com/ava-labs/subnet-evm/predicate" "github.com/ava-labs/avalanche-tooling-sdk-go/constants" "github.com/ava-labs/avalanche-tooling-sdk-go/utils" @@ -28,7 +28,6 @@ import ( avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" ethereum "github.com/ava-labs/libevm" ethparams "github.com/ava-labs/libevm/params" - subnetEvmUtils "github.com/ava-labs/subnet-evm/utils" ) const ( @@ -531,7 +530,7 @@ func (client Client) TransactWithWarpMessage( accessList := types.AccessList{ types.AccessTuple{ Address: warp.ContractAddress, - StorageKeys: subnetEvmUtils.BytesToHashSlice(predicate.PackPredicate(warpMessage.Bytes())), + StorageKeys: predicate.New(warpMessage.Bytes()), }, } msg := ethereum.CallMsg{ diff --git a/evm/evm_test.go b/evm/evm_test.go index 1e2365b..a7996f3 100644 --- a/evm/evm_test.go +++ b/evm/evm_test.go @@ -2077,6 +2077,11 @@ func TestSetupProposerVM(t *testing.T) { // GetChainID mockClient.EXPECT().ChainID(gomock.Any()). Return(chainID, nil) + // Setup - initial BlockNumber and NonceAt calls + mockClient.EXPECT().BlockNumber(gomock.Any()). + Return(uint64(1000), nil) + mockClient.EXPECT().NonceAt(gomock.Any(), address, gomock.Any()). + Return(uint64(0), nil) // First block mockClient.EXPECT().BlockNumber(gomock.Any()). Return(uint64(1000), nil) @@ -2153,6 +2158,11 @@ func TestSetupProposerVM(t *testing.T) { // GetChainID succeeds mockClient.EXPECT().ChainID(gomock.Any()). Return(chainID, nil) + // Setup - initial BlockNumber and NonceAt calls + mockClient.EXPECT().BlockNumber(gomock.Any()). + Return(uint64(1000), nil) + mockClient.EXPECT().NonceAt(gomock.Any(), address, gomock.Any()). + Return(uint64(0), nil) // First block - error sending transaction mockClient.EXPECT().BlockNumber(gomock.Any()). Return(uint64(1000), nil) diff --git a/go.mod b/go.mod index 7bdc769..4194358 100644 --- a/go.mod +++ b/go.mod @@ -1,35 +1,54 @@ module github.com/ava-labs/avalanche-tooling-sdk-go -go 1.23.11 - -toolchain go1.24.7 +go 1.24.8 require ( - github.com/ava-labs/avalanchego v1.13.3-0.20250701190537-839ace23368a + github.com/ava-labs/avalanchego v1.13.6-0.20251010155017-cd3e6bee30ab github.com/ava-labs/icm-services v1.4.1-0.20250729175537-a74c15880023 - github.com/ava-labs/libevm v1.13.14-0.3.0.rc.1 - github.com/ava-labs/subnet-evm v0.7.5 + github.com/ava-labs/libevm v1.13.15-0.20251002164226-35926db4d661 + github.com/ava-labs/subnet-evm v0.7.10-0.20251010143515-0e03d5e4c9c9 github.com/cavaliergopher/grab/v3 v3.0.1 - github.com/stretchr/testify v1.10.0 + github.com/cubist-labs/cubesigner-go-sdk v0.0.16 + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 + github.com/stretchr/testify v1.11.1 go.uber.org/mock v0.5.2 go.uber.org/zap v1.27.0 golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 ) require ( + connectrpc.com/connect v1.18.1 // indirect + connectrpc.com/grpcreflect v1.3.0 // indirect github.com/DataDog/zstd v1.5.2 // indirect github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e // indirect github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/StephenButtolph/canoto v0.15.0 // indirect + github.com/StephenButtolph/canoto v0.17.2 // indirect github.com/VictoriaMetrics/fastcache v1.12.1 // indirect - github.com/ava-labs/coreth v0.15.2-rc.0.0.20250620163936-0058a7ec6d9d // indirect - github.com/ava-labs/ledger-avalanche/go v0.0.0-20241009183145-e6f90a8a1a60 // indirect + github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect + github.com/ava-labs/coreth v0.15.4-rc.3.0.20251002221438-a857a64c28ea // indirect + github.com/ava-labs/firewood-go-ethhash/ffi v0.0.12 // indirect + github.com/ava-labs/ledger-avalanche-go v1.1.0 // indirect + github.com/aws/aws-sdk-go-v2 v1.37.0 // indirect + github.com/aws/aws-sdk-go-v2/config v1.29.18 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.71 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.33 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.0 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.0 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.18 // indirect + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.19 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.25.6 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.4 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.34.1 // indirect + github.com/aws/smithy-go v1.22.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.10.0 // indirect - github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.5 // indirect github.com/btcsuite/btcd/btcutil v1.1.3 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cenkalti/backoff/v5 v5.0.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cockroachdb/errors v1.9.1 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect @@ -39,18 +58,17 @@ require ( github.com/consensys/bavard v0.1.13 // indirect github.com/consensys/gnark-crypto v0.12.1 // indirect github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 // indirect - github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect + github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/deckarep/golang-set/v2 v2.1.0 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect - github.com/ethereum/c-kzg-4844 v0.4.0 // indirect - github.com/fsnotify/fsnotify v1.8.0 // indirect + github.com/ethereum/c-kzg-4844 v1.0.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 // indirect - github.com/getsentry/sentry-go v0.18.0 // indirect + github.com/getsentry/sentry-go v0.35.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect - github.com/go-viper/mapstructure/v2 v2.3.0 // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/gofrs/flock v0.12.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect @@ -61,7 +79,7 @@ require ( github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/rpc v1.2.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect github.com/holiman/uint256 v1.2.4 // indirect @@ -72,57 +90,59 @@ require ( github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/mr-tron/base58 v1.2.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/oapi-codegen/runtime v1.1.1 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pingcap/errors v0.11.4 // indirect github.com/pires/go-proxyproto v0.6.2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.22.0 // indirect - github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.62.0 // indirect - github.com/prometheus/procfs v0.15.1 // indirect + github.com/prometheus/client_golang v1.23.0 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/procfs v0.16.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rs/cors v1.11.1 // indirect - github.com/sagikazarmark/locafero v0.7.0 // indirect + github.com/sagikazarmark/locafero v0.9.0 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/sourcegraph/conc v0.3.0 // indirect - github.com/spf13/afero v1.12.0 // indirect - github.com/spf13/cast v1.7.1 // indirect + github.com/spf13/afero v1.14.0 // indirect + github.com/spf13/cast v1.9.2 // indirect github.com/spf13/pflag v1.0.7 // indirect github.com/spf13/viper v1.20.1 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/supranational/blst v0.3.14 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect - github.com/tklauser/go-sysconf v0.3.12 // indirect - github.com/tklauser/numcpus v0.6.1 // indirect + github.com/tklauser/go-sysconf v0.3.15 // indirect + github.com/tklauser/numcpus v0.10.0 // indirect github.com/tyler-smith/go-bip32 v1.0.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect + github.com/zondax/golem v0.27.0 // indirect github.com/zondax/hid v0.9.2 // indirect - github.com/zondax/ledger-go v1.0.0 // indirect + github.com/zondax/ledger-go v1.0.1 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/otel v1.36.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 // indirect + go.opentelemetry.io/otel v1.37.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 // indirect - go.opentelemetry.io/otel/metric v1.36.0 // indirect - go.opentelemetry.io/otel/sdk v1.36.0 // indirect - go.opentelemetry.io/otel/trace v1.36.0 // indirect - go.opentelemetry.io/proto/otlp v1.6.0 // indirect + go.opentelemetry.io/otel/metric v1.37.0 // indirect + go.opentelemetry.io/otel/sdk v1.37.0 // indirect + go.opentelemetry.io/otel/trace v1.37.0 // indirect + go.opentelemetry.io/proto/otlp v1.7.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.39.0 // indirect - golang.org/x/net v0.41.0 // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.33.0 // indirect - golang.org/x/term v0.32.0 // indirect - golang.org/x/text v0.26.0 // indirect - golang.org/x/time v0.10.0 // indirect - gonum.org/v1/gonum v0.11.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect - google.golang.org/grpc v1.74.2 // indirect - google.golang.org/protobuf v1.36.6 // indirect + golang.org/x/crypto v0.42.0 // indirect + golang.org/x/net v0.44.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/term v0.35.0 // indirect + golang.org/x/text v0.29.0 // indirect + golang.org/x/time v0.12.0 // indirect + gonum.org/v1/gonum v0.16.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c // indirect + google.golang.org/grpc v1.75.0 // indirect + google.golang.org/protobuf v1.36.8 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect diff --git a/go.sum b/go.sum index 6befc49..22a5d08 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,12 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw= +connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= +connectrpc.com/grpcreflect v1.3.0 h1:Y4V+ACf8/vOb1XOc251Qun7jMB75gCUNw6llvB9csXc= +connectrpc.com/grpcreflect v1.3.0/go.mod h1:nfloOtCS8VUQOQ1+GTdFzVg2CJo4ZGaat8JIovCtDYs= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= -github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= +github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= @@ -14,48 +18,83 @@ github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec/go.mod h1 github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= -github.com/StephenButtolph/canoto v0.15.0 h1:3iGdyTSQZ7/y09WaJCe0O/HIi53ZyTrnmVzfCqt64mM= -github.com/StephenButtolph/canoto v0.15.0/go.mod h1:IcnAHC6nJUfQFVR9y60ko2ecUqqHHSB6UwI9NnBFZnE= +github.com/StephenButtolph/canoto v0.17.2 h1:kRLJwtYk0bzdGEeEvwHaVmmDm0HFHxrS0VlVN5Hyo7U= +github.com/StephenButtolph/canoto v0.17.2/go.mod h1:IcnAHC6nJUfQFVR9y60ko2ecUqqHHSB6UwI9NnBFZnE= github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= 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/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/ava-labs/avalanchego v1.13.3-0.20250701190537-839ace23368a h1:mLzIwC2BZXqZiFEiBB2myEAhU9dfYroQ6pNYikttrYw= -github.com/ava-labs/avalanchego v1.13.3-0.20250701190537-839ace23368a/go.mod h1:s7W/kim5L6hiD2PB1v/Ozy1ZZyoLQ4H6mxVO0aMnxng= -github.com/ava-labs/coreth v0.15.2-rc.0.0.20250620163936-0058a7ec6d9d h1:nZA7nEhlf2uWM1doZz4WFiMkZVZ0iH+c3IebokIO1sk= -github.com/ava-labs/coreth v0.15.2-rc.0.0.20250620163936-0058a7ec6d9d/go.mod h1:Mk5g2ZI9lEbxA67qikOtUrq71Yr389B34S+Ddsa51K4= +github.com/ava-labs/avalanchego v1.13.6-0.20251010155017-cd3e6bee30ab h1:lS6N4VvIP6ihFWwP7ytGF9+AhCJoaVcsHNLg/LJD/Hw= +github.com/ava-labs/avalanchego v1.13.6-0.20251010155017-cd3e6bee30ab/go.mod h1:EL0MGbL2liE9jp4QtCHR2thkNl8hCkD26DJ+7cmcaqs= +github.com/ava-labs/coreth v0.15.4-rc.3.0.20251002221438-a857a64c28ea h1:vrHUSx6hlQgdVufhtT9LT9i7eHZcWmBEjH9cBozDLuc= +github.com/ava-labs/coreth v0.15.4-rc.3.0.20251002221438-a857a64c28ea/go.mod h1:y/14LplmA0lLwIDlKiGAZ8OlxQ7OxhaU2dfkYcviLPM= +github.com/ava-labs/firewood-go-ethhash/ffi v0.0.12 h1:aMcrLbpJ/dyu2kZDf/Di/4JIWsUcYPyTDKymiHpejt0= +github.com/ava-labs/firewood-go-ethhash/ffi v0.0.12/go.mod h1:cq89ua3iiZ5wPBALTEQS5eG8DIZcs7ov6OiL4YR1BVY= github.com/ava-labs/icm-services v1.4.1-0.20250729175537-a74c15880023 h1:6WaIMdc35U7mNOmWJAO0lthvsM8e38Jl+mq/RiUopsU= github.com/ava-labs/icm-services v1.4.1-0.20250729175537-a74c15880023/go.mod h1:17BcQ5NzeHSo80tIKaiP8MsxR7UJTeSQNmF3+cocXe8= -github.com/ava-labs/ledger-avalanche/go v0.0.0-20241009183145-e6f90a8a1a60 h1:EL66gtXOAwR/4KYBjOV03LTWgkEXvLePribLlJNu4g0= -github.com/ava-labs/ledger-avalanche/go v0.0.0-20241009183145-e6f90a8a1a60/go.mod h1:/7qKobTfbzBu7eSTVaXMTr56yTYk4j2Px6/8G+idxHo= -github.com/ava-labs/libevm v1.13.14-0.3.0.rc.1 h1:vBMYo+Iazw0rGTr+cwjkBdh5eadLPlv4ywI4lKye3CA= -github.com/ava-labs/libevm v1.13.14-0.3.0.rc.1/go.mod h1:+Iol+sVQ1KyoBsHf3veyrBmHCXr3xXRWq6ZXkgVfNLU= -github.com/ava-labs/subnet-evm v0.7.5 h1:2lEGhTLR4nirTD5031dIJUJVC1FfCSF6ihz22TbJDug= -github.com/ava-labs/subnet-evm v0.7.5/go.mod h1:fR6+mKlylACo0O47kY4VptfEeLYVcshWgVtiiR/0kOE= +github.com/ava-labs/ledger-avalanche-go v1.1.0 h1:OkscKtb/gX20HBt8RyAtwXLrQnCEls5SzWGieE7NoNM= +github.com/ava-labs/ledger-avalanche-go v1.1.0/go.mod h1:mAlG9ptnPjvNoLGLHXnM3slGY8ewvBJtJNVTEjG8KvI= +github.com/ava-labs/libevm v1.13.15-0.20251002164226-35926db4d661 h1:lt4yQE1HMvxWrdD5RFj+h9kWUsZK2rmNohvkeQsbG9M= +github.com/ava-labs/libevm v1.13.15-0.20251002164226-35926db4d661/go.mod h1:ivRC/KojP8sai7j8WnpXIReQpcRklL2bIzoysnjpARQ= +github.com/ava-labs/subnet-evm v0.7.10-0.20251010143515-0e03d5e4c9c9 h1:qRKllyK9es8t8u/Q8QLPtvQiHmL1Ar2f60VYw4a3HMo= +github.com/ava-labs/subnet-evm v0.7.10-0.20251010143515-0e03d5e4c9c9/go.mod h1:lcCfer4eo/Po0uVaTsxGFNMpYYpMBYAdKysaeVTJDFc= +github.com/aws/aws-sdk-go-v2 v1.37.0 h1:YtCOESR/pN4j5oA7cVHSfOwIcuh/KwHC4DOSXFbv5F0= +github.com/aws/aws-sdk-go-v2 v1.37.0/go.mod h1:9Q0OoGQoboYIAJyslFyF1f5K1Ryddop8gqMhWx/n4Wg= +github.com/aws/aws-sdk-go-v2/config v1.29.18 h1:x4T1GRPnqKV8HMJOMtNktbpQMl3bIsfx8KbqmveUO2I= +github.com/aws/aws-sdk-go-v2/config v1.29.18/go.mod h1:bvz8oXugIsH8K7HLhBv06vDqnFv3NsGDt2Znpk7zmOU= +github.com/aws/aws-sdk-go-v2/credentials v1.17.71 h1:r2w4mQWnrTMJjOyIsZtGp3R3XGY3nqHn8C26C2lQWgA= +github.com/aws/aws-sdk-go-v2/credentials v1.17.71/go.mod h1:E7VF3acIup4GB5ckzbKFrCK0vTvEQxOxgdq4U3vcMCY= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.33 h1:D9ixiWSG4lyUBL2DDNK924Px9V/NBVpML90MHqyTADY= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.33/go.mod h1:caS/m4DI+cij2paz3rtProRBI4s/+TCiWoaWZuQ9010= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.0 h1:H2iZoqW/v2Jnrh1FnU725Bq6KJ0k2uP63yH+DcY+HUI= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.0/go.mod h1:L0FqLbwMXHvNC/7crWV1iIxUlOKYZUE8KuTIA+TozAI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.0 h1:EDped/rNzAhFPhVY0sDGbtD16OKqksfA8OjF/kLEgw8= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.0/go.mod h1:uUI335jvzpZRPpjYx6ODc/wg1qH+NnoSTK/FwVeK0C0= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4 h1:CXV68E2dNqhuynZJPB80bhPQwAKqBWVer887figW6Jc= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4/go.mod h1:/xFi9KtvBXP97ppCz1TAEvU1Uf66qvid89rbem3wCzQ= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.18 h1:vvbXsA2TVO80/KT7ZqCbx934dt6PY+vQ8hZpUZ/cpYg= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.18/go.mod h1:m2JJHledjBGNMsLOF1g9gbAxprzq3KjC8e4lxtn+eWg= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.19 h1:O2xbipq7k1kTct69V7mFidwTagld9c/6iyK+3yo+QNg= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.19/go.mod h1:CxTOwBy2Qs8/+yV7fkz4eZB1RB5qeWaW9SvznvFLgRA= +github.com/aws/aws-sdk-go-v2/service/sso v1.25.6 h1:rGtWqkQbPk7Bkwuv3NzpE/scwwL9sC1Ul3tn9x83DUI= +github.com/aws/aws-sdk-go-v2/service/sso v1.25.6/go.mod h1:u4ku9OLv4TO4bCPdxf4fA1upaMaJmP9ZijGk3AAOC6Q= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.4 h1:OV/pxyXh+eMA0TExHEC4jyWdumLxNbzz1P0zJoezkJc= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.4/go.mod h1:8Mm5VGYwtm+r305FfPSuc+aFkrypeylGYhFim6XEPoc= +github.com/aws/aws-sdk-go-v2/service/sts v1.34.1 h1:aUrLQwJfZtwv3/ZNG2xRtEen+NqI3iesuacjP51Mv1s= +github.com/aws/aws-sdk-go-v2/service/sts v1.34.1/go.mod h1:3wFBZKoWnX3r+Sm7in79i54fBmNfwhdNdQuscCw7QIk= +github.com/aws/smithy-go v1.22.5 h1:P9ATCXPMb2mPjYBgueqJNCA5S9UfktsW0tTxi+a7eqw= +github.com/aws/smithy-go v1.22.5/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= github.com/btcsuite/btcd v0.23.0 h1:V2/ZgjfDFIygAX3ZapeigkVBoVUtOJKSwrhZdlpSvaA= github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= -github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= -github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcec/v2 v2.3.5 h1:dpAlnAwmT1yIBm3exhT1/8iUSD98RDJM5vqJVQDQLiU= +github.com/btcsuite/btcd/btcec/v2 v2.3.5/go.mod h1:m22FrOAiuxl/tht9wIqAoGHcbnCCaPWyauO8y2LGGtQ= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +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/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= @@ -69,6 +108,8 @@ github.com/cavaliergopher/grab/v3 v3.0.1 h1:4z7TkBfmPjmLAAmkkAZNX/6QJ1nNFdv3SdIH github.com/cavaliergopher/grab/v3 v3.0.1/go.mod h1:1U/KNnD+Ft6JJiYoYBAimKH2XrYptb8Kl3DFGmsjpq4= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= +github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= @@ -110,9 +151,11 @@ github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3 github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= -github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= -github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= +github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI= +github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cubist-labs/cubesigner-go-sdk v0.0.16 h1:X7kPk4BhVggw/XtKXPJcT1kgnV0v70nIdqn+wAnJgqU= +github.com/cubist-labs/cubesigner-go-sdk v0.0.16/go.mod h1:aSYPLNkIt10+QQiNI4ctE+wQ1IoJdUqlUlLCmwYXk2w= 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= @@ -120,11 +163,12 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= 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 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +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.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= @@ -135,8 +179,8 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= -github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= -github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= +github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -144,16 +188,16 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= -github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= -github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= -github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ= +github.com/getsentry/sentry-go v0.35.0 h1:+FJNlnjJsZMG3g0/rmmP7GiKjQoUF5EXfEtBwtPtkzY= +github.com/getsentry/sentry-go v0.35.0/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= @@ -170,8 +214,8 @@ github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiU github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk= -github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= @@ -231,8 +275,8 @@ github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36j github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= @@ -267,6 +311,7 @@ github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlT github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8= github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE= @@ -339,6 +384,8 @@ github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OS github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= +github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= 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= @@ -359,8 +406,8 @@ github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9 github.com/onsi/gomega v1.38.0 h1:c/WX+w8SLAinvuKKQFh77WEucCnPk4j2OTUr7lt7BeY= github.com/onsi/gomega v1.38.0/go.mod h1:OcXcwId0b9QsE7Y49u+BTrL4IdKOBOKnD6VQNTJEB6o= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= -github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8= @@ -372,15 +419,15 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= -github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= +github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= -github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= +github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -396,8 +443,8 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= -github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= +github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k= +github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk= github.com/sanity-io/litter v1.5.1 h1:dwnrSypP6q56o3lFxTU+t2fwQ9A+U5qrXVO4Qg9KwVU= github.com/sanity-io/litter v1.5.1/go.mod h1:5Z71SvaYy5kcGtyglXOC9rrUi3c1E8CamFWjQsazTh0= github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= @@ -410,11 +457,11 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9 github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= -github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= +github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= +github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= -github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE= +github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -423,6 +470,7 @@ github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= +github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -433,8 +481,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/supranational/blst v0.3.14 h1:xNMoHRJOTwMn63ip6qoWJ2Ymgvj7E2b9jY2FAwY+qRo= @@ -444,10 +492,10 @@ github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a h1:1ur3QoCqvE5f github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/thepudds/fzgen v0.4.3 h1:srUP/34BulQaEwPP/uHZkdjUcUjIzL7Jkf4CBVryiP8= github.com/thepudds/fzgen v0.4.3/go.mod h1:BhhwtRhzgvLWAjjcHDJ9pEiLD2Z9hrVIFjBCHJ//zJ4= -github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= -github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= -github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= -github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= +github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= +github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= +github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ= github.com/tyler-smith/go-bip32 v1.0.0 h1:sDR9juArbUgX+bO/iblgZnMPeWY1KZMUC2AFUJdv5KE= github.com/tyler-smith/go-bip32 v1.0.0/go.mod h1:onot+eHknzV4BVPwrzqY5OoVpyCvnwD7lMawL5aQupE= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= @@ -479,30 +527,32 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/zondax/golem v0.27.0 h1:IbBjGIXF3SoGOZHsILJvIM/F/ylwJzMcHAcggiqniPw= +github.com/zondax/golem v0.27.0/go.mod h1:AmorCgJPt00L8xN1VrMBe13PSifoZksnQ1Ge906bu4A= github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= -github.com/zondax/ledger-go v1.0.0 h1:BvNoksIyRqyQTW78rIZP9A44WwAminKiomQa7jXp9EI= -github.com/zondax/ledger-go v1.0.0/go.mod h1:HpgkgFh3Jkwi9iYLDATdyRxc8CxqxcywsFj6QerWzvo= +github.com/zondax/ledger-go v1.0.1 h1:Ks/2tz/dOF+dbRynfZ0dEhcdL1lqw43Sa0zMXHpQ3aQ= +github.com/zondax/ledger-go v1.0.1/go.mod h1:j7IgMY39f30apthJYMd1YsHZRqdyu4KbVmUp0nU78X0= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= -go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 h1:H2JFgRcGiyHg7H7bwcwaQJYrNFqCqrbTQ8K4p1OvDu8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0/go.mod h1:WfCWp1bGoYK8MeULtI15MmQVczfR+bFkk0DF3h06QmQ= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 h1:EtFWSnwW9hGObjkIdmlnWSydO+Qs8OwzfzXLUPg4xOc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0/go.mod h1:QjUEoiGCPkvFZ/MjK6ZZfNOS6mfVEVKYE99dFhuN2LI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0= -go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= -go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= -go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= -go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= -go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= -go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= -go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= -go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= -go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI= -go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= +go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= +go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= +go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= +go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= +go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= +go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os= +go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= @@ -521,8 +571,8 @@ golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= -golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= +golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= +golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= @@ -534,8 +584,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= -golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= +golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -559,8 +609,8 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= -golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= +golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -569,8 +619,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -606,25 +656,23 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= -golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= +golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= +golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= -golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= -golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -644,8 +692,8 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -gonum.org/v1/gonum v0.11.0 h1:f1IJhK4Km5tBJmaiJXtk/PkL4cdVX6J+tGiM187uT5E= -gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -653,18 +701,18 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a h1:SGktgSolFCo75dnHJF2yMvnns6jCmHFJ0vE4Vn2JKvQ= -google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a/go.mod h1:a77HrdMjoeKbnd2jmgcWdaS++ZLZAEq3orIOAEIKiVw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:FiusG7LWj+4byqhbvmB+Q93B/mOxJLN2DTozDuZm4EU= +google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c h1:qXWI/sQtv5UKboZ/zUk7h+mrf/lXORyI+n9DKDAusdg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4= -google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM= +google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= +google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -676,8 +724,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= -google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= +google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/keychain/cubesigner/cubesigner_client.go b/keychain/cubesigner/cubesigner_client.go new file mode 100644 index 0000000..b40e9e9 --- /dev/null +++ b/keychain/cubesigner/cubesigner_client.go @@ -0,0 +1,25 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package cubesigner + +import ( + "github.com/cubist-labs/cubesigner-go-sdk/client" + "github.com/cubist-labs/cubesigner-go-sdk/models" +) + +// CubeSignerClient defines the interface for CubeSigner client operations +// needed by the keychain implementation +type CubeSignerClient interface { + // GetKeyInOrg retrieves key information for the given keyID from the CubeSigner organization. + // It returns the key metadata, including public key and key type. + GetKeyInOrg(keyID string) (*models.KeyInfo, error) + + // BlobSign signs arbitrary data using the specified keyID. + // request contains the data to be signed. + BlobSign(keyID string, request models.BlobSignRequest, receipts ...*client.MfaReceipt) (*client.CubeSignerResponse[models.SignResponse], error) + + // AvaSerializedTxSign signs Avalanche transactions using the specified chainAlias (P/X/C), and materialID (address). + // request contains the serialized transaction data to be signed. + AvaSerializedTxSign(chainAlias, materialID string, request models.AvaSerializedTxSignRequest, receipts ...*client.MfaReceipt) (*client.CubeSignerResponse[models.SignResponse], error) +} diff --git a/keychain/cubesigner/cubesigner_keychain.go b/keychain/cubesigner/cubesigner_keychain.go new file mode 100644 index 0000000..80d02aa --- /dev/null +++ b/keychain/cubesigner/cubesigner_keychain.go @@ -0,0 +1,273 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package cubesigner + +import ( + "encoding/base64" + "encoding/hex" + "errors" + "fmt" + "strings" + + "github.com/ava-labs/libevm/common" + "github.com/cubist-labs/cubesigner-go-sdk/client" + "github.com/cubist-labs/cubesigner-go-sdk/models" + "golang.org/x/exp/maps" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/crypto/keychain" + "github.com/ava-labs/avalanchego/utils/formatting/address" + "github.com/ava-labs/avalanchego/utils/set" + "github.com/ava-labs/avalanchego/wallet/chain/c" + + avasecp256k1 "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" + secp256k1 "github.com/decred/dcrd/dcrec/secp256k1/v4" +) + +var ( + _ keychain.Keychain = (*Keychain)(nil) + _ c.EthKeychain = (*Keychain)(nil) + _ keychain.Signer = (*cubesignerSigner)(nil) + _ CubeSignerClient = (*client.ApiClient)(nil) + + ErrNoKeysProvided = errors.New("you need to provide at least one key to create a server keychain") + ErrEmptySignatureFromServer = errors.New("empty signature obtained from server") + ErrChainAliasMissing = errors.New("chainAlias must be specified in options for CubeSigner") + ErrInvalidChainAlias = errors.New("chainAlias must be 'P', 'X' or 'C' for CubeSigner") + ErrNetworkIDMissing = errors.New("network ID must be specified in options for CubeSigner for P/X chain") + ErrUnsupportedKeyType = errors.New("unsupported key type") + ErrInvalidPublicKey = errors.New("invalid public key format") +) + +const ( + // UncompressedPublicKeyLength is the expected length of an uncompressed secp256k1 public key in bytes. + // This includes the prefix byte (1 byte) plus the X and Y coordinates (32 bytes each). + UncompressedPublicKeyLength = 65 + // UncompressedPublicKeyPrefix is the prefix byte for uncompressed secp256k1 public keys. + // This byte indicates that the key is in uncompressed format. + UncompressedPublicKeyPrefix = 0x04 +) + +// keyInfo holds both the public key and keyID for a CubeSigner key. +type keyInfo struct { + pubKey *avasecp256k1.PublicKey // The Avalanche public key derived from CubeSigner + keyID string // The CubeSigner key identifier +} + +// Keychain provides an abstraction over CubeSigner remote signing capabilities. +type Keychain struct { + cubesignerClient CubeSignerClient // Client for CubeSigner API operations + avaAddrToKeyInfo map[ids.ShortID]*keyInfo // Maps Avalanche addresses to key info + ethAddrToKeyInfo map[common.Address]*keyInfo // Maps Ethereum addresses to key info +} + +// processKey obtains and processes key information from CubeSigner. +// It validates that the key exists in the CubeSigner organization, verifies +// that the key type is supported (secp256k1 for Avalanche/Ethereum), and +// converts the public key from hex format to an Avalanche public key. +func processKey( + cubesignerClient CubeSignerClient, + keyID string, +) (*avasecp256k1.PublicKey, error) { + // Validate key exists + keyInfo, err := cubesignerClient.GetKeyInOrg(keyID) + if err != nil { + return nil, fmt.Errorf("could not find server key %s: %w", keyID, err) + } + + // Validate key type + switch keyInfo.KeyType { + case models.SecpAvaAddr, models.SecpAvaTestAddr, models.SecpEthAddr: + // Supported key types + default: + return nil, fmt.Errorf("keytype %s of server key %s: %w", keyInfo.KeyType, keyID, ErrUnsupportedKeyType) + } + + // get public key + pubKeyHex := strings.TrimPrefix(keyInfo.PublicKey, "0x") + pubKeyBytes, err := hex.DecodeString(pubKeyHex) + if err != nil { + return nil, fmt.Errorf("%w: failed to decode public key for server key %s: %w", ErrInvalidPublicKey, keyID, err) + } + if len(pubKeyBytes) != UncompressedPublicKeyLength { + return nil, fmt.Errorf("invalid public key length for server key %s: expected %d bytes, got %d", keyID, UncompressedPublicKeyLength, len(pubKeyBytes)) + } + if pubKeyBytes[0] != UncompressedPublicKeyPrefix { + return nil, fmt.Errorf("invalid public key format for server key %s: expected uncompressed format (0x%02x prefix), got 0x%02x", keyID, UncompressedPublicKeyPrefix, pubKeyBytes[0]) + } + pubKey, err := secp256k1.ParsePubKey(pubKeyBytes) + if err != nil { + return nil, fmt.Errorf("invalid public key format for server key %s: %w", keyID, err) + } + avaPubKey, err := avasecp256k1.ToPublicKey(pubKey.SerializeCompressed()) + if err != nil { + return nil, fmt.Errorf("invalid public key format for server key %s: %w", keyID, err) + } + + return avaPubKey, nil +} + +// NewKeychain creates a new keychain abstraction over a CubeSigner connection. +// It validates that all provided keyIDs exist in the CubeSigner organization and returns +// a keychain that can be used to sign transactions using those keys. +func NewKeychain( + cubesignerClient CubeSignerClient, + keyIDs []string, +) (*Keychain, error) { + if len(keyIDs) == 0 { + return nil, ErrNoKeysProvided + } + + avaAddrToKeyInfo := map[ids.ShortID]*keyInfo{} + ethAddrToKeyInfo := map[common.Address]*keyInfo{} + + for _, keyID := range keyIDs { + avaPubKey, err := processKey(cubesignerClient, keyID) + if err != nil { + return nil, err + } + + keyInf := &keyInfo{ + pubKey: avaPubKey, + keyID: keyID, + } + + avaAddrToKeyInfo[avaPubKey.Address()] = keyInf + ethAddrToKeyInfo[avaPubKey.EthAddress()] = keyInf + } + + return &Keychain{ + cubesignerClient: cubesignerClient, + avaAddrToKeyInfo: avaAddrToKeyInfo, + ethAddrToKeyInfo: ethAddrToKeyInfo, + }, nil +} + +// Addresses returns the set of Avalanche addresses that this keychain can sign for. +func (kc *Keychain) Addresses() set.Set[ids.ShortID] { + return set.Of(maps.Keys(kc.avaAddrToKeyInfo)...) +} + +// Get returns a signer for the given Avalanche address, if it exists in this keychain. +func (kc *Keychain) Get(addr ids.ShortID) (keychain.Signer, bool) { + keyInf, found := kc.avaAddrToKeyInfo[addr] + if !found { + return nil, false + } + return &cubesignerSigner{ + cubesignerClient: kc.cubesignerClient, + pubKey: keyInf.pubKey, + keyID: keyInf.keyID, + }, true +} + +// EthAddresses returns the set of Ethereum addresses that this keychain can sign for. +func (kc *Keychain) EthAddresses() set.Set[common.Address] { + return set.Of(maps.Keys(kc.ethAddrToKeyInfo)...) +} + +// GetEth returns a signer for the given Ethereum address, if it exists in this keychain. +func (kc *Keychain) GetEth(addr common.Address) (keychain.Signer, bool) { + keyInf, found := kc.ethAddrToKeyInfo[addr] + if !found { + return nil, false + } + return &cubesignerSigner{ + cubesignerClient: kc.cubesignerClient, + pubKey: keyInf.pubKey, + keyID: keyInf.keyID, + }, true +} + +// cubesignerAvagoSigner is an abstraction of the underlying cubesigner connection, +// to be able sign for a specific address +type cubesignerSigner struct { + cubesignerClient CubeSignerClient + pubKey *avasecp256k1.PublicKey + keyID string +} + +// processSignatureResponse is a helper function that processes the common response +// pattern from CubeSigner signing operations. It decodes the hex signature and validates its length. +func processSignatureResponse(signatureHex string) ([]byte, error) { + signatureBytes, err := hex.DecodeString(strings.TrimPrefix(signatureHex, "0x")) + if err != nil { + return nil, fmt.Errorf("failed to decode server's signature: %w", err) + } + if len(signatureBytes) != avasecp256k1.SignatureLen { + return nil, fmt.Errorf("invalid server's signature length: expected %d bytes, got %d", avasecp256k1.SignatureLen, len(signatureBytes)) + } + return signatureBytes, nil +} + +// SignHash signs the given hash using CubeSigner's BlobSign API. +// It expects to receive a hash of the unsigned transaction bytes. +func (s *cubesignerSigner) SignHash(b []byte) ([]byte, error) { + response, err := s.cubesignerClient.BlobSign( + s.keyID, + models.BlobSignRequest{ + MessageBase64: base64.StdEncoding.EncodeToString(b), + }, + ) + if err != nil { + return nil, fmt.Errorf("server signing err: %w", err) + } + if response.ResponseData == nil { + return nil, ErrEmptySignatureFromServer + } + return processSignatureResponse(response.ResponseData.Signature) +} + +// Sign signs the given payload according to the given signing options. +// It expects to receive the unsigned transaction bytes and requires ChainAlias and NetworkID +// to be specified in the signing options for CubeSigner's AvaSerializedTxSign API. +func (s *cubesignerSigner) Sign(b []byte, opts ...keychain.SigningOption) ([]byte, error) { + options := &keychain.SigningOptions{} + for _, opt := range opts { + opt(options) + } + // Require chainAlias and network from options + if options.ChainAlias == "" { + return nil, ErrChainAliasMissing + } + if options.ChainAlias != "P" && options.ChainAlias != "X" && options.ChainAlias != "C" { + return nil, fmt.Errorf("%w, got %q", ErrInvalidChainAlias, options.ChainAlias) + } + if options.ChainAlias != "C" && options.NetworkID == 0 { + return nil, ErrNetworkIDMissing + } + var materialID string + if options.ChainAlias == "C" { + materialID = s.pubKey.EthAddress().Hex() + } else { + hrp := constants.GetHRP(options.NetworkID) + addr := s.pubKey.Address() + var err error + materialID, err = address.FormatBech32(hrp, addr.Bytes()) + if err != nil { + return nil, fmt.Errorf("failed to format %s address %v as Bech32: %w", hrp, addr, err) + } + } + + response, err := s.cubesignerClient.AvaSerializedTxSign( + options.ChainAlias, + materialID, + models.AvaSerializedTxSignRequest{ + Tx: "0x" + hex.EncodeToString(b), + }, + ) + if err != nil { + return nil, fmt.Errorf("server signing err: %w", err) + } + if response.ResponseData == nil { + return nil, ErrEmptySignatureFromServer + } + return processSignatureResponse(response.ResponseData.Signature) +} + +// Address returns the Avalanche address associated with this signer. +func (s *cubesignerSigner) Address() ids.ShortID { + return s.pubKey.Address() +} diff --git a/keychain/cubesigner/cubesigner_keychain_test.go b/keychain/cubesigner/cubesigner_keychain_test.go new file mode 100644 index 0000000..87e6d74 --- /dev/null +++ b/keychain/cubesigner/cubesigner_keychain_test.go @@ -0,0 +1,335 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package cubesigner + +import ( + "errors" + "testing" + + "github.com/ava-labs/libevm/common" + "github.com/cubist-labs/cubesigner-go-sdk/client" + "github.com/cubist-labs/cubesigner-go-sdk/models" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/crypto/keychain" + + "github.com/ava-labs/avalanche-tooling-sdk-go/keychain/cubesigner/cubesignermock" +) + +var errTest = errors.New("test") + +const testKeyID = "test-key-id" + +func TestNewKeychain(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + + // user provides no keys + mockClient := cubesignermock.NewCubeSignerClient(ctrl) + _, err := NewKeychain(mockClient, []string{}) + require.ErrorIs(err, ErrNoKeysProvided) + + // client returns error when getting key info + mockClient = cubesignermock.NewCubeSignerClient(ctrl) + mockClient.EXPECT().GetKeyInOrg(testKeyID).Return(nil, errTest).Times(1) + _, err = NewKeychain(mockClient, []string{testKeyID}) + require.ErrorIs(err, errTest) + + // client returns unsupported key type + mockClient = cubesignermock.NewCubeSignerClient(ctrl) + keyInfo := &models.KeyInfo{ + KeyType: "UnsupportedType", + PublicKey: "0x04" + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", + } + mockClient.EXPECT().GetKeyInOrg(testKeyID).Return(keyInfo, nil).Times(1) + _, err = NewKeychain(mockClient, []string{testKeyID}) + require.ErrorIs(err, ErrUnsupportedKeyType) + + // client returns invalid public key format + mockClient = cubesignermock.NewCubeSignerClient(ctrl) + keyInfo = &models.KeyInfo{ + KeyType: models.SecpAvaAddr, + PublicKey: "invalid-hex", + } + mockClient.EXPECT().GetKeyInOrg(testKeyID).Return(keyInfo, nil).Times(1) + _, err = NewKeychain(mockClient, []string{testKeyID}) + require.ErrorIs(err, ErrInvalidPublicKey) + + // good path - Avalanche address + mockClient = cubesignermock.NewCubeSignerClient(ctrl) + keyInfo = &models.KeyInfo{ + KeyType: models.SecpAvaAddr, + PublicKey: "0x04" + "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + } + mockClient.EXPECT().GetKeyInOrg(testKeyID).Return(keyInfo, nil).Times(1) + kc, err := NewKeychain(mockClient, []string{testKeyID}) + require.NoError(err) + require.NotNil(kc) + + // good path - Ethereum address + mockClient = cubesignermock.NewCubeSignerClient(ctrl) + keyInfo = &models.KeyInfo{ + KeyType: models.SecpEthAddr, + PublicKey: "0x04" + "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + } + mockClient.EXPECT().GetKeyInOrg(testKeyID).Return(keyInfo, nil).Times(1) + kc, err = NewKeychain(mockClient, []string{testKeyID}) + require.NoError(err) + require.NotNil(kc) +} + +func TestKeychain_Addresses(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + + mockClient := cubesignermock.NewCubeSignerClient(ctrl) + + keyInfo := &models.KeyInfo{ + KeyType: models.SecpAvaAddr, + PublicKey: "0x04" + "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + } + mockClient.EXPECT().GetKeyInOrg(testKeyID).Return(keyInfo, nil).Times(1) + + kc, err := NewKeychain(mockClient, []string{testKeyID}) + require.NoError(err) + + addresses := kc.Addresses() + require.Len(addresses, 1) +} + +func TestKeychain_Get(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + + mockClient := cubesignermock.NewCubeSignerClient(ctrl) + + keyInfo := &models.KeyInfo{ + KeyType: models.SecpAvaAddr, + PublicKey: "0x04" + "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + } + mockClient.EXPECT().GetKeyInOrg(testKeyID).Return(keyInfo, nil).Times(1) + + kc, err := NewKeychain(mockClient, []string{testKeyID}) + require.NoError(err) + + addresses := kc.Addresses() + require.Len(addresses, 1) + + addr := addresses.List()[0] + signer, found := kc.Get(addr) + require.True(found) + require.NotNil(signer) + require.Equal(addr, signer.Address()) + + // test with non-existent address + randomAddr := ids.GenerateTestShortID() + signer, found = kc.Get(randomAddr) + require.False(found) + require.Nil(signer) +} + +func TestKeychain_EthAddresses(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + + mockClient := cubesignermock.NewCubeSignerClient(ctrl) + + keyInfo := &models.KeyInfo{ + KeyType: models.SecpEthAddr, + PublicKey: "0x04" + "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + } + mockClient.EXPECT().GetKeyInOrg(testKeyID).Return(keyInfo, nil).Times(1) + + kc, err := NewKeychain(mockClient, []string{testKeyID}) + require.NoError(err) + + ethAddresses := kc.EthAddresses() + require.Len(ethAddresses, 1) +} + +func TestKeychain_GetEth(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + + mockClient := cubesignermock.NewCubeSignerClient(ctrl) + + keyInfo := &models.KeyInfo{ + KeyType: models.SecpEthAddr, + PublicKey: "0x04" + "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + } + mockClient.EXPECT().GetKeyInOrg(testKeyID).Return(keyInfo, nil).Times(1) + + kc, err := NewKeychain(mockClient, []string{testKeyID}) + require.NoError(err) + + ethAddresses := kc.EthAddresses() + require.Len(ethAddresses, 1) + + ethAddr := ethAddresses.List()[0] + signer, found := kc.GetEth(ethAddr) + require.True(found) + require.NotNil(signer) + + // Test non-existent address + nonExistentAddr := common.HexToAddress("0x0000000000000000000000000000000000000000") + signer, found = kc.GetEth(nonExistentAddr) + require.False(found) + require.Nil(signer) +} + +func TestCubesignerSigner_SignHash(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + + mockClient := cubesignermock.NewCubeSignerClient(ctrl) + + keyInfo := &models.KeyInfo{ + KeyType: models.SecpAvaAddr, + PublicKey: "0x04" + "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + } + mockClient.EXPECT().GetKeyInOrg(testKeyID).Return(keyInfo, nil).Times(1) + + kc, err := NewKeychain(mockClient, []string{testKeyID}) + require.NoError(err) + + addresses := kc.Addresses() + addr := addresses.List()[0] + signer, found := kc.Get(addr) + require.True(found) + + hash := []byte("test-hash") + + // test successful signing + response := &client.CubeSignerResponse[models.SignResponse]{ + ResponseData: &models.SignResponse{ + Signature: "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef01", + }, + } + mockClient.EXPECT().BlobSign(testKeyID, gomock.Any()).Return(response, nil).Times(1) + + signature, err := signer.SignHash(hash) + require.NoError(err) + require.NotNil(signature) + + // test client error + mockClient.EXPECT().BlobSign(testKeyID, gomock.Any()).Return(nil, errTest).Times(1) + _, err = signer.SignHash(hash) + require.ErrorIs(err, errTest) + + // test empty response + emptyResponse := &client.CubeSignerResponse[models.SignResponse]{ + ResponseData: nil, + } + mockClient.EXPECT().BlobSign(testKeyID, gomock.Any()).Return(emptyResponse, nil).Times(1) + _, err = signer.SignHash(hash) + require.ErrorIs(err, ErrEmptySignatureFromServer) +} + +func TestCubesignerSigner_Sign(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + + mockClient := cubesignermock.NewCubeSignerClient(ctrl) + + keyInfo := &models.KeyInfo{ + KeyType: models.SecpAvaAddr, + PublicKey: "0x04" + "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + } + mockClient.EXPECT().GetKeyInOrg(testKeyID).Return(keyInfo, nil).Times(1) + + kc, err := NewKeychain(mockClient, []string{testKeyID}) + require.NoError(err) + + addresses := kc.Addresses() + addr := addresses.List()[0] + signer, found := kc.Get(addr) + require.True(found) + + txBytes := []byte("test-transaction-bytes") + + // Test missing chain alias + _, err = signer.Sign(txBytes) + require.ErrorIs(err, ErrChainAliasMissing) + + // Test invalid chain alias + _, err = signer.Sign(txBytes, keychain.WithChainAlias("invalid")) + require.ErrorIs(err, ErrInvalidChainAlias) + + // Test missing network ID + _, err = signer.Sign(txBytes, keychain.WithChainAlias("P")) + require.ErrorIs(err, ErrNetworkIDMissing) + + // Test successful P-chain signing + response := &client.CubeSignerResponse[models.SignResponse]{ + ResponseData: &models.SignResponse{ + Signature: "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef01", + }, + } + mockClient.EXPECT().AvaSerializedTxSign( + "P", + gomock.Any(), // materialID (Bech32 address) + gomock.Any(), // request with hex-encoded tx + ).Return(response, nil).Times(1) + + signature, err := signer.Sign(txBytes, + keychain.WithChainAlias("P"), + keychain.WithNetworkID(1)) + require.NoError(err) + require.NotNil(signature) + + // Test successful X-chain signing + mockClient.EXPECT().AvaSerializedTxSign( + "X", + gomock.Any(), // materialID (Bech32 address) + gomock.Any(), // request with hex-encoded tx + ).Return(response, nil).Times(1) + + signature, err = signer.Sign(txBytes, + keychain.WithChainAlias("X"), + keychain.WithNetworkID(1)) + require.NoError(err) + require.NotNil(signature) + + // Test successful C-chain signing + mockClient.EXPECT().AvaSerializedTxSign( + "C", + gomock.Any(), // materialID (Eth address) + gomock.Any(), // request with hex-encoded tx + ).Return(response, nil).Times(1) + + signature, err = signer.Sign(txBytes, + keychain.WithChainAlias("C"), + keychain.WithNetworkID(1)) + require.NoError(err) + require.NotNil(signature) + + // Test client error + mockClient.EXPECT().AvaSerializedTxSign( + gomock.Any(), + gomock.Any(), + gomock.Any(), + ).Return(nil, errTest).Times(1) + + _, err = signer.Sign(txBytes, + keychain.WithChainAlias("P"), + keychain.WithNetworkID(1)) + require.ErrorIs(err, errTest) + + // Test empty response + emptyResponse := &client.CubeSignerResponse[models.SignResponse]{ + ResponseData: nil, + } + mockClient.EXPECT().AvaSerializedTxSign( + gomock.Any(), + gomock.Any(), + gomock.Any(), + ).Return(emptyResponse, nil).Times(1) + + _, err = signer.Sign(txBytes, + keychain.WithChainAlias("P"), + keychain.WithNetworkID(1)) + require.ErrorIs(err, ErrEmptySignatureFromServer) +} diff --git a/keychain/cubesigner/cubesignermock/cubesigner_client.go b/keychain/cubesigner/cubesignermock/cubesigner_client.go new file mode 100644 index 0000000..b2ce1b1 --- /dev/null +++ b/keychain/cubesigner/cubesignermock/cubesigner_client.go @@ -0,0 +1,97 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/ava-labs/avalanchego/utils/crypto/keychain/cubesigner (interfaces: CubeSignerClient) +// +// Generated by this command: +// +// mockgen -package=cubesignermock -destination=cubesignermock/cubesigner_client.go -mock_names=CubeSignerClient=CubeSignerClient . CubeSignerClient +// + +// Package cubesignermock is a generated GoMock package. +package cubesignermock + +import ( + reflect "reflect" + + client "github.com/cubist-labs/cubesigner-go-sdk/client" + models "github.com/cubist-labs/cubesigner-go-sdk/models" + gomock "go.uber.org/mock/gomock" +) + +// CubeSignerClient is a mock of CubeSignerClient interface. +type CubeSignerClient struct { + ctrl *gomock.Controller + recorder *CubeSignerClientMockRecorder + isgomock struct{} +} + +// CubeSignerClientMockRecorder is the mock recorder for CubeSignerClient. +type CubeSignerClientMockRecorder struct { + mock *CubeSignerClient +} + +// NewCubeSignerClient creates a new mock instance. +func NewCubeSignerClient(ctrl *gomock.Controller) *CubeSignerClient { + mock := &CubeSignerClient{ctrl: ctrl} + mock.recorder = &CubeSignerClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *CubeSignerClient) EXPECT() *CubeSignerClientMockRecorder { + return m.recorder +} + +// AvaSerializedTxSign mocks base method. +func (m *CubeSignerClient) AvaSerializedTxSign(chainAlias, materialID string, request models.AvaSerializedTxSignRequest, receipts ...*client.MfaReceipt) (*client.CubeSignerResponse[models.SignResponse], error) { + m.ctrl.T.Helper() + varargs := []any{chainAlias, materialID, request} + for _, a := range receipts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "AvaSerializedTxSign", varargs...) + ret0, _ := ret[0].(*client.CubeSignerResponse[models.SignResponse]) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AvaSerializedTxSign indicates an expected call of AvaSerializedTxSign. +func (mr *CubeSignerClientMockRecorder) AvaSerializedTxSign(chainAlias, materialID, request any, receipts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{chainAlias, materialID, request}, receipts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AvaSerializedTxSign", reflect.TypeOf((*CubeSignerClient)(nil).AvaSerializedTxSign), varargs...) +} + +// BlobSign mocks base method. +func (m *CubeSignerClient) BlobSign(keyID string, request models.BlobSignRequest, receipts ...*client.MfaReceipt) (*client.CubeSignerResponse[models.SignResponse], error) { + m.ctrl.T.Helper() + varargs := []any{keyID, request} + for _, a := range receipts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "BlobSign", varargs...) + ret0, _ := ret[0].(*client.CubeSignerResponse[models.SignResponse]) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// BlobSign indicates an expected call of BlobSign. +func (mr *CubeSignerClientMockRecorder) BlobSign(keyID, request any, receipts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{keyID, request}, receipts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BlobSign", reflect.TypeOf((*CubeSignerClient)(nil).BlobSign), varargs...) +} + +// GetKeyInOrg mocks base method. +func (m *CubeSignerClient) GetKeyInOrg(keyID string) (*models.KeyInfo, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetKeyInOrg", keyID) + ret0, _ := ret[0].(*models.KeyInfo) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetKeyInOrg indicates an expected call of GetKeyInOrg. +func (mr *CubeSignerClientMockRecorder) GetKeyInOrg(keyID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetKeyInOrg", reflect.TypeOf((*CubeSignerClient)(nil).GetKeyInOrg), keyID) +} diff --git a/keychain/cubesigner/mocks_generate_test.go b/keychain/cubesigner/mocks_generate_test.go new file mode 100644 index 0000000..dbe08ac --- /dev/null +++ b/keychain/cubesigner/mocks_generate_test.go @@ -0,0 +1,6 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package cubesigner + +//go:generate go run go.uber.org/mock/mockgen -package=${GOPACKAGE}mock -destination=${GOPACKAGE}mock/cubesigner_client.go -mock_names=CubeSignerClient=CubeSignerClient . CubeSignerClient From a9b5558b5e5d5fd5a6e0f1893561aa3318591481 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 10 Oct 2025 14:29:17 -0300 Subject: [PATCH 02/10] add cubesigner keychain README with usage examples Add comprehensive documentation for the cubesigner keychain package including setup instructions, usage examples for subnet creation and cross-chain transfers, and integration details with Avalanche wallet. --- keychain/cubesigner/README.md | 268 ++++++++++++++++++++++++++++++++++ 1 file changed, 268 insertions(+) create mode 100644 keychain/cubesigner/README.md diff --git a/keychain/cubesigner/README.md b/keychain/cubesigner/README.md new file mode 100644 index 0000000..8c46bb4 --- /dev/null +++ b/keychain/cubesigner/README.md @@ -0,0 +1,268 @@ +# CubeSigner Keychain + +This Go package provides a keychain implementation for Avalanche that integrates with [CubeSigner](https://www.cubist.dev/), a remote signing service for secure key management. + +CubeSigner enables secure transaction signing without exposing private keys locally, supporting both Avalanche native chains (P-Chain, X-Chain, C-Chain) and Ethereum-compatible chains. + +## Features + +* Remote transaction signing via CubeSigner API +* Support for P-Chain, X-Chain, and C-Chain operations +* Compatible with Avalanche's `keychain.Keychain` and `c.EthKeychain` interfaces +* Automatic key validation and format conversion +* Support for both hash signing and serialized transaction signing + +## Usage Example + +### Creating a Subnet + +```go +package main + +import ( + "context" + "fmt" + "log" + "os" + + "github.com/ava-labs/avalanche-tooling-sdk-go/keychain/cubesigner" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/avalanchego/wallet/subnet/primary" + "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" + "github.com/cubist-labs/cubesigner-go-sdk/client" + "github.com/cubist-labs/cubesigner-go-sdk/session" +) + +func main() { + // Get key ID from environment + keyID := os.Getenv("CUBESIGNER_KEY_ID") + if keyID == "" { + log.Fatal("CUBESIGNER_KEY_ID environment variable is required") + } + + // Initialize CubeSigner client + sessionFile := "session.json" + manager, err := session.NewJsonSessionManager(&sessionFile) + if err != nil { + log.Fatalf("Failed to create session manager: %v", err) + } + + apiClient, err := client.NewApiClient(manager) + if err != nil { + log.Fatalf("Failed to create API client: %v", err) + } + + // Create CubeSigner keychain + kc, err := cubesigner.NewKeychain(apiClient, []string{keyID}) + if err != nil { + log.Fatalf("Failed to create keychain: %v", err) + } + + // Create primary wallet on Fuji testnet + ctx := context.Background() + wallet, err := primary.MakeWallet( + ctx, + primary.FujiAPIURI, + kc, + kc, + primary.WalletConfig{}, + ) + if err != nil { + log.Fatalf("Failed to create wallet: %v", err) + } + + // Create subnet with threshold of 1 + subnetOwnerAddrs := kc.Addresses().List() + owner := &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: subnetOwnerAddrs, + } + + // Issue create subnet transaction + createSubnetTx, err := wallet.P().IssueCreateSubnetTx( + owner, + common.WithContext(ctx), + ) + if err != nil { + log.Fatalf("Failed to create subnet: %v", err) + } + + fmt.Printf("Subnet created with ID: %s\n", createSubnetTx.ID()) +} +``` + +### Cross-Chain Transfer (P-Chain to C-Chain) + +```go +package main + +import ( + "context" + "fmt" + "log" + "os" + "time" + + "github.com/ava-labs/avalanche-tooling-sdk-go/keychain/cubesigner" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/units" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/avalanchego/wallet/subnet/primary" + "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" + "github.com/cubist-labs/cubesigner-go-sdk/client" + "github.com/cubist-labs/cubesigner-go-sdk/session" +) + +func main() { + // Get key IDs from environment + pChainKeyID := os.Getenv("PCHAIN_KEY_ID") + cChainKeyID := os.Getenv("CCHAIN_KEY_ID") + if pChainKeyID == "" || cChainKeyID == "" { + log.Fatal("PCHAIN_KEY_ID and CCHAIN_KEY_ID environment variables are required") + } + + // Initialize CubeSigner client + sessionFile := "session.json" + manager, err := session.NewJsonSessionManager(&sessionFile) + if err != nil { + log.Fatalf("Failed to create session manager: %v", err) + } + + apiClient, err := client.NewApiClient(manager) + if err != nil { + log.Fatalf("Failed to create API client: %v", err) + } + + // Create keychains + kcPChain, err := cubesigner.NewKeychain(apiClient, []string{pChainKeyID}) + if err != nil { + log.Fatalf("Failed to create P-chain keychain: %v", err) + } + + kcCChain, err := cubesigner.NewKeychain(apiClient, []string{cChainKeyID}) + if err != nil { + log.Fatalf("Failed to create C-chain keychain: %v", err) + } + + // Create wallet for P-chain operations + ctx := context.Background() + walletPChain, err := primary.MakeWallet( + ctx, + primary.FujiAPIURI, + kcPChain, + kcPChain, + primary.WalletConfig{}, + ) + if err != nil { + log.Fatalf("Failed to create P-chain wallet: %v", err) + } + + // Get chain and asset IDs + cChainID := walletPChain.C().Builder().Context().BlockchainID + avaxAssetID := walletPChain.P().Builder().Context().AVAXAssetID + + // Export 0.5 AVAX from P-chain to C-chain + exportOwner := &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: kcCChain.Addresses().List(), + } + + exportTx, err := walletPChain.P().IssueExportTx( + cChainID, + []*avax.TransferableOutput{{ + Asset: avax.Asset{ID: avaxAssetID}, + Out: &secp256k1fx.TransferOutput{ + Amt: units.Avax / 2, // 0.5 AVAX + OutputOwners: *exportOwner, + }, + }}, + common.WithContext(ctx), + ) + if err != nil { + log.Fatalf("Failed to export from P-chain: %v", err) + } + + fmt.Printf("Exported from P-chain, tx ID: %s\n", exportTx.ID()) + + // Wait for export to be processed + time.Sleep(10 * time.Second) + + // Create wallet for C-chain operations + walletCChain, err := primary.MakeWallet( + ctx, + primary.FujiAPIURI, + kcCChain, + kcCChain, + primary.WalletConfig{}, + ) + if err != nil { + log.Fatalf("Failed to create C-chain wallet: %v", err) + } + + // Import on C-chain + cChainAddr := kcCChain.EthAddresses().List()[0] + importTx, err := walletCChain.C().IssueImportTx( + constants.PlatformChainID, + cChainAddr, + common.WithContext(ctx), + ) + if err != nil { + log.Fatalf("Failed to import on C-chain: %v", err) + } + + fmt.Printf("Imported on C-chain, tx ID: %s\n", importTx.ID()) +} +``` + +## Key Features + +### Supported Key Types + +The keychain supports CubeSigner keys with the following types: +- `SecpAvaAddr` - Avalanche mainnet secp256k1 keys +- `SecpAvaTestAddr` - Avalanche testnet secp256k1 keys +- `SecpEthAddr` - Ethereum secp256k1 keys + +### Signing Methods + +#### 1. Serialized Transaction Signing (Recommended) + +Used automatically by the Avalanche wallet for P-Chain and X-Chain transactions. Requires: +- `ChainAlias` option: "P", "X", or "C" +- `NetworkID` option: Network ID + +```go +import "github.com/ava-labs/avalanchego/utils/crypto/keychain" + +// Example with signing options +signer, _ := kc.Get(address) +signature, err := signer.Sign( + unsignedTxBytes, + keychain.WithChainAlias("P"), + keychain.WithNetworkID(constants.FujiID), +) +``` + +#### 2. Hash Signing + +Used for signing arbitrary data hashed using Keccak-256: + +```go +signer, _ := kc.Get(address) +signature, err := signer.SignHash(hashBytes) +``` + +## Integration with Avalanche Wallet + +The CubeSigner keychain implements both `keychain.Keychain` and `c.EthKeychain` interfaces, making it compatible with the Avalanche primary wallet: + +```go +wallet, err := primary.MakeWallet( + ctx, + endpoint, + cubesignerKeychain, // For P/X chain operations + cubesignerKeychain, // For C chain operations + primary.WalletConfig{}, +) +``` From abc9d8f3ff742243669f6594eb110156aef1a24c Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 10 Oct 2025 14:40:00 -0300 Subject: [PATCH 03/10] fix import formatting in cubesigner keychain Fix import grouping to comply with gci linter rules. --- keychain/cubesigner/cubesigner_keychain.go | 9 ++++----- keychain/cubesigner/cubesigner_keychain_test.go | 5 ++--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/keychain/cubesigner/cubesigner_keychain.go b/keychain/cubesigner/cubesigner_keychain.go index 80d02aa..b2eaf8c 100644 --- a/keychain/cubesigner/cubesigner_keychain.go +++ b/keychain/cubesigner/cubesigner_keychain.go @@ -10,17 +10,16 @@ import ( "fmt" "strings" - "github.com/ava-labs/libevm/common" - "github.com/cubist-labs/cubesigner-go-sdk/client" - "github.com/cubist-labs/cubesigner-go-sdk/models" - "golang.org/x/exp/maps" - "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/crypto/keychain" "github.com/ava-labs/avalanchego/utils/formatting/address" "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/wallet/chain/c" + "github.com/ava-labs/libevm/common" + "github.com/cubist-labs/cubesigner-go-sdk/client" + "github.com/cubist-labs/cubesigner-go-sdk/models" + "golang.org/x/exp/maps" avasecp256k1 "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" secp256k1 "github.com/decred/dcrd/dcrec/secp256k1/v4" diff --git a/keychain/cubesigner/cubesigner_keychain_test.go b/keychain/cubesigner/cubesigner_keychain_test.go index 87e6d74..98679ea 100644 --- a/keychain/cubesigner/cubesigner_keychain_test.go +++ b/keychain/cubesigner/cubesigner_keychain_test.go @@ -7,15 +7,14 @@ import ( "errors" "testing" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/crypto/keychain" "github.com/ava-labs/libevm/common" "github.com/cubist-labs/cubesigner-go-sdk/client" "github.com/cubist-labs/cubesigner-go-sdk/models" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/utils/crypto/keychain" - "github.com/ava-labs/avalanche-tooling-sdk-go/keychain/cubesigner/cubesignermock" ) From f3fd0023865263772bd572ee208c27f418afce55 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 10 Oct 2025 14:50:26 -0300 Subject: [PATCH 04/10] remove icm-services dependency and use local types Replace icm-services dependency with local type definitions for AggregateSignatureRequest and AggregateSignatureResponse to avoid dependency conflicts with avalanchego/libevm/subnet-evm versions. This resolves linter errors related to incompatible icm-services peer package versions. --- go.mod | 74 +++++------ go.sum | 161 ++++++++++------------- interchain/signature-aggregator.go | 5 +- interchain/signature_aggregator_types.go | 19 +++ 4 files changed, 124 insertions(+), 135 deletions(-) create mode 100644 interchain/signature_aggregator_types.go diff --git a/go.mod b/go.mod index 4194358..c0ad872 100644 --- a/go.mod +++ b/go.mod @@ -4,20 +4,19 @@ go 1.24.8 require ( github.com/ava-labs/avalanchego v1.13.6-0.20251010155017-cd3e6bee30ab - github.com/ava-labs/icm-services v1.4.1-0.20250729175537-a74c15880023 github.com/ava-labs/libevm v1.13.15-0.20251002164226-35926db4d661 github.com/ava-labs/subnet-evm v0.7.10-0.20251010143515-0e03d5e4c9c9 github.com/cavaliergopher/grab/v3 v3.0.1 github.com/cubist-labs/cubesigner-go-sdk v0.0.16 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 github.com/stretchr/testify v1.11.1 - go.uber.org/mock v0.5.2 + go.uber.org/mock v0.6.0 go.uber.org/zap v1.27.0 - golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 + golang.org/x/exp v0.0.0-20251002181428-27f1f14c8bb9 ) require ( - connectrpc.com/connect v1.18.1 // indirect + connectrpc.com/connect v1.19.1 // indirect connectrpc.com/grpcreflect v1.3.0 // indirect github.com/DataDog/zstd v1.5.2 // indirect github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e // indirect @@ -29,20 +28,20 @@ require ( github.com/ava-labs/coreth v0.15.4-rc.3.0.20251002221438-a857a64c28ea // indirect github.com/ava-labs/firewood-go-ethhash/ffi v0.0.12 // indirect github.com/ava-labs/ledger-avalanche-go v1.1.0 // indirect - github.com/aws/aws-sdk-go-v2 v1.37.0 // indirect - github.com/aws/aws-sdk-go-v2/config v1.29.18 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.17.71 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.33 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.0 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.0 // indirect + github.com/aws/aws-sdk-go-v2 v1.39.2 // indirect + github.com/aws/aws-sdk-go-v2/config v1.31.12 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.18.16 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.9 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.18 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9 // indirect github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.19 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.25.6 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.4 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.34.1 // indirect - github.com/aws/smithy-go v1.22.5 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.29.6 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.38.6 // indirect + github.com/aws/smithy-go v1.23.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.10.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.5 // indirect @@ -68,7 +67,6 @@ require ( github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect - github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/gofrs/flock v0.12.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect @@ -79,8 +77,7 @@ require ( github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/rpc v1.2.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect - github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect github.com/holiman/uint256 v1.2.4 // indirect github.com/klauspost/compress v1.18.0 // indirect @@ -92,26 +89,18 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/oapi-codegen/runtime v1.1.1 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/pingcap/errors v0.11.4 // indirect github.com/pires/go-proxyproto v0.6.2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.23.0 // indirect + github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/common v0.66.1 // indirect github.com/prometheus/procfs v0.16.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rs/cors v1.11.1 // indirect - github.com/sagikazarmark/locafero v0.9.0 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect - github.com/sourcegraph/conc v0.3.0 // indirect - github.com/spf13/afero v1.14.0 // indirect - github.com/spf13/cast v1.9.2 // indirect - github.com/spf13/pflag v1.0.7 // indirect - github.com/spf13/viper v1.20.1 // indirect - github.com/subosito/gotenv v1.6.0 // indirect + github.com/spf13/cast v1.10.0 // indirect github.com/supranational/blst v0.3.14 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect github.com/tklauser/go-sysconf v0.3.15 // indirect @@ -121,28 +110,29 @@ require ( github.com/zondax/golem v0.27.0 // indirect github.com/zondax/hid v0.9.2 // indirect github.com/zondax/ledger-go v1.0.1 // indirect - go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/otel v1.37.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/otel v1.38.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 // indirect - go.opentelemetry.io/otel/metric v1.37.0 // indirect - go.opentelemetry.io/otel/sdk v1.37.0 // indirect - go.opentelemetry.io/otel/trace v1.37.0 // indirect - go.opentelemetry.io/proto/otlp v1.7.0 // indirect + go.opentelemetry.io/otel/metric v1.38.0 // indirect + go.opentelemetry.io/otel/sdk v1.38.0 // indirect + go.opentelemetry.io/otel/trace v1.38.0 // indirect + go.opentelemetry.io/proto/otlp v1.8.0 // indirect go.uber.org/multierr v1.11.0 // indirect + go.yaml.in/yaml/v2 v2.4.2 // indirect golang.org/x/crypto v0.42.0 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/term v0.35.0 // indirect golang.org/x/text v0.29.0 // indirect golang.org/x/time v0.12.0 // indirect gonum.org/v1/gonum v0.16.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c // indirect - google.golang.org/grpc v1.75.0 // indirect - google.golang.org/protobuf v1.36.8 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect + google.golang.org/grpc v1.76.0 // indirect + google.golang.org/protobuf v1.36.10 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect diff --git a/go.sum b/go.sum index 22a5d08..8d0cb5d 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw= -connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= +connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14= +connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w= connectrpc.com/grpcreflect v1.3.0 h1:Y4V+ACf8/vOb1XOc251Qun7jMB75gCUNw6llvB9csXc= connectrpc.com/grpcreflect v1.3.0/go.mod h1:nfloOtCS8VUQOQ1+GTdFzVg2CJo4ZGaat8JIovCtDYs= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= @@ -37,42 +37,40 @@ github.com/ava-labs/coreth v0.15.4-rc.3.0.20251002221438-a857a64c28ea h1:vrHUSx6 github.com/ava-labs/coreth v0.15.4-rc.3.0.20251002221438-a857a64c28ea/go.mod h1:y/14LplmA0lLwIDlKiGAZ8OlxQ7OxhaU2dfkYcviLPM= github.com/ava-labs/firewood-go-ethhash/ffi v0.0.12 h1:aMcrLbpJ/dyu2kZDf/Di/4JIWsUcYPyTDKymiHpejt0= github.com/ava-labs/firewood-go-ethhash/ffi v0.0.12/go.mod h1:cq89ua3iiZ5wPBALTEQS5eG8DIZcs7ov6OiL4YR1BVY= -github.com/ava-labs/icm-services v1.4.1-0.20250729175537-a74c15880023 h1:6WaIMdc35U7mNOmWJAO0lthvsM8e38Jl+mq/RiUopsU= -github.com/ava-labs/icm-services v1.4.1-0.20250729175537-a74c15880023/go.mod h1:17BcQ5NzeHSo80tIKaiP8MsxR7UJTeSQNmF3+cocXe8= github.com/ava-labs/ledger-avalanche-go v1.1.0 h1:OkscKtb/gX20HBt8RyAtwXLrQnCEls5SzWGieE7NoNM= github.com/ava-labs/ledger-avalanche-go v1.1.0/go.mod h1:mAlG9ptnPjvNoLGLHXnM3slGY8ewvBJtJNVTEjG8KvI= github.com/ava-labs/libevm v1.13.15-0.20251002164226-35926db4d661 h1:lt4yQE1HMvxWrdD5RFj+h9kWUsZK2rmNohvkeQsbG9M= github.com/ava-labs/libevm v1.13.15-0.20251002164226-35926db4d661/go.mod h1:ivRC/KojP8sai7j8WnpXIReQpcRklL2bIzoysnjpARQ= github.com/ava-labs/subnet-evm v0.7.10-0.20251010143515-0e03d5e4c9c9 h1:qRKllyK9es8t8u/Q8QLPtvQiHmL1Ar2f60VYw4a3HMo= github.com/ava-labs/subnet-evm v0.7.10-0.20251010143515-0e03d5e4c9c9/go.mod h1:lcCfer4eo/Po0uVaTsxGFNMpYYpMBYAdKysaeVTJDFc= -github.com/aws/aws-sdk-go-v2 v1.37.0 h1:YtCOESR/pN4j5oA7cVHSfOwIcuh/KwHC4DOSXFbv5F0= -github.com/aws/aws-sdk-go-v2 v1.37.0/go.mod h1:9Q0OoGQoboYIAJyslFyF1f5K1Ryddop8gqMhWx/n4Wg= -github.com/aws/aws-sdk-go-v2/config v1.29.18 h1:x4T1GRPnqKV8HMJOMtNktbpQMl3bIsfx8KbqmveUO2I= -github.com/aws/aws-sdk-go-v2/config v1.29.18/go.mod h1:bvz8oXugIsH8K7HLhBv06vDqnFv3NsGDt2Znpk7zmOU= -github.com/aws/aws-sdk-go-v2/credentials v1.17.71 h1:r2w4mQWnrTMJjOyIsZtGp3R3XGY3nqHn8C26C2lQWgA= -github.com/aws/aws-sdk-go-v2/credentials v1.17.71/go.mod h1:E7VF3acIup4GB5ckzbKFrCK0vTvEQxOxgdq4U3vcMCY= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.33 h1:D9ixiWSG4lyUBL2DDNK924Px9V/NBVpML90MHqyTADY= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.33/go.mod h1:caS/m4DI+cij2paz3rtProRBI4s/+TCiWoaWZuQ9010= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.0 h1:H2iZoqW/v2Jnrh1FnU725Bq6KJ0k2uP63yH+DcY+HUI= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.0/go.mod h1:L0FqLbwMXHvNC/7crWV1iIxUlOKYZUE8KuTIA+TozAI= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.0 h1:EDped/rNzAhFPhVY0sDGbtD16OKqksfA8OjF/kLEgw8= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.0/go.mod h1:uUI335jvzpZRPpjYx6ODc/wg1qH+NnoSTK/FwVeK0C0= +github.com/aws/aws-sdk-go-v2 v1.39.2 h1:EJLg8IdbzgeD7xgvZ+I8M1e0fL0ptn/M47lianzth0I= +github.com/aws/aws-sdk-go-v2 v1.39.2/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY= +github.com/aws/aws-sdk-go-v2/config v1.31.12 h1:pYM1Qgy0dKZLHX2cXslNacbcEFMkDMl+Bcj5ROuS6p8= +github.com/aws/aws-sdk-go-v2/config v1.31.12/go.mod h1:/MM0dyD7KSDPR+39p9ZNVKaHDLb9qnfDurvVS2KAhN8= +github.com/aws/aws-sdk-go-v2/credentials v1.18.16 h1:4JHirI4zp958zC026Sm+V4pSDwW4pwLefKrc0bF2lwI= +github.com/aws/aws-sdk-go-v2/credentials v1.18.16/go.mod h1:qQMtGx9OSw7ty1yLclzLxXCRbrkjWAM7JnObZjmCB7I= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.9 h1:Mv4Bc0mWmv6oDuSWTKnk+wgeqPL5DRFu5bQL9BGPQ8Y= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.9/go.mod h1:IKlKfRppK2a1y0gy1yH6zD+yX5uplJ6UuPlgd48dJiQ= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9 h1:se2vOWGD3dWQUtfn4wEjRQJb1HK1XsNIt825gskZ970= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9/go.mod h1:hijCGH2VfbZQxqCDN7bwz/4dzxV+hkyhjawAtdPWKZA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9 h1:6RBnKZLkJM4hQ+kN6E7yWFveOTg8NLPHAkqrs4ZPlTU= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9/go.mod h1:V9rQKRmK7AWuEsOMnHzKj8WyrIir1yUJbZxDuZLFvXI= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4 h1:CXV68E2dNqhuynZJPB80bhPQwAKqBWVer887figW6Jc= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4/go.mod h1:/xFi9KtvBXP97ppCz1TAEvU1Uf66qvid89rbem3wCzQ= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.18 h1:vvbXsA2TVO80/KT7ZqCbx934dt6PY+vQ8hZpUZ/cpYg= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.18/go.mod h1:m2JJHledjBGNMsLOF1g9gbAxprzq3KjC8e4lxtn+eWg= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 h1:oegbebPEMA/1Jny7kvwejowCaHz1FWZAQ94WXFNCyTM= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1/go.mod h1:kemo5Myr9ac0U9JfSjMo9yHLtw+pECEHsFtJ9tqCEI8= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9 h1:5r34CgVOD4WZudeEKZ9/iKpiT6cM1JyEROpXjOcdWv8= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9/go.mod h1:dB12CEbNWPbzO2uC6QSWHteqOg4JfBVJOojbAoAUb5I= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.19 h1:O2xbipq7k1kTct69V7mFidwTagld9c/6iyK+3yo+QNg= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.19/go.mod h1:CxTOwBy2Qs8/+yV7fkz4eZB1RB5qeWaW9SvznvFLgRA= -github.com/aws/aws-sdk-go-v2/service/sso v1.25.6 h1:rGtWqkQbPk7Bkwuv3NzpE/scwwL9sC1Ul3tn9x83DUI= -github.com/aws/aws-sdk-go-v2/service/sso v1.25.6/go.mod h1:u4ku9OLv4TO4bCPdxf4fA1upaMaJmP9ZijGk3AAOC6Q= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.4 h1:OV/pxyXh+eMA0TExHEC4jyWdumLxNbzz1P0zJoezkJc= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.4/go.mod h1:8Mm5VGYwtm+r305FfPSuc+aFkrypeylGYhFim6XEPoc= -github.com/aws/aws-sdk-go-v2/service/sts v1.34.1 h1:aUrLQwJfZtwv3/ZNG2xRtEen+NqI3iesuacjP51Mv1s= -github.com/aws/aws-sdk-go-v2/service/sts v1.34.1/go.mod h1:3wFBZKoWnX3r+Sm7in79i54fBmNfwhdNdQuscCw7QIk= -github.com/aws/smithy-go v1.22.5 h1:P9ATCXPMb2mPjYBgueqJNCA5S9UfktsW0tTxi+a7eqw= -github.com/aws/smithy-go v1.22.5/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= +github.com/aws/aws-sdk-go-v2/service/sso v1.29.6 h1:A1oRkiSQOWstGh61y4Wc/yQ04sqrQZr1Si/oAXj20/s= +github.com/aws/aws-sdk-go-v2/service/sso v1.29.6/go.mod h1:5PfYspyCU5Vw1wNPsxi15LZovOnULudOQuVxphSflQA= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1 h1:5fm5RTONng73/QA73LhCNR7UT9RpFH3hR6HWL6bIgVY= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1/go.mod h1:xBEjWD13h+6nq+z4AkqSfSvqRKFgDIQeaMguAJndOWo= +github.com/aws/aws-sdk-go-v2/service/sts v1.38.6 h1:p3jIvqYwUZgu/XYeI48bJxOhvm47hZb5HUQ0tn6Q9kA= +github.com/aws/aws-sdk-go-v2/service/sts v1.38.6/go.mod h1:WtKK+ppze5yKPkZ0XwqIVWD4beCwv056ZbPQNoeHqM8= +github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE= +github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= @@ -147,8 +145,8 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= -github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI= @@ -214,8 +212,6 @@ github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiU github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= -github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= @@ -275,15 +271,13 @@ github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36j github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= -github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4= github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= @@ -344,8 +338,8 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.8/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.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= -github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= @@ -402,12 +396,9 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa 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.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.38.0 h1:c/WX+w8SLAinvuKKQFh77WEucCnPk4j2OTUr7lt7BeY= -github.com/onsi/gomega v1.38.0/go.mod h1:OcXcwId0b9QsE7Y49u+BTrL4IdKOBOKnD6VQNTJEB6o= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= -github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8= @@ -419,13 +410,13 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= -github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= -github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= +github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -443,8 +434,6 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k= -github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk= github.com/sanity-io/litter v1.5.1 h1:dwnrSypP6q56o3lFxTU+t2fwQ9A+U5qrXVO4Qg9KwVU= github.com/sanity-io/litter v1.5.1/go.mod h1:5Z71SvaYy5kcGtyglXOC9rrUi3c1E8CamFWjQsazTh0= github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= @@ -454,22 +443,14 @@ github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMT github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= -github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= -github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE= -github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= -github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= @@ -483,8 +464,6 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= -github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/supranational/blst v0.3.14 h1:xNMoHRJOTwMn63ip6qoWJ2Ymgvj7E2b9jY2FAwY+qRo= github.com/supranational/blst v0.3.14/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= @@ -533,34 +512,36 @@ github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= github.com/zondax/ledger-go v1.0.1 h1:Ks/2tz/dOF+dbRynfZ0dEhcdL1lqw43Sa0zMXHpQ3aQ= github.com/zondax/ledger-go v1.0.1/go.mod h1:j7IgMY39f30apthJYMd1YsHZRqdyu4KbVmUp0nU78X0= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= -go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 h1:EtFWSnwW9hGObjkIdmlnWSydO+Qs8OwzfzXLUPg4xOc= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0/go.mod h1:QjUEoiGCPkvFZ/MjK6ZZfNOS6mfVEVKYE99dFhuN2LI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0= -go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= -go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= -go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= -go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= -go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= -go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= -go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= -go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= -go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os= -go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= +go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= +go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= +go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/proto/otlp v1.8.0 h1:fRAZQDcAFHySxpJ1TwlA1cJ4tvcrw7nXl9xWWC8N5CE= +go.opentelemetry.io/proto/otlp v1.8.0/go.mod h1:tIeYOeNBU4cvmPqpaji1P+KbB4Oloai8wN4rWzRrFF0= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= -go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= +go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= golang.org/x/crypto v0.0.0-20170613210332-850760c427c5/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -574,8 +555,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= -golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= +golang.org/x/exp v0.0.0-20251002181428-27f1f14c8bb9 h1:TQwNpfvNkxAVlItJf6Cr5JTsVZoC/Sj7K3OZv2Pc14A= +golang.org/x/exp v0.0.0-20251002181428-27f1f14c8bb9/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -609,8 +590,8 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -701,18 +682,18 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:FiusG7LWj+4byqhbvmB+Q93B/mOxJLN2DTozDuZm4EU= -google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c h1:qXWI/sQtv5UKboZ/zUk7h+mrf/lXORyI+n9DKDAusdg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 h1:d8Nakh1G+ur7+P3GcMjpRDEkoLUcLW2iU92XVqR+XMQ= +google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090/go.mod h1:U8EXRNSd8sUYyDfs/It7KVWodQr+Hf9xtxyxWudSwEw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 h1:/OQuEa4YWtDt7uQWHd3q3sUMb+QOLQUg1xa8CEsRv5w= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= -google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A= +google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -724,8 +705,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= -google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/interchain/signature-aggregator.go b/interchain/signature-aggregator.go index 3ea2c86..132b0d0 100644 --- a/interchain/signature-aggregator.go +++ b/interchain/signature-aggregator.go @@ -13,7 +13,6 @@ import ( "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/vms/platformvm/warp" - "github.com/ava-labs/icm-services/signature-aggregator/api" "go.uber.org/zap" ) @@ -39,7 +38,7 @@ func SignMessage( } else if quorumPercentage > 100 { return nil, fmt.Errorf("quorum percentage cannot be greater than 100") } - request := api.AggregateSignatureRequest{ + request := AggregateSignatureRequest{ Message: message, SigningSubnetID: signingSubnetID, QuorumPercentage: quorumPercentage, @@ -118,7 +117,7 @@ func SignMessage( continue } - var response api.AggregateSignatureResponse + var response AggregateSignatureResponse if err := json.Unmarshal(body, &response); err != nil { lastErr = fmt.Errorf("failed to parse response: %w", err) logger.Error("Error parsing response", diff --git a/interchain/signature_aggregator_types.go b/interchain/signature_aggregator_types.go new file mode 100644 index 0000000..48923dd --- /dev/null +++ b/interchain/signature_aggregator_types.go @@ -0,0 +1,19 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package interchain + +// AggregateSignatureRequest is the request structure for the signature aggregator API. +// This is a local copy to avoid dependency issues with icm-services. +type AggregateSignatureRequest struct { + Message string `json:"message"` + SigningSubnetID string `json:"signingSubnetID"` + QuorumPercentage uint64 `json:"quorumPercentage"` + Justification string `json:"justification"` +} + +// AggregateSignatureResponse is the response structure from the signature aggregator API. +// This is a local copy to avoid dependency issues with icm-services. +type AggregateSignatureResponse struct { + SignedMessage string `json:"signedMessage"` +} From a38205c15a9b357ead2f9b1b5d8559c368aa1dea Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 10 Oct 2025 15:48:53 -0300 Subject: [PATCH 05/10] regenerate cubesigner mock with correct source path Updates the generated mock to reference the correct source package path and adds required golang.org/x/tools dependency for mockgen. --- go.mod | 2 ++ go.sum | 2 ++ keychain/cubesigner/cubesignermock/cubesigner_client.go | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index c0ad872..30d9e09 100644 --- a/go.mod +++ b/go.mod @@ -122,12 +122,14 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect golang.org/x/crypto v0.42.0 // indirect + golang.org/x/mod v0.28.0 // indirect golang.org/x/net v0.45.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/term v0.35.0 // indirect golang.org/x/text v0.29.0 // indirect golang.org/x/time v0.12.0 // indirect + golang.org/x/tools v0.37.0 // indirect gonum.org/v1/gonum v0.16.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect diff --git a/go.sum b/go.sum index 8d0cb5d..7cd5f55 100644 --- a/go.sum +++ b/go.sum @@ -668,6 +668,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= +golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/keychain/cubesigner/cubesignermock/cubesigner_client.go b/keychain/cubesigner/cubesignermock/cubesigner_client.go index b2ce1b1..f1b15c9 100644 --- a/keychain/cubesigner/cubesignermock/cubesigner_client.go +++ b/keychain/cubesigner/cubesignermock/cubesigner_client.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/ava-labs/avalanchego/utils/crypto/keychain/cubesigner (interfaces: CubeSignerClient) +// Source: github.com/ava-labs/avalanche-tooling-sdk-go/keychain/cubesigner (interfaces: CubeSignerClient) // // Generated by this command: // From 5a4fce917374b2aaf86277ef22fa8a3409840a4c Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 10 Oct 2025 16:38:13 -0300 Subject: [PATCH 06/10] Add ledger keychain implementation Implements a keychain that integrates with Ledger hardware wallets for secure transaction signing with physical device confirmation. Features: - Hardware-secured transaction signing via Ledger device - Support for P-Chain, X-Chain, and C-Chain operations - Compatible with keychain.Keychain and c.EthKeychain interfaces - Automatic key derivation using BIP-44 path (m/44'/9000'/0'/0/n) - Support for both full transaction signing and hash signing - Retry logic for transient USB communication errors The implementation uses github.com/ava-labs/ledger-avalanche-go for hardware wallet communication. --- go.mod | 8 +- go.sum | 2 - keychain/ledger/README.md | 252 ++++ .../ledger/examples/validate-ledger-txs.go | 1303 +++++++++++++++++ keychain/ledger/ledger.go | 22 + keychain/ledger/ledger_device.go | 230 +++ keychain/ledger/ledger_device_test.go | 75 + keychain/ledger/ledger_keychain.go | 215 +++ keychain/ledger/ledger_keychain_test.go | 841 +++++++++++ keychain/ledger/ledgermock/ledger.go | 131 ++ keychain/ledger/mocks_generate_test.go | 6 + 11 files changed, 3078 insertions(+), 7 deletions(-) create mode 100644 keychain/ledger/README.md create mode 100644 keychain/ledger/examples/validate-ledger-txs.go create mode 100644 keychain/ledger/ledger.go create mode 100644 keychain/ledger/ledger_device.go create mode 100644 keychain/ledger/ledger_device_test.go create mode 100644 keychain/ledger/ledger_keychain.go create mode 100644 keychain/ledger/ledger_keychain_test.go create mode 100644 keychain/ledger/ledgermock/ledger.go create mode 100644 keychain/ledger/mocks_generate_test.go diff --git a/go.mod b/go.mod index 30d9e09..ad9fa16 100644 --- a/go.mod +++ b/go.mod @@ -4,12 +4,15 @@ go 1.24.8 require ( github.com/ava-labs/avalanchego v1.13.6-0.20251010155017-cd3e6bee30ab + github.com/ava-labs/coreth v0.15.4-rc.3.0.20251002221438-a857a64c28ea + github.com/ava-labs/ledger-avalanche-go v1.1.0 github.com/ava-labs/libevm v1.13.15-0.20251002164226-35926db4d661 github.com/ava-labs/subnet-evm v0.7.10-0.20251010143515-0e03d5e4c9c9 github.com/cavaliergopher/grab/v3 v3.0.1 github.com/cubist-labs/cubesigner-go-sdk v0.0.16 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 github.com/stretchr/testify v1.11.1 + github.com/tyler-smith/go-bip32 v1.0.0 go.uber.org/mock v0.6.0 go.uber.org/zap v1.27.0 golang.org/x/exp v0.0.0-20251002181428-27f1f14c8bb9 @@ -25,9 +28,7 @@ require ( github.com/StephenButtolph/canoto v0.17.2 // indirect github.com/VictoriaMetrics/fastcache v1.12.1 // indirect github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect - github.com/ava-labs/coreth v0.15.4-rc.3.0.20251002221438-a857a64c28ea // indirect github.com/ava-labs/firewood-go-ethhash/ffi v0.0.12 // indirect - github.com/ava-labs/ledger-avalanche-go v1.1.0 // indirect github.com/aws/aws-sdk-go-v2 v1.39.2 // indirect github.com/aws/aws-sdk-go-v2/config v1.31.12 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.18.16 // indirect @@ -105,7 +106,6 @@ require ( github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect github.com/tklauser/go-sysconf v0.3.15 // indirect github.com/tklauser/numcpus v0.10.0 // indirect - github.com/tyler-smith/go-bip32 v1.0.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/zondax/golem v0.27.0 // indirect github.com/zondax/hid v0.9.2 // indirect @@ -122,14 +122,12 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect golang.org/x/crypto v0.42.0 // indirect - golang.org/x/mod v0.28.0 // indirect golang.org/x/net v0.45.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/term v0.35.0 // indirect golang.org/x/text v0.29.0 // indirect golang.org/x/time v0.12.0 // indirect - golang.org/x/tools v0.37.0 // indirect gonum.org/v1/gonum v0.16.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect diff --git a/go.sum b/go.sum index 7cd5f55..8d0cb5d 100644 --- a/go.sum +++ b/go.sum @@ -668,8 +668,6 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= -golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/keychain/ledger/README.md b/keychain/ledger/README.md new file mode 100644 index 0000000..0adfb65 --- /dev/null +++ b/keychain/ledger/README.md @@ -0,0 +1,252 @@ +# Ledger Keychain + +This Go package provides a keychain implementation for Avalanche that integrates with [Ledger](https://www.ledger.com/) hardware wallets, enabling secure transaction signing with physical device confirmation. + +The Ledger keychain allows users to sign Avalanche transactions using their Ledger hardware wallet, keeping private keys secure on the device while maintaining full compatibility with the Avalanche SDK. + +## Features + +* Hardware-secured transaction signing via Ledger device +* Support for P-Chain, X-Chain, and C-Chain operations +* Compatible with Avalanche's `keychain.Keychain` and `c.EthKeychain` interfaces +* Automatic key derivation using BIP-44 path (m/44'/9000'/0'/0/n) +* Support for both full transaction signing and hash signing +* Retry logic for transient USB communication errors + +## Prerequisites + +* Ledger Nano S, Nano S Plus, Nano X, Stax, or Flex device +* [Avalanche app](https://github.com/ava-labs/ledger-avalanche) installed on the Ledger device +* Device must be unlocked and Avalanche app must be open + +## Usage Example + +### Basic Setup and Subnet Creation + +```go +package main + +import ( + "context" + "fmt" + "log" + "time" + + "github.com/ava-labs/avalanche-tooling-sdk-go/keychain/ledger" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/avalanchego/wallet/subnet/primary" + "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" +) + +func main() { + // Connect to Ledger device + // Make sure your Ledger is connected, unlocked, and the Avalanche app is open + fmt.Println("Connecting to Ledger device...") + device, err := ledger.New() + if err != nil { + log.Fatalf("Failed to connect to Ledger: %v", err) + } + defer device.Disconnect() + + // Create keychain using the first address (index 0) + kc, err := ledger.NewKeychain(device, 1) + if err != nil { + log.Fatalf("Failed to create keychain: %v", err) + } + + // Create wallet on Fuji testnet + ctx := context.Background() + wallet, err := primary.MakeWallet( + ctx, + primary.FujiAPIURI, + kc, + kc, + primary.WalletConfig{}, + ) + if err != nil { + log.Fatalf("Failed to create wallet: %v", err) + } + + // Create subnet with threshold of 1 + subnetOwnerAddrs := kc.Addresses().List() + owner := &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: subnetOwnerAddrs, + } + + fmt.Println("Building subnet creation transaction...") + fmt.Println("*** Please confirm the transaction on your Ledger device ***") + + // Sign and issue create subnet transaction + signCtx, cancel := context.WithTimeout(ctx, 2*time.Minute) + defer cancel() + + createSubnetTx, err := wallet.P().IssueCreateSubnetTx( + owner, + common.WithContext(signCtx), + ) + if err != nil { + log.Fatalf("Failed to create subnet: %v", err) + } + + fmt.Printf("✓ Subnet created with ID: %s\n", createSubnetTx.ID()) +} +``` + +### Using Specific Address Indices + +```go +// Create keychain with specific indices (e.g., addresses at indices 0, 5, and 10) +kc, err := ledger.NewKeychainFromIndices(device, []uint32{0, 5, 10}) +if err != nil { + log.Fatalf("Failed to create keychain: %v", err) +} + +// Get all addresses managed by this keychain +addresses := kc.Addresses() +fmt.Printf("Managing %d addresses\n", addresses.Len()) +``` + +### Cross-Chain Transfer (P-Chain to C-Chain) + +```go +package main + +import ( + "context" + "fmt" + "log" + "time" + + "github.com/ava-labs/avalanche-tooling-sdk-go/keychain/ledger" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/units" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/avalanchego/wallet/subnet/primary" + "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" +) + +func main() { + // Connect to Ledger + device, err := ledger.New() + if err != nil { + log.Fatalf("Failed to connect to Ledger: %v", err) + } + defer device.Disconnect() + + // Create keychain + kc, err := ledger.NewKeychain(device, 1) + if err != nil { + log.Fatalf("Failed to create keychain: %v", err) + } + + // Create wallet + ctx := context.Background() + wallet, err := primary.MakeWallet( + ctx, + primary.FujiAPIURI, + kc, + kc, + primary.WalletConfig{}, + ) + if err != nil { + log.Fatalf("Failed to create wallet: %v", err) + } + + // Get chain and asset IDs + cChainID := wallet.C().Builder().Context().BlockchainID + avaxAssetID := wallet.P().Builder().Context().AVAXAssetID + + // Export 0.5 AVAX from P-Chain to C-Chain + exportOwner := &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: kc.Addresses().List(), + } + + fmt.Println("Exporting from P-Chain...") + fmt.Println("*** Please confirm the transaction on your Ledger device ***") + + exportTx, err := wallet.P().IssueExportTx( + cChainID, + []*avax.TransferableOutput{{ + Asset: avax.Asset{ID: avaxAssetID}, + Out: &secp256k1fx.TransferOutput{ + Amt: units.Avax / 2, // 0.5 AVAX + OutputOwners: *exportOwner, + }, + }}, + common.WithContext(ctx), + ) + if err != nil { + log.Fatalf("Failed to export from P-Chain: %v", err) + } + + fmt.Printf("✓ Exported from P-Chain, tx ID: %s\n", exportTx.ID()) + + // Wait for export to be processed + time.Sleep(10 * time.Second) + + // Import on C-Chain + // Get Ethereum address from the Ledger-derived public key + pubKey, err := device.PubKey(0) + if err != nil { + log.Fatalf("Failed to get public key: %v", err) + } + cChainAddr := pubKey.EthAddress() + + fmt.Println("Importing on C-Chain...") + fmt.Println("*** Please confirm the transaction on your Ledger device ***") + + importTx, err := wallet.C().IssueImportTx( + constants.PlatformChainID, + cChainAddr, + common.WithContext(ctx), + ) + if err != nil { + log.Fatalf("Failed to import on C-Chain: %v", err) + } + + fmt.Printf("✓ Imported on C-Chain, tx ID: %s\n", importTx.ID()) +} +``` + +## Comprehensive Example + +For a complete example demonstrating all supported transaction types across P-Chain, X-Chain, and C-Chain, see [examples/validate-ledger-txs.go](examples/validate-ledger-txs.go). + +This example includes: +- All P-Chain transaction types (CreateSubnet, ConvertSubnetToL1, AddValidator, etc.) +- X-Chain transactions (BaseTx, CreateAsset, Import/Export) +- C-Chain atomic transactions (Import/Export) +- L1 validator operations (Register, SetWeight, IncreaseBalance, Disable) + +## Integration with Avalanche Wallet + +The Ledger keychain implements both `keychain.Keychain` and `c.EthKeychain` interfaces, making it compatible with the Avalanche primary wallet: + +```go +wallet, err := primary.MakeWallet( + ctx, + endpoint, + ledgerKeychain, // For P/X chain operations + ledgerKeychain, // For C chain operations (EthKeychain) + primary.WalletConfig{}, +) +``` + +## Device Connection + +The `New()` function automatically: +1. Detects connected Ledger devices +2. Connects to the Avalanche app +3. Returns a `Device` that implements the `Ledger` interface + +Always remember to call `Disconnect()` when done: +```go +device, err := ledger.New() +if err != nil { + log.Fatal(err) +} +defer device.Disconnect() +``` diff --git a/keychain/ledger/examples/validate-ledger-txs.go b/keychain/ledger/examples/validate-ledger-txs.go new file mode 100644 index 0000000..a198eb6 --- /dev/null +++ b/keychain/ledger/examples/validate-ledger-txs.go @@ -0,0 +1,1303 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +// validate-ledger-txs is a comprehensive example demonstrating how to use the Ledger keychain +// to build and sign all supported Avalanche transaction types across P-Chain, X-Chain, and C-Chain. +// +// This example covers: +// - P-Chain: Subnet operations (CreateSubnet, ConvertSubnetToL1), validator management +// (AddValidator, AddSubnetValidator, RemoveSubnetValidator), L1 validator operations +// (RegisterL1Validator, SetL1ValidatorWeight, IncreaseL1ValidatorBalance, DisableL1Validator), +// cross-chain transfers (ExportTx, ImportTx), and ownership transfers (TransferSubnetOwnership) +// - X-Chain: Asset operations (BaseTx, CreateAssetTx, OperationTx) and cross-chain transfers +// (ImportTx, ExportTx) +// - C-Chain: Atomic transactions for cross-chain transfers (ImportTx, ExportTx) +// +// The example connects to both a local Avalanche network and Fuji testnet. The local network is +// used for P-Chain operations, while Fuji testnet is used for X-Chain and C-Chain operations +// (as custom local network operations for X-Chain and C-Chain are not yet implemented). +// Some transactions are issued to the network while others are only built and signed for validation purposes. +// +// Prerequisites: +// - Ledger device connected and unlocked +// - Avalanche app open on the Ledger +// - Local Avalanche network running +// - Sufficient AVAX balance on local network for the Ledger address +// - Sufficient AVAX balance on Fuji testnet for the Ledger address +package main + +import ( + "context" + "fmt" + "log" + "math/big" + "time" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/crypto/bls/signer/localsigner" + "github.com/ava-labs/avalanchego/vms/platformvm/signer" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "github.com/ava-labs/avalanchego/vms/platformvm/warp" + "github.com/ava-labs/avalanchego/vms/platformvm/warp/message" + "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/avalanchego/wallet/subnet/primary" + "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" + avmtxs "github.com/ava-labs/avalanchego/vms/avm/txs" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/components/verify" + "github.com/ava-labs/coreth/plugin/evm/atomic" + ethcommon "github.com/ava-labs/libevm/common" + + "github.com/ava-labs/avalanche-tooling-sdk-go/keychain/ledger" +) + +const ( + localEndpoint = "http://localhost:9650" + fujiEndpoint = "https://api.avax-test.network" +) + +func main() { + fmt.Println("Setting up Ledger connection...") + fmt.Println("Make sure your Ledger device is connected and the Avalanche app is open.") + + ledgerDevice, err := ledger.New() + if err != nil { + log.Fatalf("Failed to connect to Ledger device: %v", err) + } + defer ledgerDevice.Disconnect() + + fmt.Println("Ledger device connected successfully!") + + kc, err := ledger.NewKeychainFromIndices(ledgerDevice, []uint32{0}) + if err != nil { + log.Fatalf("Failed to create ledger keychain: %v", err) + } + + fmt.Println("\nCreating wallet context...") + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + wallet, err := primary.MakeWallet( + ctx, + localEndpoint, + kc, + kc, + primary.WalletConfig{}, + ) + if err != nil { + log.Fatalf("Failed to create wallet: %v", err) + } + + fmt.Println("Wallet created successfully!") + + fmt.Println("\nCreating Fuji wallet context...") + fujiCtx, fujiCancel := context.WithTimeout(context.Background(), 30*time.Second) + defer fujiCancel() + + fujiWallet, err := primary.MakeWallet( + fujiCtx, + fujiEndpoint, + kc, + kc, + primary.WalletConfig{}, + ) + if err != nil { + log.Fatalf("Failed to create fuji wallet: %v", err) + } + fmt.Println("Fuji wallet created successfully!") + + controlKeys := kc.Addresses().List() + threshold := uint32(1) + + owners := &secp256k1fx.OutputOwners{ + Addrs: controlKeys, + Threshold: threshold, + Locktime: 0, + } + + fmt.Println("\n=== P-Chain Transaction Examples ===") + + // 1. CreateSubnet - Build, Sign and Issue + fmt.Println("\n1. Building and issuing CreateSubnet transaction...") + createSubnetTx, err := buildAndSignCreateSubnetTx(wallet, owners) + if err != nil { + log.Fatalf("Failed to create CreateSubnet tx: %v", err) + } + fmt.Printf(" Tx ID: %s\n", createSubnetTx.ID()) + + fmt.Println(" Issuing CreateSubnet transaction to network...") + issueCtx, issueCancel := context.WithTimeout(context.Background(), 30*time.Second) + defer issueCancel() + + if err := wallet.P().IssueTx(createSubnetTx, common.WithContext(issueCtx)); err != nil { + log.Fatalf("Failed to issue CreateSubnet tx: %v", err) + } + fmt.Println(" ✓ CreateSubnet transaction issued successfully!") + + // Use the CreateSubnet tx ID as the subnet ID for subsequent operations + subnetID := createSubnetTx.ID() + fmt.Printf(" Subnet ID: %s\n", subnetID) + + // 2. CreateChain + fmt.Println("\n2. Building CreateChain transaction...") + createChainTx, err := buildAndSignCreateChainTx(wallet, subnetID) + if err != nil { + log.Fatalf("Failed to create CreateChain tx: %v", err) + } + fmt.Printf(" Tx ID: %s\n", createChainTx.ID()) + + // 3. AddSubnetValidator + fmt.Println("\n3. Building AddSubnetValidator transaction...") + addValidatorTx, err := buildAndSignAddSubnetValidatorTx(wallet, subnetID, controlKeys[0]) + if err != nil { + log.Fatalf("Failed to create AddSubnetValidator tx: %v", err) + } + fmt.Printf(" Tx ID: %s\n", addValidatorTx.ID()) + + // 4. RemoveSubnetValidator + fmt.Println("\n4. Building RemoveSubnetValidator transaction...") + removeValidatorTx, err := buildAndSignRemoveSubnetValidatorTx(wallet, subnetID) + if err != nil { + log.Fatalf("Failed to create RemoveSubnetValidator tx: %v", err) + } + fmt.Printf(" Tx ID: %s\n", removeValidatorTx.ID()) + + // 5. TransferSubnetOwnership + fmt.Println("\n5. Building TransferSubnetOwnership transaction...") + transferOwnershipTx, err := buildAndSignTransferSubnetOwnershipTx(wallet, subnetID, owners) + if err != nil { + log.Fatalf("Failed to create TransferSubnetOwnership tx: %v", err) + } + fmt.Printf(" Tx ID: %s\n", transferOwnershipTx.ID()) + + // 6. ConvertSubnetToL1 - Build, Sign and Issue + fmt.Println("\n6. Building and issuing ConvertSubnetToL1 transaction...") + convertL1Tx, err := buildAndSignConvertSubnetToL1Tx(wallet, subnetID, controlKeys) + if err != nil { + log.Fatalf("Failed to create ConvertSubnetToL1 tx: %v", err) + } + fmt.Printf(" Tx ID: %s\n", convertL1Tx.ID()) + + fmt.Println(" Issuing ConvertSubnetToL1 transaction to network...") + convertIssueCtx, convertIssueCancel := context.WithTimeout(context.Background(), 30*time.Second) + defer convertIssueCancel() + + if err := wallet.P().IssueTx(convertL1Tx, common.WithContext(convertIssueCtx)); err != nil { + log.Fatalf("Failed to issue ConvertSubnetToL1 tx: %v", err) + } + fmt.Println(" ✓ ConvertSubnetToL1 transaction issued successfully!") + + // Generate validation ID for the first bootstrap validator (index 0) + // This follows the pattern: validationID = subnetID.Append(index) + validationID := subnetID.Append(0) + fmt.Printf(" Bootstrap Validation ID (index 0): %s\n", validationID) + + // 7. BaseTx + fmt.Println("\n7. Building BaseTx transaction...") + baseTx, err := buildAndSignBaseTx(wallet, controlKeys[0]) + if err != nil { + log.Fatalf("Failed to create BaseTx: %v", err) + } + fmt.Printf(" Tx ID: %s\n", baseTx.ID()) + + // 8. AddValidator (Primary Network) + fmt.Println("\n8. Building AddValidator transaction...") + addPrimaryValidatorTx, err := buildAndSignAddValidatorTx(wallet, controlKeys[0]) + if err != nil { + log.Fatalf("Failed to create AddValidator tx: %v", err) + } + fmt.Printf(" Tx ID: %s\n", addPrimaryValidatorTx.ID()) + + // 9. AddDelegator + fmt.Println("\n9. Building AddDelegator transaction...") + addDelegatorTx, err := buildAndSignAddDelegatorTx(wallet, controlKeys[0]) + if err != nil { + log.Fatalf("Failed to create AddDelegator tx: %v", err) + } + fmt.Printf(" Tx ID: %s\n", addDelegatorTx.ID()) + + // 10. ExportTx (P-Chain to X-Chain) - Using Fuji network for known chain IDs + fmt.Println("\n10. Building and issuing ExportTx transaction (using Fuji network)...") + exportTx, err := buildAndSignPChainExportTx(fujiWallet, owners, fujiWallet.X().Builder().Context().BlockchainID) + if err != nil { + log.Fatalf("Failed to create ExportTx: %v", err) + } + fmt.Printf(" Tx ID: %s\n", exportTx.ID()) + + fmt.Println(" Issuing P-Chain ExportTx to network...") + pExportIssueCtx, pExportIssueCancel := context.WithTimeout(context.Background(), 30*time.Second) + defer pExportIssueCancel() + + if err := fujiWallet.P().IssueTx(exportTx, common.WithContext(pExportIssueCtx)); err != nil { + log.Fatalf("Failed to issue P-Chain ExportTx: %v", err) + } + fmt.Println(" ✓ P-Chain ExportTx issued successfully!") + + // 11. X-Chain Import (from P-Chain) + fmt.Println("\n11. Building and issuing X-Chain ImportTx (from P-Chain)...") + xChainImportTx, err := buildAndSignXChainImportTx(fujiWallet, owners, constants.PlatformChainID) + if err != nil { + log.Fatalf("Failed to create X-Chain ImportTx: %v", err) + } + fmt.Printf(" Tx ID: %s\n", xChainImportTx.ID()) + + fmt.Println(" Issuing X-Chain ImportTx to network...") + xImportIssueCtx, xImportIssueCancel := context.WithTimeout(context.Background(), 30*time.Second) + defer xImportIssueCancel() + + if err := fujiWallet.X().IssueTx(xChainImportTx, common.WithContext(xImportIssueCtx)); err != nil { + log.Fatalf("Failed to issue X-Chain ImportTx: %v", err) + } + fmt.Println(" ✓ X-Chain ImportTx issued successfully!") + + // 12. X-Chain Export (required for ImportTx) + fmt.Println("\n12. Building and issuing X-Chain ExportTx (to enable P-Chain import)...") + xChainExportTx, err := buildAndSignXChainExportTx(fujiWallet, owners, constants.PlatformChainID) + if err != nil { + log.Fatalf("Failed to create X-Chain ExportTx: %v", err) + } + fmt.Printf(" Tx ID: %s\n", xChainExportTx.ID()) + + fmt.Println(" Issuing X-Chain ExportTx to network...") + xExportIssueCtx, xExportIssueCancel := context.WithTimeout(context.Background(), 30*time.Second) + defer xExportIssueCancel() + + if err := fujiWallet.X().IssueTx(xChainExportTx, common.WithContext(xExportIssueCtx)); err != nil { + log.Fatalf("Failed to issue X-Chain ExportTx: %v", err) + } + fmt.Println(" ✓ X-Chain ExportTx issued successfully!") + + // 13. X-Chain BaseTx + fmt.Println("\n13. Building X-Chain BaseTx (without issuance)...") + xChainBaseTx, err := buildAndSignXChainBaseTx(fujiWallet, controlKeys[0]) + if err != nil { + log.Fatalf("Failed to create X-Chain BaseTx: %v", err) + } + fmt.Printf(" Tx ID: %s\n", xChainBaseTx.ID()) + + // 14. X-Chain CreateAssetTx + fmt.Println("\n14. Building X-Chain CreateAssetTx (without issuance)...") + xChainCreateAssetTx, err := buildAndSignXChainCreateAssetTx(fujiWallet, controlKeys[0]) + if err != nil { + log.Fatalf("Failed to create X-Chain CreateAssetTx: %v", err) + } + fmt.Printf(" Tx ID: %s\n", xChainCreateAssetTx.ID()) + + // 15. X-Chain OperationTx + fmt.Println("\n15. Building X-Chain OperationTx (without issuance)...") + xChainOperationTx, err := buildAndSignXChainOperationTx(fujiWallet) + if err != nil { + log.Fatalf("Failed to create X-Chain OperationTx: %v", err) + } + fmt.Printf(" Tx ID: %s\n", xChainOperationTx.ID()) + + // 16. P-Chain ExportTx to C-Chain + fmt.Println("\n16. Building and issuing P-Chain ExportTx to C-Chain...") + cChainID := fujiWallet.C().Builder().Context().BlockchainID + pToC_ExportTx, err := buildAndSignPChainExportTx(fujiWallet, owners, cChainID) + if err != nil { + log.Fatalf("Failed to create P-Chain ExportTx to C-Chain: %v", err) + } + fmt.Printf(" Tx ID: %s\n", pToC_ExportTx.ID()) + + fmt.Println(" Issuing P-Chain ExportTx to C-Chain to network...") + pToC_ExportIssueCtx, pToC_ExportIssueCancel := context.WithTimeout(context.Background(), 30*time.Second) + defer pToC_ExportIssueCancel() + + if err := fujiWallet.P().IssueTx(pToC_ExportTx, common.WithContext(pToC_ExportIssueCtx)); err != nil { + log.Fatalf("Failed to issue P-Chain ExportTx to C-Chain: %v", err) + } + fmt.Println(" ✓ P-Chain ExportTx to C-Chain issued successfully!") + + // 17. C-Chain ImportTx from P-Chain + fmt.Println("\n17. Building and issuing C-Chain ImportTx from P-Chain...") + pubKey, err := ledgerDevice.PubKey(0) + if err != nil { + log.Fatalf("Failed to get pubKey from Ledger: %v", err) + } + ethAddr := pubKey.EthAddress() + fmt.Println("ETH ADDR:", ethAddr) + cChainImportTx, err := buildAndSignCChainImportTx(fujiWallet, ethAddr, constants.PlatformChainID) + if err != nil { + log.Fatalf("Failed to create C-Chain ImportTx: %v", err) + } + fmt.Printf(" Tx ID: %s\n", cChainImportTx.ID()) + + fmt.Println(" Issuing C-Chain ImportTx to network...") + cImportIssueCtx, cImportIssueCancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cImportIssueCancel() + + if err := fujiWallet.C().IssueAtomicTx(cChainImportTx, common.WithContext(cImportIssueCtx)); err != nil { + log.Fatalf("Failed to issue C-Chain ImportTx: %v", err) + } + fmt.Println(" ✓ C-Chain ImportTx issued successfully!") + + // 18. C-Chain ExportTx to P-Chain + fmt.Println("\n18. Building and issuing C-Chain ExportTx to P-Chain...") + cChainExportTx, err := buildAndSignCChainExportTx(fujiWallet, owners, constants.PlatformChainID) + if err != nil { + log.Fatalf("Failed to create C-Chain ExportTx: %v", err) + } + fmt.Printf(" Tx ID: %s\n", cChainExportTx.ID()) + + fmt.Println(" Issuing C-Chain ExportTx to network...") + cExportIssueCtx, cExportIssueCancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cExportIssueCancel() + + if err := fujiWallet.C().IssueAtomicTx(cChainExportTx, common.WithContext(cExportIssueCtx)); err != nil { + log.Fatalf("Failed to issue C-Chain ExportTx: %v", err) + } + fmt.Println(" ✓ C-Chain ExportTx issued successfully!") + + // 19. P-Chain ImportTx from C-Chain + fmt.Println("\n19. Building and issuing P-Chain ImportTx from C-Chain...") + cToP_ImportTx, err := buildAndSignPChainImportTx(fujiWallet, owners, cChainID) + if err != nil { + log.Fatalf("Failed to create P-Chain ImportTx from C-Chain: %v", err) + } + fmt.Printf(" Tx ID: %s\n", cToP_ImportTx.ID()) + + fmt.Println(" Issuing P-Chain ImportTx from C-Chain to network...") + cToP_ImportIssueCtx, cToP_ImportIssueCancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cToP_ImportIssueCancel() + + if err := fujiWallet.P().IssueTx(cToP_ImportTx, common.WithContext(cToP_ImportIssueCtx)); err != nil { + log.Fatalf("Failed to issue P-Chain ImportTx from C-Chain: %v", err) + } + fmt.Println(" ✓ P-Chain ImportTx from C-Chain issued successfully!") + + // 20. P-Chain ImportTx from X-Chain + fmt.Println("\n20. Building and issuing P-Chain ImportTx from X-Chain...") + importTx, err := buildAndSignPChainImportTx(fujiWallet, owners, fujiWallet.X().Builder().Context().BlockchainID) + if err != nil { + log.Fatalf("Failed to create ImportTx: %v", err) + } + fmt.Printf(" Tx ID: %s\n", importTx.ID()) + + fmt.Println(" Issuing P-Chain ImportTx to network...") + pImportIssueCtx, pImportIssueCancel := context.WithTimeout(context.Background(), 30*time.Second) + defer pImportIssueCancel() + + if err := fujiWallet.P().IssueTx(importTx, common.WithContext(pImportIssueCtx)); err != nil { + log.Fatalf("Failed to issue P-Chain ImportTx: %v", err) + } + fmt.Println(" ✓ P-Chain ImportTx issued successfully!") + + // 21. AddPermissionlessValidator + fmt.Println("\n21. Building AddPermissionlessValidator transaction...") + addPermissionlessValidatorTx, err := buildAndSignAddPermissionlessValidatorTx(wallet, subnetID, controlKeys[0]) + if err != nil { + log.Fatalf("Failed to create AddPermissionlessValidator tx: %v", err) + } + fmt.Printf(" Tx ID: %s\n", addPermissionlessValidatorTx.ID()) + + // 22. AddPermissionlessDelegator + fmt.Println("\n22. Building AddPermissionlessDelegator transaction...") + addPermissionlessDelegatorTx, err := buildAndSignAddPermissionlessDelegatorTx(wallet, subnetID, controlKeys[0]) + if err != nil { + log.Fatalf("Failed to create AddPermissionlessDelegator tx: %v", err) + } + fmt.Printf(" Tx ID: %s\n", addPermissionlessDelegatorTx.ID()) + + // 23. TransformSubnet + fmt.Println("\n23. Building TransformSubnet transaction...") + transformSubnetTx, err := buildAndSignTransformSubnetTx(wallet, subnetID) + if err != nil { + log.Fatalf("Failed to create TransformSubnet tx: %v", err) + } + fmt.Printf(" Tx ID: %s\n", transformSubnetTx.ID()) + + // 24. RegisterL1Validator + fmt.Println("\n24. Building RegisterL1Validator transaction...") + registerL1ValidatorTx, err := buildAndSignRegisterL1ValidatorTx(wallet) + if err != nil { + log.Fatalf("Failed to create RegisterL1Validator tx: %v", err) + } + fmt.Printf(" Tx ID: %s\n", registerL1ValidatorTx.ID()) + + // 25. SetL1ValidatorWeight + fmt.Println("\n25. Building SetL1ValidatorWeight transaction...") + setL1ValidatorWeightTx, err := buildAndSignSetL1ValidatorWeightTx(wallet) + if err != nil { + log.Fatalf("Failed to create SetL1ValidatorWeight tx: %v", err) + } + fmt.Printf(" Tx ID: %s\n", setL1ValidatorWeightTx.ID()) + + // 26. IncreaseL1ValidatorBalance + fmt.Println("\n26. Building IncreaseL1ValidatorBalance transaction...") + increaseL1ValidatorBalanceTx, err := buildAndSignIncreaseL1ValidatorBalanceTx(wallet, validationID) + if err != nil { + log.Fatalf("Failed to create IncreaseL1ValidatorBalance tx: %v", err) + } + fmt.Printf(" Tx ID: %s\n", increaseL1ValidatorBalanceTx.ID()) + + // 27. DisableL1Validator + fmt.Println("\n27. Building DisableL1Validator transaction...") + disableL1ValidatorTx, err := buildAndSignDisableL1ValidatorTx(wallet, validationID) + if err != nil { + log.Fatalf("Failed to create DisableL1Validator tx: %v", err) + } + fmt.Printf(" Tx ID: %s\n", disableL1ValidatorTx.ID()) + + fmt.Println("\n✓ All transactions created and signed successfully!") + fmt.Println("Note: Some transactions were issued to the network for demonstration. Others were only built and signed.") +} + +func buildAndSignCreateSubnetTx(wallet *primary.Wallet, owners *secp256k1fx.OutputOwners) (*txs.Tx, error) { + unsignedTx, err := wallet.P().Builder().NewCreateSubnetTx(owners) + if err != nil { + return nil, fmt.Errorf("failed to build tx: %w", err) + } + + tx := &txs.Tx{Unsigned: unsignedTx} + + fmt.Println(" *** Please sign CreateSubnet transaction on your Ledger device ***") + signCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + if err := wallet.P().Signer().Sign(signCtx, tx); err != nil { + return nil, fmt.Errorf("failed to sign tx: %w", err) + } + + return tx, nil +} + +func buildAndSignCreateChainTx(wallet *primary.Wallet, subnetID ids.ID) (*txs.Tx, error) { + genesis := []byte("{\"config\":{\"chainId\":1}}") + vmID := ids.GenerateTestID() + fxIDs := []ids.ID{} + chainName := "example-chain" + + unsignedTx, err := wallet.P().Builder().NewCreateChainTx( + subnetID, + genesis, + vmID, + fxIDs, + chainName, + ) + if err != nil { + return nil, fmt.Errorf("failed to build tx: %w", err) + } + + tx := &txs.Tx{Unsigned: unsignedTx} + + fmt.Println(" *** Please sign CreateChain transaction on your Ledger device ***") + signCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + if err := wallet.P().Signer().Sign(signCtx, tx); err != nil { + return nil, fmt.Errorf("failed to sign tx: %w", err) + } + + return tx, nil +} + +func buildAndSignAddSubnetValidatorTx(wallet *primary.Wallet, subnetID ids.ID, nodeAddr ids.ShortID) (*txs.Tx, error) { + nodeID := ids.BuildTestNodeID(nodeAddr[:]) + startTime := time.Now().Add(1 * time.Hour) + endTime := startTime.Add(24 * time.Hour) + weight := uint64(1000) + + validator := &txs.SubnetValidator{ + Validator: txs.Validator{ + NodeID: nodeID, + Start: uint64(startTime.Unix()), + End: uint64(endTime.Unix()), + Wght: weight, + }, + Subnet: subnetID, + } + + unsignedTx, err := wallet.P().Builder().NewAddSubnetValidatorTx(validator) + if err != nil { + return nil, fmt.Errorf("failed to build tx: %w", err) + } + + tx := &txs.Tx{Unsigned: unsignedTx} + + fmt.Println(" *** Please sign AddSubnetValidator transaction on your Ledger device ***") + signCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + if err := wallet.P().Signer().Sign(signCtx, tx); err != nil { + return nil, fmt.Errorf("failed to sign tx: %w", err) + } + + return tx, nil +} + +func buildAndSignRemoveSubnetValidatorTx(wallet *primary.Wallet, subnetID ids.ID) (*txs.Tx, error) { + nodeID := ids.GenerateTestNodeID() + + unsignedTx, err := wallet.P().Builder().NewRemoveSubnetValidatorTx(nodeID, subnetID) + if err != nil { + return nil, fmt.Errorf("failed to build tx: %w", err) + } + + tx := &txs.Tx{Unsigned: unsignedTx} + + fmt.Println(" *** Please sign RemoveSubnetValidator transaction on your Ledger device ***") + signCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + if err := wallet.P().Signer().Sign(signCtx, tx); err != nil { + return nil, fmt.Errorf("failed to sign tx: %w", err) + } + + return tx, nil +} + +func buildAndSignTransferSubnetOwnershipTx(wallet *primary.Wallet, subnetID ids.ID, newOwner *secp256k1fx.OutputOwners) (*txs.Tx, error) { + unsignedTx, err := wallet.P().Builder().NewTransferSubnetOwnershipTx(subnetID, newOwner) + if err != nil { + return nil, fmt.Errorf("failed to build tx: %w", err) + } + + tx := &txs.Tx{Unsigned: unsignedTx} + + fmt.Println(" *** Please sign TransferSubnetOwnership transaction on your Ledger device ***") + signCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + if err := wallet.P().Signer().Sign(signCtx, tx); err != nil { + return nil, fmt.Errorf("failed to sign tx: %w", err) + } + + return tx, nil +} + +func buildAndSignConvertSubnetToL1Tx(wallet *primary.Wallet, subnetID ids.ID, controlKeys []ids.ShortID) (*txs.Tx, error) { + validatorManagerChainID := ids.GenerateTestID() + validatorManagerAddress := []byte{1, 2, 3, 4} + + sk, err := localsigner.New() + if err != nil { + return nil, fmt.Errorf("failed to generate BLS key: %w", err) + } + + pop, err := signer.NewProofOfPossession(sk) + if err != nil { + return nil, fmt.Errorf("failed to create proof of possession: %w", err) + } + + validators := []*txs.ConvertSubnetToL1Validator{ + { + NodeID: ids.GenerateTestNodeID().Bytes(), + Weight: 1000, + Balance: 100000, + Signer: *pop, + RemainingBalanceOwner: message.PChainOwner{ + Threshold: 1, + Addresses: []ids.ShortID{controlKeys[0]}, + }, + DeactivationOwner: message.PChainOwner{ + Threshold: 1, + Addresses: []ids.ShortID{controlKeys[0]}, + }, + }, + } + + unsignedTx, err := wallet.P().Builder().NewConvertSubnetToL1Tx( + subnetID, + validatorManagerChainID, + validatorManagerAddress, + validators, + ) + if err != nil { + return nil, fmt.Errorf("failed to build tx: %w", err) + } + + tx := &txs.Tx{Unsigned: unsignedTx} + + fmt.Println(" *** Please sign ConvertSubnetToL1 transaction on your Ledger device ***") + signCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + if err := wallet.P().Signer().Sign(signCtx, tx); err != nil { + return nil, fmt.Errorf("failed to sign tx: %w", err) + } + + return tx, nil +} + +func buildAndSignBaseTx(wallet *primary.Wallet, toAddr ids.ShortID) (*txs.Tx, error) { + outputs := []*avax.TransferableOutput{ + { + Asset: avax.Asset{ID: wallet.P().Builder().Context().AVAXAssetID}, + Out: &secp256k1fx.TransferOutput{ + Amt: 1000000, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{toAddr}, + }, + }, + }, + } + + unsignedTx, err := wallet.P().Builder().NewBaseTx(outputs) + if err != nil { + return nil, fmt.Errorf("failed to build tx: %w", err) + } + + tx := &txs.Tx{Unsigned: unsignedTx} + + fmt.Println(" *** Please sign BaseTx on your Ledger device ***") + signCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + if err := wallet.P().Signer().Sign(signCtx, tx); err != nil { + return nil, fmt.Errorf("failed to sign tx: %w", err) + } + + return tx, nil +} + +func buildAndSignAddValidatorTx(wallet *primary.Wallet, rewardsAddr ids.ShortID) (*txs.Tx, error) { + nodeID := ids.GenerateTestNodeID() + startTime := time.Now().Add(1 * time.Hour) + endTime := startTime.Add(24 * time.Hour) + weight := uint64(2000000000) + shares := uint32(20000) + + validator := &txs.Validator{ + NodeID: nodeID, + Start: uint64(startTime.Unix()), + End: uint64(endTime.Unix()), + Wght: weight, + } + + rewardsOwner := &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{rewardsAddr}, + } + + unsignedTx, err := wallet.P().Builder().NewAddValidatorTx(validator, rewardsOwner, shares) + if err != nil { + return nil, fmt.Errorf("failed to build tx: %w", err) + } + + tx := &txs.Tx{Unsigned: unsignedTx} + + fmt.Println(" *** Please sign AddValidator transaction on your Ledger device ***") + signCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + if err := wallet.P().Signer().Sign(signCtx, tx); err != nil { + return nil, fmt.Errorf("failed to sign tx: %w", err) + } + + return tx, nil +} + +func buildAndSignAddDelegatorTx(wallet *primary.Wallet, rewardsAddr ids.ShortID) (*txs.Tx, error) { + nodeID := ids.GenerateTestNodeID() + startTime := time.Now().Add(1 * time.Hour) + endTime := startTime.Add(24 * time.Hour) + weight := uint64(25000000) + + validator := &txs.Validator{ + NodeID: nodeID, + Start: uint64(startTime.Unix()), + End: uint64(endTime.Unix()), + Wght: weight, + } + + rewardsOwner := &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{rewardsAddr}, + } + + unsignedTx, err := wallet.P().Builder().NewAddDelegatorTx(validator, rewardsOwner) + if err != nil { + return nil, fmt.Errorf("failed to build tx: %w", err) + } + + tx := &txs.Tx{Unsigned: unsignedTx} + + fmt.Println(" *** Please sign AddDelegator transaction on your Ledger device ***") + signCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + if err := wallet.P().Signer().Sign(signCtx, tx); err != nil { + return nil, fmt.Errorf("failed to sign tx: %w", err) + } + + return tx, nil +} + +func buildAndSignXChainImportTx(wallet *primary.Wallet, to *secp256k1fx.OutputOwners, sourceChainID ids.ID) (*avmtxs.Tx, error) { + unsignedTx, err := wallet.X().Builder().NewImportTx( + sourceChainID, + to, + ) + if err != nil { + return nil, fmt.Errorf("failed to build tx: %w", err) + } + + tx := &avmtxs.Tx{Unsigned: unsignedTx} + + fmt.Println(" *** Please sign X-Chain ImportTx on your Ledger device ***") + signCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + if err := wallet.X().Signer().Sign(signCtx, tx); err != nil { + return nil, fmt.Errorf("failed to sign tx: %w", err) + } + + return tx, nil +} + +func buildAndSignXChainExportTx(wallet *primary.Wallet, to *secp256k1fx.OutputOwners, destinationChainID ids.ID) (*avmtxs.Tx, error) { + unsignedTx, err := wallet.X().Builder().NewExportTx( + destinationChainID, + []*avax.TransferableOutput{ + { + Asset: avax.Asset{ID: wallet.X().Builder().Context().AVAXAssetID}, + Out: &secp256k1fx.TransferOutput{ + Amt: 500000000, + OutputOwners: *to, + }, + }, + }, + ) + if err != nil { + return nil, fmt.Errorf("failed to build tx: %w", err) + } + + tx := &avmtxs.Tx{Unsigned: unsignedTx} + + fmt.Println(" *** Please sign X-Chain ExportTx on your Ledger device ***") + signCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + if err := wallet.X().Signer().Sign(signCtx, tx); err != nil { + return nil, fmt.Errorf("failed to sign tx: %w", err) + } + + return tx, nil +} + +func buildAndSignXChainBaseTx(wallet *primary.Wallet, toAddr ids.ShortID) (*avmtxs.Tx, error) { + unsignedTx, err := wallet.X().Builder().NewBaseTx( + []*avax.TransferableOutput{ + { + Asset: avax.Asset{ID: wallet.X().Builder().Context().AVAXAssetID}, + Out: &secp256k1fx.TransferOutput{ + Amt: 1000000, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{toAddr}, + }, + }, + }, + }, + ) + if err != nil { + return nil, fmt.Errorf("failed to build tx: %w", err) + } + + tx := &avmtxs.Tx{Unsigned: unsignedTx} + + fmt.Println(" *** Please sign X-Chain BaseTx on your Ledger device ***") + signCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + if err := wallet.X().Signer().Sign(signCtx, tx); err != nil { + return nil, fmt.Errorf("failed to sign tx: %w", err) + } + + return tx, nil +} + +func buildAndSignXChainCreateAssetTx(wallet *primary.Wallet, addr ids.ShortID) (*avmtxs.Tx, error) { + unsignedTx, err := wallet.X().Builder().NewCreateAssetTx( + "Test Asset", + "TST", + 9, + map[uint32][]verify.State{ + 0: { + &secp256k1fx.TransferOutput{ + Amt: 1000000000, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{addr}, + }, + }, + }, + }, + ) + if err != nil { + return nil, fmt.Errorf("failed to build tx: %w", err) + } + + tx := &avmtxs.Tx{Unsigned: unsignedTx} + + fmt.Println(" *** Please sign X-Chain CreateAssetTx on your Ledger device ***") + signCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + if err := wallet.X().Signer().Sign(signCtx, tx); err != nil { + return nil, fmt.Errorf("failed to sign tx: %w", err) + } + + return tx, nil +} + +func buildAndSignXChainOperationTx(wallet *primary.Wallet) (*avmtxs.Tx, error) { + unsignedTx, err := wallet.X().Builder().NewOperationTx( + []*avmtxs.Operation{}, + ) + if err != nil { + return nil, fmt.Errorf("failed to build tx: %w", err) + } + + tx := &avmtxs.Tx{Unsigned: unsignedTx} + + fmt.Println(" *** Please sign X-Chain OperationTx on your Ledger device ***") + signCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + if err := wallet.X().Signer().Sign(signCtx, tx); err != nil { + return nil, fmt.Errorf("failed to sign tx: %w", err) + } + + return tx, nil +} + +func buildAndSignPChainImportTx(wallet *primary.Wallet, to *secp256k1fx.OutputOwners, sourceChainID ids.ID) (*txs.Tx, error) { + unsignedTx, err := wallet.P().Builder().NewImportTx( + sourceChainID, + to, + ) + if err != nil { + return nil, fmt.Errorf("failed to build tx: %w", err) + } + + tx := &txs.Tx{Unsigned: unsignedTx} + + fmt.Println(" *** Please sign ImportTx on your Ledger device ***") + signCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + if err := wallet.P().Signer().Sign(signCtx, tx); err != nil { + return nil, fmt.Errorf("failed to sign tx: %w", err) + } + + return tx, nil +} + +func buildAndSignPChainExportTx(wallet *primary.Wallet, to *secp256k1fx.OutputOwners, destinationChainID ids.ID) (*txs.Tx, error) { + outputs := []*avax.TransferableOutput{ + { + Asset: avax.Asset{ID: wallet.P().Builder().Context().AVAXAssetID}, + Out: &secp256k1fx.TransferOutput{ + Amt: 1000000000, + OutputOwners: *to, + }, + }, + } + + unsignedTx, err := wallet.P().Builder().NewExportTx( + destinationChainID, + outputs, + ) + if err != nil { + return nil, fmt.Errorf("failed to build tx: %w", err) + } + + tx := &txs.Tx{Unsigned: unsignedTx} + + fmt.Println(" *** Please sign ExportTx on your Ledger device ***") + signCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + if err := wallet.P().Signer().Sign(signCtx, tx); err != nil { + return nil, fmt.Errorf("failed to sign tx: %w", err) + } + + return tx, nil +} + +func buildAndSignAddPermissionlessValidatorTx(wallet *primary.Wallet, subnetID ids.ID, rewardsAddr ids.ShortID) (*txs.Tx, error) { + nodeID := ids.GenerateTestNodeID() + startTime := uint64(time.Now().Add(1 * time.Hour).Unix()) + endTime := uint64(time.Now().Add(25 * time.Hour).Unix()) + weight := uint64(1000) + + validator := &txs.SubnetValidator{ + Validator: txs.Validator{ + NodeID: nodeID, + Start: startTime, + End: endTime, + Wght: weight, + }, + Subnet: subnetID, + } + + rewardsOwner := &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{rewardsAddr}, + } + + unsignedTx, err := wallet.P().Builder().NewAddPermissionlessValidatorTx( + validator, + &signer.Empty{}, + wallet.P().Builder().Context().AVAXAssetID, + rewardsOwner, + rewardsOwner, + 20000, + ) + if err != nil { + return nil, fmt.Errorf("failed to build tx: %w", err) + } + + tx := &txs.Tx{Unsigned: unsignedTx} + + fmt.Println(" *** Please sign AddPermissionlessValidator transaction on your Ledger device ***") + signCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + if err := wallet.P().Signer().Sign(signCtx, tx); err != nil { + return nil, fmt.Errorf("failed to sign tx: %w", err) + } + + return tx, nil +} + +func buildAndSignAddPermissionlessDelegatorTx(wallet *primary.Wallet, subnetID ids.ID, rewardsAddr ids.ShortID) (*txs.Tx, error) { + nodeID := ids.GenerateTestNodeID() + startTime := uint64(time.Now().Add(1 * time.Hour).Unix()) + endTime := uint64(time.Now().Add(25 * time.Hour).Unix()) + weight := uint64(500) + + validator := &txs.SubnetValidator{ + Validator: txs.Validator{ + NodeID: nodeID, + Start: startTime, + End: endTime, + Wght: weight, + }, + Subnet: subnetID, + } + + rewardsOwner := &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{rewardsAddr}, + } + + unsignedTx, err := wallet.P().Builder().NewAddPermissionlessDelegatorTx( + validator, + wallet.P().Builder().Context().AVAXAssetID, + rewardsOwner, + ) + if err != nil { + return nil, fmt.Errorf("failed to build tx: %w", err) + } + + tx := &txs.Tx{Unsigned: unsignedTx} + + fmt.Println(" *** Please sign AddPermissionlessDelegator transaction on your Ledger device ***") + signCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + if err := wallet.P().Signer().Sign(signCtx, tx); err != nil { + return nil, fmt.Errorf("failed to sign tx: %w", err) + } + + return tx, nil +} + +func buildAndSignRegisterL1ValidatorTx(wallet *primary.Wallet) (*txs.Tx, error) { + balance := uint64(100000) + proofOfPossession := [96]byte{} + + subnetID := ids.GenerateTestID() + nodeID := ids.GenerateTestNodeID() + blsPublicKey := [48]byte{} + expiry := uint64(time.Now().Add(24 * time.Hour).Unix()) + weight := uint64(1000) + + balanceOwners := message.PChainOwner{ + Threshold: 1, + Addresses: []ids.ShortID{ids.GenerateTestShortID()}, + } + disableOwners := message.PChainOwner{ + Threshold: 1, + Addresses: []ids.ShortID{ids.GenerateTestShortID()}, + } + + addressedCallPayload, err := message.NewRegisterL1Validator( + subnetID, + nodeID, + blsPublicKey, + expiry, + balanceOwners, + disableOwners, + weight, + ) + if err != nil { + return nil, fmt.Errorf("failed to create register L1 validator message: %w", err) + } + + managerAddress := ids.ShortID{} + addressedCall, err := payload.NewAddressedCall( + managerAddress[:], + addressedCallPayload.Bytes(), + ) + if err != nil { + return nil, fmt.Errorf("failed to create addressed call: %w", err) + } + + unsignedWarpMessage, err := warp.NewUnsignedMessage( + wallet.P().Builder().Context().NetworkID, + ids.GenerateTestID(), + addressedCall.Bytes(), + ) + if err != nil { + return nil, fmt.Errorf("failed to create unsigned warp message: %w", err) + } + + emptySignature := &warp.BitSetSignature{ + Signers: []byte{}, + Signature: [96]byte{}, + } + + signedWarpMessage, err := warp.NewMessage( + unsignedWarpMessage, + emptySignature, + ) + if err != nil { + return nil, fmt.Errorf("failed to create signed warp message: %w", err) + } + + warpMessage := signedWarpMessage.Bytes() + + unsignedTx, err := wallet.P().Builder().NewRegisterL1ValidatorTx( + balance, + proofOfPossession, + warpMessage, + ) + if err != nil { + return nil, fmt.Errorf("failed to build tx: %w", err) + } + + tx := &txs.Tx{Unsigned: unsignedTx} + + fmt.Println(" *** Please sign RegisterL1Validator transaction on your Ledger device ***") + signCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + if err := wallet.P().Signer().Sign(signCtx, tx); err != nil { + return nil, fmt.Errorf("failed to sign tx: %w", err) + } + + return tx, nil +} + +func buildAndSignSetL1ValidatorWeightTx(wallet *primary.Wallet) (*txs.Tx, error) { + validationID := ids.GenerateTestID() + nonce := uint64(1) + weight := uint64(2000) + + l1ValidatorWeightPayload, err := message.NewL1ValidatorWeight( + validationID, + nonce, + weight, + ) + if err != nil { + return nil, fmt.Errorf("failed to create L1 validator weight message: %w", err) + } + + managerAddress := ids.ShortID{} + addressedCall, err := payload.NewAddressedCall( + managerAddress[:], + l1ValidatorWeightPayload.Bytes(), + ) + if err != nil { + return nil, fmt.Errorf("failed to create addressed call: %w", err) + } + + unsignedWarpMessage, err := warp.NewUnsignedMessage( + wallet.P().Builder().Context().NetworkID, + ids.GenerateTestID(), + addressedCall.Bytes(), + ) + if err != nil { + return nil, fmt.Errorf("failed to create unsigned warp message: %w", err) + } + + emptySignature := &warp.BitSetSignature{ + Signers: []byte{}, + Signature: [96]byte{}, + } + + signedWarpMessage, err := warp.NewMessage( + unsignedWarpMessage, + emptySignature, + ) + if err != nil { + return nil, fmt.Errorf("failed to create signed warp message: %w", err) + } + + warpMessage := signedWarpMessage.Bytes() + + unsignedTx, err := wallet.P().Builder().NewSetL1ValidatorWeightTx(warpMessage) + if err != nil { + return nil, fmt.Errorf("failed to build tx: %w", err) + } + + tx := &txs.Tx{Unsigned: unsignedTx} + + fmt.Println(" *** Please sign SetL1ValidatorWeight transaction on your Ledger device ***") + signCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + if err := wallet.P().Signer().Sign(signCtx, tx); err != nil { + return nil, fmt.Errorf("failed to sign tx: %w", err) + } + + return tx, nil +} + +func buildAndSignIncreaseL1ValidatorBalanceTx(wallet *primary.Wallet, validationID ids.ID) (*txs.Tx, error) { + balance := uint64(50000) + + unsignedTx, err := wallet.P().Builder().NewIncreaseL1ValidatorBalanceTx(validationID, balance) + if err != nil { + return nil, fmt.Errorf("failed to build tx: %w", err) + } + + tx := &txs.Tx{Unsigned: unsignedTx} + + fmt.Println(" *** Please sign IncreaseL1ValidatorBalance transaction on your Ledger device ***") + signCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + if err := wallet.P().Signer().Sign(signCtx, tx); err != nil { + return nil, fmt.Errorf("failed to sign tx: %w", err) + } + + return tx, nil +} + +func buildAndSignDisableL1ValidatorTx(wallet *primary.Wallet, validationID ids.ID) (*txs.Tx, error) { + unsignedTx, err := wallet.P().Builder().NewDisableL1ValidatorTx(validationID) + if err != nil { + return nil, fmt.Errorf("failed to build tx: %w", err) + } + + tx := &txs.Tx{Unsigned: unsignedTx} + + fmt.Println(" *** Please sign DisableL1Validator transaction on your Ledger device ***") + signCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + if err := wallet.P().Signer().Sign(signCtx, tx); err != nil { + return nil, fmt.Errorf("failed to sign tx: %w", err) + } + + return tx, nil +} + +func buildAndSignTransformSubnetTx(wallet *primary.Wallet, subnetID ids.ID) (*txs.Tx, error) { + assetID := ids.Empty + initialSupply := uint64(100) + maxSupply := uint64(100) + minConsumptionRate := uint64(0) + maxConsumptionRate := uint64(0) + minValidatorStake := uint64(1) + maxValidatorStake := uint64(1000000) + minStakeDuration := 24 * time.Hour + maxStakeDuration := 365 * 24 * time.Hour + minDelegationFee := uint32(20000) + minDelegatorStake := uint64(1) + maxValidatorWeightFactor := byte(5) + uptimeRequirement := uint32(800000) + + unsignedTx, err := wallet.P().Builder().NewTransformSubnetTx( + subnetID, + assetID, + initialSupply, + maxSupply, + minConsumptionRate, + maxConsumptionRate, + minValidatorStake, + maxValidatorStake, + minStakeDuration, + maxStakeDuration, + minDelegationFee, + minDelegatorStake, + maxValidatorWeightFactor, + uptimeRequirement, + ) + if err != nil { + return nil, fmt.Errorf("failed to build tx: %w", err) + } + + tx := &txs.Tx{Unsigned: unsignedTx} + + fmt.Println(" *** Please sign TransformSubnet transaction on your Ledger device ***") + signCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + if err := wallet.P().Signer().Sign(signCtx, tx); err != nil { + return nil, fmt.Errorf("failed to sign tx: %w", err) + } + + return tx, nil +} + +func buildAndSignCChainImportTx(wallet *primary.Wallet, to ethcommon.Address, sourceChainID ids.ID) (*atomic.Tx, error) { + baseFee := big.NewInt(25000000000) + unsignedTx, err := wallet.C().Builder().NewImportTx( + sourceChainID, + to, + baseFee, + ) + if err != nil { + return nil, fmt.Errorf("failed to build tx: %w", err) + } + + tx := &atomic.Tx{UnsignedAtomicTx: unsignedTx} + + fmt.Println(" *** Please sign C-Chain ImportTx on your Ledger device ***") + signCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + if err := wallet.C().Signer().SignAtomic(signCtx, tx); err != nil { + return nil, fmt.Errorf("failed to sign tx: %w", err) + } + + return tx, nil +} + +func buildAndSignCChainExportTx(wallet *primary.Wallet, to *secp256k1fx.OutputOwners, destinationChainID ids.ID) (*atomic.Tx, error) { + baseFee := big.NewInt(25000000000) + unsignedTx, err := wallet.C().Builder().NewExportTx( + destinationChainID, + []*secp256k1fx.TransferOutput{ + { + Amt: 500000000, + OutputOwners: *to, + }, + }, + baseFee, + ) + if err != nil { + return nil, fmt.Errorf("failed to build tx: %w", err) + } + + tx := &atomic.Tx{UnsignedAtomicTx: unsignedTx} + + fmt.Println(" *** Please sign C-Chain ExportTx on your Ledger device ***") + signCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + if err := wallet.C().Signer().SignAtomic(signCtx, tx); err != nil { + return nil, fmt.Errorf("failed to sign tx: %w", err) + } + + return tx, nil +} diff --git a/keychain/ledger/ledger.go b/keychain/ledger/ledger.go new file mode 100644 index 0000000..7e10e98 --- /dev/null +++ b/keychain/ledger/ledger.go @@ -0,0 +1,22 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package ledger + +import ( + "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" + "github.com/ava-labs/avalanchego/version" +) + +// Ledger interface for the ledger wrapper +type Ledger interface { + Version() (v *version.Semantic, err error) + PubKey(addressIndex uint32) (*secp256k1.PublicKey, error) + PubKeys(addressIndices []uint32) ([]*secp256k1.PublicKey, error) + SignHash(hash []byte, addressIndices []uint32) ([][]byte, error) + Sign(unsignedTxBytes []byte, addressIndices []uint32) ([][]byte, error) + Disconnect() error +} + +// Verify that the Device implementation satisfies the Ledger interface +var _ Ledger = (*Device)(nil) diff --git a/keychain/ledger/ledger_device.go b/keychain/ledger/ledger_device.go new file mode 100644 index 0000000..98b1939 --- /dev/null +++ b/keychain/ledger/ledger_device.go @@ -0,0 +1,230 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package ledger + +import ( + "fmt" + "time" + + "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" + "github.com/ava-labs/avalanchego/utils/hashing" + "github.com/ava-labs/avalanchego/version" + + ledger "github.com/ava-labs/ledger-avalanche-go" + bip32 "github.com/tyler-smith/go-bip32" +) + +const ( + rootPath = "m/44'/9000'/0'" // BIP44: m / purpose' / coin_type' / account' + // ledgerBufferLimit corresponds to FLASH_BUFFER_SIZE for Nano S. + // Modern devices (Nano X, Nano S2, Stax, Flex) support up to 16384 bytes, + // but we use the conservative limit for universal compatibility. + // + // Ref: https://github.com/ava-labs/ledger-avalanche/blob/main/app/src/common/tx.c + ledgerBufferLimit = 8192 + // ledgerPathSize is the serialized size of a path suffix (e.g., "0/123") as produced + // by SerializePathSuffix in the ledger library. Format: 1 byte (component count) + + // 4 bytes (first component) + 4 bytes (second component) = 9 bytes total. + // + // Ref: https://github.com/ava-labs/ledger-avalanche-go/blob/main/common.go (SerializePathSuffix) + ledgerPathSize = 9 + maxRetries = 5 + initialRetryDelay = 200 * time.Millisecond +) + +// retryOnHIDAPIError executes a function up to maxRetries times if it encounters +// the specific "hidapi: unknown failure" error or APDU error 0x6987 +func retryOnHIDAPIError(fn func() error) error { + var err error + for attempt := 1; attempt <= maxRetries; attempt++ { + err = fn() + if err == nil { + return nil + } + + // These errors indicate transient USB communication issues that often resolve on retry: + // - "hidapi: unknown failure": USB communication error from the HIDAPI library + // - APDU 0x6987: "Interrupted execution" - occurs when the device is busy or communication is disrupted + if err.Error() == "hidapi: unknown failure" || err.Error() == "APDU Error Code from Ledger Device: 0x6987" { + if attempt < maxRetries { + // Calculate backoff delay: 200ms, 400ms, 600ms, 800ms + // This linear backoff prevents excessive delay (at most 2s), while at the same + // time it is quick enough in most cases. Also proved to successfully recover + // in all tests. + delay := time.Duration(attempt) * initialRetryDelay + time.Sleep(delay) + continue + } + } + + // If it's not a retryable error or we've exhausted retries, exit the loop + break + } + return err +} + +// Device is a wrapper around the low-level Ledger Device interface that +// provides Avalanche-specific access. +type Device struct { + device *ledger.LedgerAvalanche + epk *bip32.Key +} + +func New() (*Device, error) { + var device *ledger.LedgerAvalanche + err := retryOnHIDAPIError(func() error { + var err error + device, err = ledger.FindLedgerAvalancheApp() + return err + }) + return &Device{ + device: device, + }, err +} + +func addressPath(index uint32) string { + return fmt.Sprintf("%s/0/%d", rootPath, index) +} + +func (l *Device) PubKey(addressIndex uint32) (*secp256k1.PublicKey, error) { + var resp *ledger.ResponseAddr + err := retryOnHIDAPIError(func() error { + var err error + resp, err = l.device.GetPubKey(addressPath(addressIndex), false, "", "") + return err + }) + if err != nil { + return nil, err + } + pubKey, err := secp256k1.ToPublicKey(resp.PublicKey) + if err != nil { + return nil, fmt.Errorf("failure parsing public key from ledger: %w", err) + } + return pubKey, nil +} + +func (l *Device) PubKeys(addressIndices []uint32) ([]*secp256k1.PublicKey, error) { + if l.epk == nil { + var pk []byte + var chainCode []byte + err := retryOnHIDAPIError(func() error { + var err error + pk, chainCode, err = l.device.GetExtPubKey(rootPath, false, "", "") + return err + }) + if err != nil { + return nil, err + } + l.epk = &bip32.Key{ + Key: pk, + ChainCode: chainCode, + } + } + // derivation path rootPath/0 (BIP44 change level, when set to 0, known as external chain) + externalChain, err := l.epk.NewChildKey(0) + if err != nil { + return nil, err + } + pubKeys := make([]*secp256k1.PublicKey, len(addressIndices)) + for i, addressIndex := range addressIndices { + // derivation path rootPath/0/v (BIP44 address index level) + address, err := externalChain.NewChildKey(addressIndex) + if err != nil { + return nil, err + } + pubKey, err := secp256k1.ToPublicKey(address.Key) + if err != nil { + return nil, fmt.Errorf("failure parsing public key for ledger child key %d: %w", addressIndex, err) + } + pubKeys[i] = pubKey + } + return pubKeys, nil +} + +func convertToSigningPaths(input []uint32) []string { + output := make([]string, len(input)) + for i, v := range input { + output[i] = fmt.Sprintf("0/%d", v) + } + return output +} + +func (l *Device) SignHash(hash []byte, addressIndices []uint32) ([][]byte, error) { + strIndices := convertToSigningPaths(addressIndices) + var response *ledger.ResponseSign + err := retryOnHIDAPIError(func() error { + var err error + response, err = l.device.SignHash(rootPath, strIndices, hash) + return err + }) + if err != nil { + return nil, fmt.Errorf("%w: unable to sign hash", err) + } + responses := make([][]byte, len(addressIndices)) + for i, index := range strIndices { + sig, ok := response.Signature[index] + if !ok { + return nil, fmt.Errorf("missing signature %s", index) + } + responses[i] = sig + } + return responses, nil +} + +func (l *Device) Sign(txBytes []byte, addressIndices []uint32) ([][]byte, error) { + // We pass addressIndices both as signing paths and change paths. + // The ledger library deduplicates them, so the buffer contains len(addressIndices) paths. + // Buffer format: 1 byte (path count) + paths + transaction bytes + // + // Ref: https://github.com/ava-labs/ledger-avalanche-go/blob/main/common.go (ConcatMessageAndChangePath) + bufferSize := 1 + len(addressIndices)*ledgerPathSize + len(txBytes) + if bufferSize > ledgerBufferLimit { + // When the tx that is being signed is too large for the ledger buffer, + // we sign with hash instead. + unsignedHash := hashing.ComputeHash256(txBytes) + return l.SignHash(unsignedHash, addressIndices) + } + strIndices := convertToSigningPaths(addressIndices) + var response *ledger.ResponseSign + err := retryOnHIDAPIError(func() error { + var err error + response, err = l.device.Sign(rootPath, strIndices, txBytes, strIndices) + return err + }) + if err != nil { + return nil, fmt.Errorf("%w: unable to sign transaction", err) + } + responses := make([][]byte, len(strIndices)) + for i, index := range strIndices { + sig, ok := response.Signature[index] + if !ok { + return nil, fmt.Errorf("missing signature %s", index) + } + responses[i] = sig + } + return responses, nil +} + +func (l *Device) Version() (*version.Semantic, error) { + var resp *ledger.VersionInfo + err := retryOnHIDAPIError(func() error { + var err error + resp, err = l.device.GetVersion() + return err + }) + if err != nil { + return nil, err + } + return &version.Semantic{ + Major: int(resp.Major), + Minor: int(resp.Minor), + Patch: int(resp.Patch), + }, nil +} + +func (l *Device) Disconnect() error { + return retryOnHIDAPIError(func() error { + return l.device.Close() + }) +} diff --git a/keychain/ledger/ledger_device_test.go b/keychain/ledger/ledger_device_test.go new file mode 100644 index 0000000..e175b8a --- /dev/null +++ b/keychain/ledger/ledger_device_test.go @@ -0,0 +1,75 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package ledger + +import ( + "testing" + + "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" + "github.com/ava-labs/avalanchego/utils/formatting/address" + "github.com/ava-labs/avalanchego/utils/hashing" + "github.com/stretchr/testify/require" +) + +const ( + chainAlias = "P" + hrp = "fuji" +) + +// TestLedger will be skipped if a ledger is not connected. +func TestLedger(t *testing.T) { + require := require.New(t) + + // Initialize Ledger + device, err := New() + if err != nil { + t.Skip("ledger not detected") + } + + // Get version + version, err := device.Version() + require.NoError(err) + t.Logf("version: %s\n", version) + + // Get Fuji Address + pubKey, err := device.PubKey(0) + require.NoError(err) + addr := pubKey.Address() + paddr, err := address.Format(chainAlias, hrp, addr[:]) + require.NoError(err) + t.Logf("address: %s shortID: %s\n", paddr, addr) + + // Get Extended Public Keys + pubKeys, err := device.PubKeys([]uint32{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}) + require.NoError(err) + for i, pk := range pubKeys { + taddr := pk.Address() + paddr, err := address.Format(chainAlias, hrp, taddr[:]) + require.NoError(err) + t.Logf("address(%d): %s shortID: %s\n", i, paddr, taddr) + + // Ensure first derived address matches directly requested address + if i == 0 { + require.Equal(addr, taddr, "address mismatch at index 0") + } + } + + // Sign Hash + rawHash := hashing.ComputeHash256([]byte{0x1, 0x2, 0x3, 0x4}) + indices := []uint32{1, 3} + sigs, err := device.SignHash(rawHash, indices) + require.NoError(err) + require.Len(sigs, 2) + + for i, addrIndex := range indices { + sig := sigs[i] + + pk, err := secp256k1.RecoverPublicKeyFromHash(rawHash, sig) + require.NoError(err) + require.Equal(pubKeys[addrIndex].Address(), pk.Address()) + } + + // Disconnect + require.NoError(device.Disconnect()) +} diff --git a/keychain/ledger/ledger_keychain.go b/keychain/ledger/ledger_keychain.go new file mode 100644 index 0000000..380d268 --- /dev/null +++ b/keychain/ledger/ledger_keychain.go @@ -0,0 +1,215 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package ledger + +import ( + "errors" + "fmt" + "math" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/crypto/keychain" + "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" + "github.com/ava-labs/avalanchego/utils/hashing" + "github.com/ava-labs/avalanchego/utils/set" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "github.com/ava-labs/avalanchego/wallet/chain/c" + "github.com/ava-labs/libevm/common" + "golang.org/x/exp/maps" +) + +var ( + _ keychain.Keychain = (*KeyChain)(nil) + _ c.EthKeychain = (*KeyChain)(nil) + _ keychain.Signer = (*ledgerSigner)(nil) + + ErrInvalidIndicesLength = errors.New("number of indices should be greater than 0") + ErrInvalidNumAddrsToDerive = errors.New("number of addresses to derive should be greater than 0") + ErrInvalidNumAddrsDerived = errors.New("incorrect number of ledger derived addresses") + ErrInvalidNumSignatures = errors.New("incorrect number of signatures") +) + +// keyInfo holds both the public key and ledger index for a ledger key. +type keyInfo struct { + pubKey *secp256k1.PublicKey // The Avalanche public key obtained from ledger + idx uint32 // The ledger index +} + +// KeyChain is an abstraction of the underlying ledger hardware device, +// to be able to get a signer from a finite set of derived signers +type KeyChain struct { + ledger Ledger + avaAddrToKeyInfo map[ids.ShortID]*keyInfo // Maps Avalanche addresses to key info + ethAddrToKeyInfo map[common.Address]*keyInfo // Maps Ethereum addresses to key info +} + +// ledgerSigner is an abstraction of the underlying ledger hardware device, +// to be able sign for a specific address +type ledgerSigner struct { + ledger Ledger + idx uint32 + pubKey *secp256k1.PublicKey +} + +// NewKeychain creates a new Ledger with [numToDerive] addresses. +func NewKeychain(l Ledger, numToDerive int) (*KeyChain, error) { + if numToDerive < 1 { + return nil, ErrInvalidNumAddrsToDerive + } + if numToDerive > math.MaxUint32 { + return nil, fmt.Errorf("number of addresses to derive %d exceeds uint32 max", numToDerive) + } + + indices := make([]uint32, numToDerive) + for i := range indices { + indices[i] = uint32(i) // #nosec G115 -- numToDerive is checked to be <= math.MaxUint32 + } + + return NewKeychainFromIndices(l, indices) +} + +// NewKeychainFromIndices creates a new Ledger with addresses taken from the given [indices]. +func NewKeychainFromIndices(l Ledger, indices []uint32) (*KeyChain, error) { + if len(indices) == 0 { + return nil, ErrInvalidIndicesLength + } + + pubKeys, err := l.PubKeys(indices) + if err != nil { + return nil, err + } + + if len(pubKeys) != len(indices) { + return nil, fmt.Errorf( + "%w. expected %d, got %d", + ErrInvalidNumAddrsDerived, + len(indices), + len(pubKeys), + ) + } + + avaAddrToKeyInfo := map[ids.ShortID]*keyInfo{} + ethAddrToKeyInfo := map[common.Address]*keyInfo{} + for i := range indices { + keyInf := &keyInfo{ + pubKey: pubKeys[i], + idx: indices[i], + } + avaAddrToKeyInfo[pubKeys[i].Address()] = keyInf + ethAddrToKeyInfo[pubKeys[i].EthAddress()] = keyInf + } + + return &KeyChain{ + ledger: l, + avaAddrToKeyInfo: avaAddrToKeyInfo, + ethAddrToKeyInfo: ethAddrToKeyInfo, + }, nil +} + +func (kc *KeyChain) Addresses() set.Set[ids.ShortID] { + return set.Of(maps.Keys(kc.avaAddrToKeyInfo)...) +} + +func (kc *KeyChain) Get(addr ids.ShortID) (keychain.Signer, bool) { + keyInf, found := kc.avaAddrToKeyInfo[addr] + if !found { + return nil, false + } + return &ledgerSigner{ + ledger: kc.ledger, + pubKey: keyInf.pubKey, + idx: keyInf.idx, + }, true +} + +// EthAddresses returns the set of Ethereum addresses that this keychain can sign for. +func (kc *KeyChain) EthAddresses() set.Set[common.Address] { + return set.Of(maps.Keys(kc.ethAddrToKeyInfo)...) +} + +// GetEth returns a signer for the given Ethereum address, if it exists in this keychain. +func (kc *KeyChain) GetEth(addr common.Address) (keychain.Signer, bool) { + keyInf, found := kc.ethAddrToKeyInfo[addr] + if !found { + return nil, false + } + return &ledgerSigner{ + ledger: kc.ledger, + pubKey: keyInf.pubKey, + idx: keyInf.idx, + }, true +} + +// expects to receive a hash of the unsigned tx bytes +func (l *ledgerSigner) SignHash(b []byte) ([]byte, error) { + // Sign using the address with index l.idx on the ledger device. The number + // of returned signatures should be the same length as the provided indices. + sigs, err := l.ledger.SignHash(b, []uint32{l.idx}) + if err != nil { + return nil, err + } + + if sigsLen := len(sigs); sigsLen != 1 { + return nil, fmt.Errorf( + "%w. expected 1, got %d", + ErrInvalidNumSignatures, + sigsLen, + ) + } + + return sigs[0], nil +} + +// expects to receive the unsigned tx bytes +func (l *ledgerSigner) Sign(b []byte, opts ...keychain.SigningOption) ([]byte, error) { + options := &keychain.SigningOptions{} + for _, opt := range opts { + opt(options) + } + + // For P-Chain transactions that require hash signing on the Ledger device + if options.ChainAlias == "P" { + useSignHash, err := shouldUseSignHash(b) + if err != nil { + return nil, err + } + if useSignHash { + return l.SignHash(hashing.ComputeHash256(b)) + } + } + + // Sign using the address with index l.idx on the ledger device. The number + // of returned signatures should be the same length as the provided indices. + sigs, err := l.ledger.Sign(b, []uint32{l.idx}) + if err != nil { + return nil, err + } + + if sigsLen := len(sigs); sigsLen != 1 { + return nil, fmt.Errorf( + "%w. expected 1, got %d", + ErrInvalidNumSignatures, + sigsLen, + ) + } + + return sigs[0], nil +} + +func (l *ledgerSigner) Address() ids.ShortID { + return l.pubKey.Address() +} + +// shouldUseSignHash determines if the transaction requires hash signing on the Ledger device. +// Currently, TransferSubnetOwnershipTx requires hash signing. +func shouldUseSignHash(unsignedTxBytes []byte) (bool, error) { + var unsignedTx txs.UnsignedTx + _, err := txs.Codec.Unmarshal(unsignedTxBytes, &unsignedTx) + if err != nil { + return false, err + } + + _, isTransferSubnetOwnership := unsignedTx.(*txs.TransferSubnetOwnershipTx) + return isTransferSubnetOwnership, nil +} diff --git a/keychain/ledger/ledger_keychain_test.go b/keychain/ledger/ledger_keychain_test.go new file mode 100644 index 0000000..9638003 --- /dev/null +++ b/keychain/ledger/ledger_keychain_test.go @@ -0,0 +1,841 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package ledger + +import ( + "errors" + "testing" + + "github.com/ava-labs/avalanchego/codec" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/crypto/keychain" + "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" + "github.com/ava-labs/avalanchego/utils/hashing" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/platformvm/signer" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + + "github.com/ava-labs/avalanche-tooling-sdk-go/keychain/ledger/ledgermock" +) + +var errTest = errors.New("test") + +func TestNewLedgerKeychain(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + + pubKey, err := secp256k1.NewPrivateKey() + require.NoError(err) + + // user request invalid number of addresses to derive + ledger := ledgermock.NewLedger(ctrl) + _, err = NewKeychain(ledger, 0) + require.ErrorIs(err, ErrInvalidNumAddrsToDerive) + + // ledger does not return expected number of derived addresses + ledger = ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys([]uint32{0}).Return([]*secp256k1.PublicKey{}, nil).Times(1) + _, err = NewKeychain(ledger, 1) + require.ErrorIs(err, ErrInvalidNumAddrsDerived) + + // ledger return error when asked for derived addresses + ledger = ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys([]uint32{0}).Return([]*secp256k1.PublicKey{pubKey.PublicKey()}, errTest).Times(1) + _, err = NewKeychain(ledger, 1) + require.ErrorIs(err, errTest) + + // good path + ledger = ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys([]uint32{0}).Return([]*secp256k1.PublicKey{pubKey.PublicKey()}, nil).Times(1) + _, err = NewKeychain(ledger, 1) + require.NoError(err) +} + +func TestLedgerKeychain_Addresses(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + + key1, err := secp256k1.NewPrivateKey() + require.NoError(err) + key2, err := secp256k1.NewPrivateKey() + require.NoError(err) + key3, err := secp256k1.NewPrivateKey() + require.NoError(err) + + pubKey1 := key1.PublicKey() + pubKey2 := key2.PublicKey() + pubKey3 := key3.PublicKey() + + // 1 addr + ledger := ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys([]uint32{0}).Return([]*secp256k1.PublicKey{pubKey1}, nil).Times(1) + kc, err := NewKeychain(ledger, 1) + require.NoError(err) + + addrs := kc.Addresses() + require.Len(addrs, 1) + require.True(addrs.Contains(pubKey1.Address())) + + // multiple addresses + ledger = ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys([]uint32{0, 1, 2}).Return([]*secp256k1.PublicKey{pubKey1, pubKey2, pubKey3}, nil).Times(1) + kc, err = NewKeychain(ledger, 3) + require.NoError(err) + + addrs = kc.Addresses() + require.Len(addrs, 3) + require.Contains(addrs, pubKey1.Address()) + require.Contains(addrs, pubKey2.Address()) + require.Contains(addrs, pubKey3.Address()) +} + +func TestLedgerKeychain_Get(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + + key1, err := secp256k1.NewPrivateKey() + require.NoError(err) + key2, err := secp256k1.NewPrivateKey() + require.NoError(err) + key3, err := secp256k1.NewPrivateKey() + require.NoError(err) + + pubKey1 := key1.PublicKey() + pubKey2 := key2.PublicKey() + pubKey3 := key3.PublicKey() + + addr1 := pubKey1.Address() + addr2 := pubKey2.Address() + addr3 := pubKey3.Address() + + // 1 addr + ledger := ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys([]uint32{0}).Return([]*secp256k1.PublicKey{pubKey1}, nil).Times(1) + kc, err := NewKeychain(ledger, 1) + require.NoError(err) + + _, b := kc.Get(ids.GenerateTestShortID()) + require.False(b) + + s, b := kc.Get(addr1) + require.Equal(s.Address(), addr1) + require.True(b) + + // multiple addresses + ledger = ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys([]uint32{0, 1, 2}).Return([]*secp256k1.PublicKey{pubKey1, pubKey2, pubKey3}, nil).Times(1) + kc, err = NewKeychain(ledger, 3) + require.NoError(err) + + _, b = kc.Get(ids.GenerateTestShortID()) + require.False(b) + + s, b = kc.Get(addr1) + require.True(b) + require.Equal(s.Address(), addr1) + + s, b = kc.Get(addr2) + require.True(b) + require.Equal(s.Address(), addr2) + + s, b = kc.Get(addr3) + require.True(b) + require.Equal(s.Address(), addr3) +} + +func TestLedgerSigner_SignHash(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + + key1, err := secp256k1.NewPrivateKey() + require.NoError(err) + key2, err := secp256k1.NewPrivateKey() + require.NoError(err) + key3, err := secp256k1.NewPrivateKey() + require.NoError(err) + + pubKey1 := key1.PublicKey() + pubKey2 := key2.PublicKey() + pubKey3 := key3.PublicKey() + + addr1 := pubKey1.Address() + addr2 := pubKey2.Address() + addr3 := pubKey3.Address() + + toSign := []byte{1, 2, 3, 4, 5} + expectedSignature1 := []byte{1, 1, 1} + expectedSignature2 := []byte{2, 2, 2} + expectedSignature3 := []byte{3, 3, 3} + + // ledger returns an incorrect number of signatures + ledger := ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys([]uint32{0}).Return([]*secp256k1.PublicKey{pubKey1}, nil).Times(1) + ledger.EXPECT().SignHash(toSign, []uint32{0}).Return([][]byte{}, nil).Times(1) + kc, err := NewKeychain(ledger, 1) + require.NoError(err) + + s, b := kc.Get(addr1) + require.True(b) + + _, err = s.SignHash(toSign) + require.ErrorIs(err, ErrInvalidNumSignatures) + + // ledger returns an error when asked for signature + ledger = ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys([]uint32{0}).Return([]*secp256k1.PublicKey{pubKey1}, nil).Times(1) + ledger.EXPECT().SignHash(toSign, []uint32{0}).Return([][]byte{expectedSignature1}, errTest).Times(1) + kc, err = NewKeychain(ledger, 1) + require.NoError(err) + + s, b = kc.Get(addr1) + require.True(b) + + _, err = s.SignHash(toSign) + require.ErrorIs(err, errTest) + + // good path 1 addr + ledger = ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys([]uint32{0}).Return([]*secp256k1.PublicKey{pubKey1}, nil).Times(1) + ledger.EXPECT().SignHash(toSign, []uint32{0}).Return([][]byte{expectedSignature1}, nil).Times(1) + kc, err = NewKeychain(ledger, 1) + require.NoError(err) + + s, b = kc.Get(addr1) + require.True(b) + + signature, err := s.SignHash(toSign) + require.NoError(err) + require.Equal(expectedSignature1, signature) + + // good path 3 addr + ledger = ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys([]uint32{0, 1, 2}).Return([]*secp256k1.PublicKey{pubKey1, pubKey2, pubKey3}, nil).Times(1) + ledger.EXPECT().SignHash(toSign, []uint32{0}).Return([][]byte{expectedSignature1}, nil).Times(1) + ledger.EXPECT().SignHash(toSign, []uint32{1}).Return([][]byte{expectedSignature2}, nil).Times(1) + ledger.EXPECT().SignHash(toSign, []uint32{2}).Return([][]byte{expectedSignature3}, nil).Times(1) + kc, err = NewKeychain(ledger, 3) + require.NoError(err) + + s, b = kc.Get(addr1) + require.True(b) + + signature, err = s.SignHash(toSign) + require.NoError(err) + require.Equal(expectedSignature1, signature) + + s, b = kc.Get(addr2) + require.True(b) + + signature, err = s.SignHash(toSign) + require.NoError(err) + require.Equal(expectedSignature2, signature) + + s, b = kc.Get(addr3) + require.True(b) + + signature, err = s.SignHash(toSign) + require.NoError(err) + require.Equal(expectedSignature3, signature) +} + +func TestNewLedgerKeychainFromIndices(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + + key, err := secp256k1.NewPrivateKey() + require.NoError(err) + pubKey := key.PublicKey() + + // user request invalid number of indices + ledger := ledgermock.NewLedger(ctrl) + _, err = NewKeychainFromIndices(ledger, []uint32{}) + require.ErrorIs(err, ErrInvalidIndicesLength) + + // ledger does not return expected number of derived addresses + ledger = ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys([]uint32{0}).Return([]*secp256k1.PublicKey{}, nil).Times(1) + _, err = NewKeychainFromIndices(ledger, []uint32{0}) + require.ErrorIs(err, ErrInvalidNumAddrsDerived) + + // ledger return error when asked for derived addresses + ledger = ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys([]uint32{0}).Return([]*secp256k1.PublicKey{pubKey}, errTest).Times(1) + _, err = NewKeychainFromIndices(ledger, []uint32{0}) + require.ErrorIs(err, errTest) + + // good path + ledger = ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys([]uint32{0}).Return([]*secp256k1.PublicKey{pubKey}, nil).Times(1) + _, err = NewKeychainFromIndices(ledger, []uint32{0}) + require.NoError(err) +} + +func TestLedgerKeychainFromIndices_Addresses(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + + key1, err := secp256k1.NewPrivateKey() + require.NoError(err) + key2, err := secp256k1.NewPrivateKey() + require.NoError(err) + key3, err := secp256k1.NewPrivateKey() + require.NoError(err) + + pubKey1 := key1.PublicKey() + pubKey2 := key2.PublicKey() + pubKey3 := key3.PublicKey() + + addr1 := pubKey1.Address() + addr2 := pubKey2.Address() + addr3 := pubKey3.Address() + + // 1 addr + ledger := ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys([]uint32{0}).Return([]*secp256k1.PublicKey{pubKey1}, nil).Times(1) + kc, err := NewKeychainFromIndices(ledger, []uint32{0}) + require.NoError(err) + + addrs := kc.Addresses() + require.Len(addrs, 1) + require.True(addrs.Contains(addr1)) + + // first 3 addresses + ledger = ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys([]uint32{0, 1, 2}).Return([]*secp256k1.PublicKey{pubKey1, pubKey2, pubKey3}, nil).Times(1) + kc, err = NewKeychainFromIndices(ledger, []uint32{0, 1, 2}) + require.NoError(err) + + addrs = kc.Addresses() + require.Len(addrs, 3) + require.Contains(addrs, addr1) + require.Contains(addrs, addr2) + require.Contains(addrs, addr3) + + // some 3 addresses + indices := []uint32{3, 7, 1} + pubKeys := []*secp256k1.PublicKey{pubKey1, pubKey2, pubKey3} + ledger = ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys(indices).Return(pubKeys, nil).Times(1) + kc, err = NewKeychainFromIndices(ledger, indices) + require.NoError(err) + + addrs = kc.Addresses() + require.Len(addrs, len(indices)) + require.Contains(addrs, addr1) + require.Contains(addrs, addr2) + require.Contains(addrs, addr3) + + // repeated addresses + indices = []uint32{3, 7, 1, 3, 1, 7} + pubKeys = []*secp256k1.PublicKey{pubKey1, pubKey2, pubKey3, pubKey1, pubKey2, pubKey3} + ledger = ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys(indices).Return(pubKeys, nil).Times(1) + kc, err = NewKeychainFromIndices(ledger, indices) + require.NoError(err) + + addrs = kc.Addresses() + require.Len(addrs, 3) + require.Contains(addrs, addr1) + require.Contains(addrs, addr2) + require.Contains(addrs, addr3) +} + +func TestLedgerKeychainFromIndices_Get(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + + key1, err := secp256k1.NewPrivateKey() + require.NoError(err) + key2, err := secp256k1.NewPrivateKey() + require.NoError(err) + key3, err := secp256k1.NewPrivateKey() + require.NoError(err) + + pubKey1 := key1.PublicKey() + pubKey2 := key2.PublicKey() + pubKey3 := key3.PublicKey() + + addr1 := pubKey1.Address() + addr2 := pubKey2.Address() + addr3 := pubKey3.Address() + + // 1 addr + ledger := ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys([]uint32{0}).Return([]*secp256k1.PublicKey{pubKey1}, nil).Times(1) + kc, err := NewKeychainFromIndices(ledger, []uint32{0}) + require.NoError(err) + + _, b := kc.Get(ids.GenerateTestShortID()) + require.False(b) + + s, b := kc.Get(addr1) + require.Equal(s.Address(), addr1) + require.True(b) + + // some 3 addresses + indices := []uint32{3, 7, 1} + pubKeys := []*secp256k1.PublicKey{pubKey1, pubKey2, pubKey3} + ledger = ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys(indices).Return(pubKeys, nil).Times(1) + kc, err = NewKeychainFromIndices(ledger, indices) + require.NoError(err) + + _, b = kc.Get(ids.GenerateTestShortID()) + require.False(b) + + s, b = kc.Get(addr1) + require.True(b) + require.Equal(s.Address(), addr1) + + s, b = kc.Get(addr2) + require.True(b) + require.Equal(s.Address(), addr2) + + s, b = kc.Get(addr3) + require.True(b) + require.Equal(s.Address(), addr3) +} + +func TestLedgerSignerFromIndices_SignHash(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + + key1, err := secp256k1.NewPrivateKey() + require.NoError(err) + key2, err := secp256k1.NewPrivateKey() + require.NoError(err) + key3, err := secp256k1.NewPrivateKey() + require.NoError(err) + + pubKey1 := key1.PublicKey() + pubKey2 := key2.PublicKey() + pubKey3 := key3.PublicKey() + + addr1 := pubKey1.Address() + addr2 := pubKey2.Address() + addr3 := pubKey3.Address() + + toSign := []byte{1, 2, 3, 4, 5} + expectedSignature1 := []byte{1, 1, 1} + expectedSignature2 := []byte{2, 2, 2} + expectedSignature3 := []byte{3, 3, 3} + + // ledger returns an incorrect number of signatures + ledger := ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys([]uint32{0}).Return([]*secp256k1.PublicKey{pubKey1}, nil).Times(1) + ledger.EXPECT().SignHash(toSign, []uint32{0}).Return([][]byte{}, nil).Times(1) + kc, err := NewKeychainFromIndices(ledger, []uint32{0}) + require.NoError(err) + + s, b := kc.Get(addr1) + require.True(b) + + _, err = s.SignHash(toSign) + require.ErrorIs(err, ErrInvalidNumSignatures) + + // ledger returns an error when asked for signature + ledger = ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys([]uint32{0}).Return([]*secp256k1.PublicKey{pubKey1}, nil).Times(1) + ledger.EXPECT().SignHash(toSign, []uint32{0}).Return([][]byte{expectedSignature1}, errTest).Times(1) + kc, err = NewKeychainFromIndices(ledger, []uint32{0}) + require.NoError(err) + + s, b = kc.Get(addr1) + require.True(b) + + _, err = s.SignHash(toSign) + require.ErrorIs(err, errTest) + + // good path 1 addr + ledger = ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys([]uint32{0}).Return([]*secp256k1.PublicKey{pubKey1}, nil).Times(1) + ledger.EXPECT().SignHash(toSign, []uint32{0}).Return([][]byte{expectedSignature1}, nil).Times(1) + kc, err = NewKeychainFromIndices(ledger, []uint32{0}) + require.NoError(err) + + s, b = kc.Get(addr1) + require.True(b) + + signature, err := s.SignHash(toSign) + require.NoError(err) + require.Equal(expectedSignature1, signature) + + // good path some 3 addresses + indices := []uint32{3, 7, 1} + pubKeys := []*secp256k1.PublicKey{pubKey1, pubKey2, pubKey3} + ledger = ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys(indices).Return(pubKeys, nil).Times(1) + ledger.EXPECT().SignHash(toSign, []uint32{indices[0]}).Return([][]byte{expectedSignature1}, nil).Times(1) + ledger.EXPECT().SignHash(toSign, []uint32{indices[1]}).Return([][]byte{expectedSignature2}, nil).Times(1) + ledger.EXPECT().SignHash(toSign, []uint32{indices[2]}).Return([][]byte{expectedSignature3}, nil).Times(1) + kc, err = NewKeychainFromIndices(ledger, indices) + require.NoError(err) + + s, b = kc.Get(addr1) + require.True(b) + + signature, err = s.SignHash(toSign) + require.NoError(err) + require.Equal(expectedSignature1, signature) + + s, b = kc.Get(addr2) + require.True(b) + + signature, err = s.SignHash(toSign) + require.NoError(err) + require.Equal(expectedSignature2, signature) + + s, b = kc.Get(addr3) + require.True(b) + + signature, err = s.SignHash(toSign) + require.NoError(err) + require.Equal(expectedSignature3, signature) +} + +func TestShouldUseSignHash(t *testing.T) { + require := require.New(t) + + addr := ids.ShortID{ + 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, + 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, + 0x44, 0x55, 0x66, 0x77, + } + + txID := ids.ID{ + 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, + 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, + 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, + 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, + } + + avaxAssetID, err := ids.FromString("FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z") + require.NoError(err) + + baseTxData := txs.BaseTx{ + BaseTx: avax.BaseTx{ + NetworkID: 1, + BlockchainID: ids.GenerateTestID(), + Ins: []*avax.TransferableInput{ + { + UTXOID: avax.UTXOID{ + TxID: txID, + OutputIndex: 1, + }, + Asset: avax.Asset{ + ID: avaxAssetID, + }, + In: &secp256k1fx.TransferInput{ + Amt: 1000000, + Input: secp256k1fx.Input{ + SigIndices: []uint32{0}, + }, + }, + }, + }, + Outs: []*avax.TransferableOutput{}, + }, + } + + testCases := []struct { + name string + tx txs.UnsignedTx + expected bool + }{ + { + name: "TransferSubnetOwnershipTx should use SignHash", + tx: &txs.TransferSubnetOwnershipTx{ + BaseTx: baseTxData, + Subnet: ids.GenerateTestID(), + Owner: &secp256k1fx.OutputOwners{ + Locktime: 0, + Threshold: 1, + Addrs: []ids.ShortID{addr}, + }, + SubnetAuth: &secp256k1fx.Input{ + SigIndices: []uint32{0}, + }, + }, + expected: true, + }, + { + name: "BaseTx should not use SignHash", + tx: &txs.BaseTx{BaseTx: baseTxData.BaseTx}, + expected: false, + }, + { + name: "AddValidatorTx should not use SignHash", + tx: &txs.AddValidatorTx{ + BaseTx: baseTxData, + StakeOuts: []*avax.TransferableOutput{}, + RewardsOwner: &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{addr}, + }, + }, + expected: false, + }, + { + name: "AddSubnetValidatorTx should not use SignHash", + tx: &txs.AddSubnetValidatorTx{ + BaseTx: baseTxData, + SubnetAuth: &secp256k1fx.Input{SigIndices: []uint32{0}}, + }, + expected: false, + }, + { + name: "AddDelegatorTx should not use SignHash", + tx: &txs.AddDelegatorTx{ + BaseTx: baseTxData, + StakeOuts: []*avax.TransferableOutput{}, + DelegationRewardsOwner: &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{addr}, + }, + }, + expected: false, + }, + { + name: "RemoveSubnetValidatorTx should not use SignHash", + tx: &txs.RemoveSubnetValidatorTx{ + BaseTx: baseTxData, + SubnetAuth: &secp256k1fx.Input{SigIndices: []uint32{0}}, + }, + expected: false, + }, + { + name: "TransformSubnetTx should not use SignHash", + tx: &txs.TransformSubnetTx{ + BaseTx: baseTxData, + SubnetAuth: &secp256k1fx.Input{SigIndices: []uint32{0}}, + }, + expected: false, + }, + { + name: "AddPermissionlessValidatorTx should not use SignHash", + tx: &txs.AddPermissionlessValidatorTx{ + BaseTx: baseTxData, + Signer: &signer.Empty{}, + StakeOuts: []*avax.TransferableOutput{}, + ValidatorRewardsOwner: &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{addr}, + }, + DelegatorRewardsOwner: &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{addr}, + }, + }, + expected: false, + }, + { + name: "AddPermissionlessDelegatorTx should not use SignHash", + tx: &txs.AddPermissionlessDelegatorTx{ + BaseTx: baseTxData, + StakeOuts: []*avax.TransferableOutput{}, + DelegationRewardsOwner: &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{addr}, + }, + }, + expected: false, + }, + { + name: "ConvertSubnetToL1Tx should not use SignHash", + tx: &txs.ConvertSubnetToL1Tx{ + BaseTx: baseTxData, + SubnetAuth: &secp256k1fx.Input{SigIndices: []uint32{0}}, + Validators: []*txs.ConvertSubnetToL1Validator{}, + }, + expected: false, + }, + { + name: "RegisterL1ValidatorTx should not use SignHash", + tx: &txs.RegisterL1ValidatorTx{BaseTx: baseTxData}, + expected: false, + }, + { + name: "SetL1ValidatorWeightTx should not use SignHash", + tx: &txs.SetL1ValidatorWeightTx{BaseTx: baseTxData}, + expected: false, + }, + { + name: "IncreaseL1ValidatorBalanceTx should not use SignHash", + tx: &txs.IncreaseL1ValidatorBalanceTx{BaseTx: baseTxData}, + expected: false, + }, + { + name: "DisableL1ValidatorTx should not use SignHash", + tx: &txs.DisableL1ValidatorTx{ + BaseTx: baseTxData, + DisableAuth: &secp256k1fx.Input{SigIndices: []uint32{0}}, + }, + expected: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(_ *testing.T) { + unsignedBytes, err := txs.Codec.Marshal(txs.CodecVersion, &tc.tx) + require.NoError(err) + result, err := shouldUseSignHash(unsignedBytes) + require.NoError(err) + require.Equal(tc.expected, result) + }) + } + + // Test invalid bytes - should return error + t.Run("Invalid bytes should return error", func(_ *testing.T) { + _, err := shouldUseSignHash([]byte{0xFF, 0xFF, 0xFF}) + require.ErrorIs(err, codec.ErrUnknownVersion) + }) +} + +func TestLedgerSigner_Sign_WithPChainTransferSubnetOwnership(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + + key, err := secp256k1.NewPrivateKey() + require.NoError(err) + pubKey := key.PublicKey() + addr := pubKey.Address() + expectedSignature := []byte{1, 1, 1} + + // Create a TransferSubnetOwnershipTx + baseTxData := txs.BaseTx{ + BaseTx: avax.BaseTx{ + NetworkID: 1, + BlockchainID: ids.GenerateTestID(), + Ins: []*avax.TransferableInput{}, + Outs: []*avax.TransferableOutput{}, + }, + } + + var tx txs.UnsignedTx = &txs.TransferSubnetOwnershipTx{ + BaseTx: baseTxData, + Subnet: ids.GenerateTestID(), + Owner: &secp256k1fx.OutputOwners{ + Locktime: 0, + Threshold: 1, + Addrs: []ids.ShortID{addr}, + }, + SubnetAuth: &secp256k1fx.Input{ + SigIndices: []uint32{0}, + }, + } + + unsignedBytes, err := txs.Codec.Marshal(txs.CodecVersion, &tx) + require.NoError(err) + + // When signing with P-Chain alias, should use SignHash + ledger := ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys([]uint32{0}).Return([]*secp256k1.PublicKey{pubKey}, nil).Times(1) + ledger.EXPECT().SignHash(hashing.ComputeHash256(unsignedBytes), []uint32{0}).Return([][]byte{expectedSignature}, nil).Times(1) + + kc, err := NewKeychain(ledger, 1) + require.NoError(err) + + s, b := kc.Get(addr) + require.True(b) + + signature, err := s.Sign(unsignedBytes, keychain.WithChainAlias("P")) + require.NoError(err) + require.Equal(expectedSignature, signature) +} + +func TestLedgerSigner_Sign_WithPChainNonTransferSubnetOwnership(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + + key, err := secp256k1.NewPrivateKey() + require.NoError(err) + pubKey := key.PublicKey() + addr := pubKey.Address() + expectedSignature := []byte{2, 2, 2} + + // Create a BaseTx + var tx txs.UnsignedTx = &txs.BaseTx{ + BaseTx: avax.BaseTx{ + NetworkID: 1, + BlockchainID: ids.GenerateTestID(), + Ins: []*avax.TransferableInput{}, + Outs: []*avax.TransferableOutput{}, + }, + } + + unsignedBytes, err := txs.Codec.Marshal(txs.CodecVersion, &tx) + require.NoError(err) + + // When signing with P-Chain alias but NOT TransferSubnetOwnershipTx, should use Sign (not SignHash) + ledger := ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys([]uint32{0}).Return([]*secp256k1.PublicKey{pubKey}, nil).Times(1) + ledger.EXPECT().Sign(unsignedBytes, []uint32{0}).Return([][]byte{expectedSignature}, nil).Times(1) + + kc, err := NewKeychain(ledger, 1) + require.NoError(err) + + s, b := kc.Get(addr) + require.True(b) + + signature, err := s.Sign(unsignedBytes, keychain.WithChainAlias("P")) + require.NoError(err) + require.Equal(expectedSignature, signature) +} + +func TestLedgerSigner_Sign_WithoutChainAlias(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + + key, err := secp256k1.NewPrivateKey() + require.NoError(err) + pubKey := key.PublicKey() + addr := pubKey.Address() + toSign := []byte{1, 2, 3, 4, 5} + expectedSignature := []byte{3, 3, 3} + + // When signing without chain alias, should use Sign (not SignHash) + ledger := ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys([]uint32{0}).Return([]*secp256k1.PublicKey{pubKey}, nil).Times(1) + ledger.EXPECT().Sign(toSign, []uint32{0}).Return([][]byte{expectedSignature}, nil).Times(1) + + kc, err := NewKeychain(ledger, 1) + require.NoError(err) + + s, b := kc.Get(addr) + require.True(b) + + signature, err := s.Sign(toSign) + require.NoError(err) + require.Equal(expectedSignature, signature) +} + +func TestLedgerSigner_Sign_WithNonPChain(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + + key, err := secp256k1.NewPrivateKey() + require.NoError(err) + pubKey := key.PublicKey() + addr := pubKey.Address() + toSign := []byte{1, 2, 3, 4, 5} + expectedSignature := []byte{4, 4, 4} + + // When signing with non-P-Chain alias, should use Sign (not SignHash) + ledger := ledgermock.NewLedger(ctrl) + ledger.EXPECT().PubKeys([]uint32{0}).Return([]*secp256k1.PublicKey{pubKey}, nil).Times(1) + ledger.EXPECT().Sign(toSign, []uint32{0}).Return([][]byte{expectedSignature}, nil).Times(1) + + kc, err := NewKeychain(ledger, 1) + require.NoError(err) + + s, b := kc.Get(addr) + require.True(b) + + signature, err := s.Sign(toSign, keychain.WithChainAlias("X")) + require.NoError(err) + require.Equal(expectedSignature, signature) +} diff --git a/keychain/ledger/ledgermock/ledger.go b/keychain/ledger/ledgermock/ledger.go new file mode 100644 index 0000000..851240f --- /dev/null +++ b/keychain/ledger/ledgermock/ledger.go @@ -0,0 +1,131 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/ava-labs/avalanche-tooling-sdk-go/keychain/ledger (interfaces: Ledger) +// +// Generated by this command: +// +// mockgen -package=ledgermock -destination=ledgermock/ledger.go -mock_names=Ledger=Ledger . Ledger +// + +// Package ledgermock is a generated GoMock package. +package ledgermock + +import ( + reflect "reflect" + + secp256k1 "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" + version "github.com/ava-labs/avalanchego/version" + gomock "go.uber.org/mock/gomock" +) + +// Ledger is a mock of Ledger interface. +type Ledger struct { + ctrl *gomock.Controller + recorder *LedgerMockRecorder + isgomock struct{} +} + +// LedgerMockRecorder is the mock recorder for Ledger. +type LedgerMockRecorder struct { + mock *Ledger +} + +// NewLedger creates a new mock instance. +func NewLedger(ctrl *gomock.Controller) *Ledger { + mock := &Ledger{ctrl: ctrl} + mock.recorder = &LedgerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *Ledger) EXPECT() *LedgerMockRecorder { + return m.recorder +} + +// Disconnect mocks base method. +func (m *Ledger) Disconnect() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Disconnect") + ret0, _ := ret[0].(error) + return ret0 +} + +// Disconnect indicates an expected call of Disconnect. +func (mr *LedgerMockRecorder) Disconnect() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Disconnect", reflect.TypeOf((*Ledger)(nil).Disconnect)) +} + +// PubKey mocks base method. +func (m *Ledger) PubKey(addressIndex uint32) (*secp256k1.PublicKey, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PubKey", addressIndex) + ret0, _ := ret[0].(*secp256k1.PublicKey) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// PubKey indicates an expected call of PubKey. +func (mr *LedgerMockRecorder) PubKey(addressIndex any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PubKey", reflect.TypeOf((*Ledger)(nil).PubKey), addressIndex) +} + +// PubKeys mocks base method. +func (m *Ledger) PubKeys(addressIndices []uint32) ([]*secp256k1.PublicKey, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PubKeys", addressIndices) + ret0, _ := ret[0].([]*secp256k1.PublicKey) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// PubKeys indicates an expected call of PubKeys. +func (mr *LedgerMockRecorder) PubKeys(addressIndices any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PubKeys", reflect.TypeOf((*Ledger)(nil).PubKeys), addressIndices) +} + +// Sign mocks base method. +func (m *Ledger) Sign(unsignedTxBytes []byte, addressIndices []uint32) ([][]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Sign", unsignedTxBytes, addressIndices) + ret0, _ := ret[0].([][]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Sign indicates an expected call of Sign. +func (mr *LedgerMockRecorder) Sign(unsignedTxBytes, addressIndices any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sign", reflect.TypeOf((*Ledger)(nil).Sign), unsignedTxBytes, addressIndices) +} + +// SignHash mocks base method. +func (m *Ledger) SignHash(hash []byte, addressIndices []uint32) ([][]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SignHash", hash, addressIndices) + ret0, _ := ret[0].([][]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SignHash indicates an expected call of SignHash. +func (mr *LedgerMockRecorder) SignHash(hash, addressIndices any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignHash", reflect.TypeOf((*Ledger)(nil).SignHash), hash, addressIndices) +} + +// Version mocks base method. +func (m *Ledger) Version() (*version.Semantic, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Version") + ret0, _ := ret[0].(*version.Semantic) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Version indicates an expected call of Version. +func (mr *LedgerMockRecorder) Version() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Version", reflect.TypeOf((*Ledger)(nil).Version)) +} diff --git a/keychain/ledger/mocks_generate_test.go b/keychain/ledger/mocks_generate_test.go new file mode 100644 index 0000000..29093e1 --- /dev/null +++ b/keychain/ledger/mocks_generate_test.go @@ -0,0 +1,6 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package ledger + +//go:generate go run go.uber.org/mock/mockgen -package=${GOPACKAGE}mock -destination=${GOPACKAGE}mock/ledger.go -mock_names=Ledger=Ledger . Ledger From fb7971695922a56f438d0fe3ceb34132aaae7b0b Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 10 Oct 2025 16:41:57 -0300 Subject: [PATCH 07/10] Add package structure section to ledger keychain README Adds a tree diagram showing the package file structure and descriptions of the core files to help users understand the organization. --- keychain/ledger/README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/keychain/ledger/README.md b/keychain/ledger/README.md index 0adfb65..c6ebee3 100644 --- a/keychain/ledger/README.md +++ b/keychain/ledger/README.md @@ -221,6 +221,27 @@ This example includes: - C-Chain atomic transactions (Import/Export) - L1 validator operations (Register, SetWeight, IncreaseBalance, Disable) +## Package Structure + +``` +keychain/ledger/ +├── ledger.go # Ledger interface definition +├── ledger_device.go # Device implementation +├── ledger_device_test.go # Tests for device implementation +├── ledger_keychain.go # KeyChain and signer implementations +├── ledger_keychain_test.go # Tests for keychain implementation +├── mocks_generate_test.go # Mock generation directives +├── ledgermock/ +│ └── ledger.go # Generated mock for Ledger interface +├── examples/ +│ └── validate-ledger-txs.go # Comprehensive example of all transaction types +└── README.md # This file +``` + +**Core Files:** +- **ledger_device.go**: Implements the `Ledger` interface using `github.com/ava-labs/ledger-avalanche-go` +- **ledger_keychain.go**: Implements `keychain.Keychain` and `c.EthKeychain` interfaces, manages address derivation and transaction signing + ## Integration with Avalanche Wallet The Ledger keychain implements both `keychain.Keychain` and `c.EthKeychain` interfaces, making it compatible with the Avalanche primary wallet: From 474498e3e3345547072729bf1e5ac4c07dc40767 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 10 Oct 2025 16:43:30 -0300 Subject: [PATCH 08/10] add package structure section to cubesigner keychain README --- keychain/cubesigner/README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/keychain/cubesigner/README.md b/keychain/cubesigner/README.md index 8c46bb4..604ffa5 100644 --- a/keychain/cubesigner/README.md +++ b/keychain/cubesigner/README.md @@ -253,6 +253,22 @@ signer, _ := kc.Get(address) signature, err := signer.SignHash(hashBytes) ``` +## Package Structure + +``` +keychain/cubesigner/ +├── cubesigner.go # CubeSigner client interface definition +├── cubesigner_keychain.go # Keychain and signer implementations +├── cubesigner_keychain_test.go # Tests for keychain implementation +├── mocks_generate_test.go # Mock generation directives +├── cubesignermock/ +│ └── cubesigner_client.go # Generated mock for CubeSignerClient interface +└── README.md # This file +``` + +**Core Files:** +- **cubesigner_keychain.go**: Implements `keychain.Keychain` and `c.EthKeychain` interfaces, handles key validation, format conversion, and transaction signing via CubeSigner API + ## Integration with Avalanche Wallet The CubeSigner keychain implements both `keychain.Keychain` and `c.EthKeychain` interfaces, making it compatible with the Avalanche primary wallet: From e66a39fddf849a545a0a47a5e6feb42a1c0a1d8f Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 10 Oct 2025 16:43:30 -0300 Subject: [PATCH 09/10] add package structure section to cubesigner keychain README --- keychain/cubesigner/README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/keychain/cubesigner/README.md b/keychain/cubesigner/README.md index 8c46bb4..604ffa5 100644 --- a/keychain/cubesigner/README.md +++ b/keychain/cubesigner/README.md @@ -253,6 +253,22 @@ signer, _ := kc.Get(address) signature, err := signer.SignHash(hashBytes) ``` +## Package Structure + +``` +keychain/cubesigner/ +├── cubesigner.go # CubeSigner client interface definition +├── cubesigner_keychain.go # Keychain and signer implementations +├── cubesigner_keychain_test.go # Tests for keychain implementation +├── mocks_generate_test.go # Mock generation directives +├── cubesignermock/ +│ └── cubesigner_client.go # Generated mock for CubeSignerClient interface +└── README.md # This file +``` + +**Core Files:** +- **cubesigner_keychain.go**: Implements `keychain.Keychain` and `c.EthKeychain` interfaces, handles key validation, format conversion, and transaction signing via CubeSigner API + ## Integration with Avalanche Wallet The CubeSigner keychain implements both `keychain.Keychain` and `c.EthKeychain` interfaces, making it compatible with the Avalanche primary wallet: From b87949cd3dd2c2340087acbf3abbb554d5491a60 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Thu, 16 Oct 2025 13:18:23 -0300 Subject: [PATCH 10/10] Add chain auto-detection and refactor keychain signing This commit introduces automatic chain detection for transactions and refactors the keychain signing interface to simplify usage by removing the need for explicit chain specification. Key changes: - Add utils/txs.go with AutoDetectChain function that detects P/X/C chain transactions by attempting to unmarshal with each chain's codec - Add ChainAlias type and constants (PChainAlias, XChainAlias, CChainAlias, UndefinedAlias) to constants package - Refactor ledger keychain Sign method to use auto-detection instead of requiring ChainAlias option - Remove cubesigner keychain implementation and related files - Update signature aggregator to use icm-services API types - Fix evm.go import to use subnet-evm predicate package - Add comprehensive unit tests covering all P-chain, X-chain, and C-chain transaction types --- constants/constants.go | 14 + evm/evm.go | 5 +- go.mod | 101 ++- go.sum | 218 +++--- interchain/signature-aggregator.go | 5 +- interchain/signature_aggregator_types.go | 19 - keychain/cubesigner/README.md | 284 ------- keychain/cubesigner/cubesigner_client.go | 25 - keychain/cubesigner/cubesigner_keychain.go | 272 ------- .../cubesigner/cubesigner_keychain_test.go | 334 --------- .../cubesignermock/cubesigner_client.go | 97 --- keychain/cubesigner/mocks_generate_test.go | 6 - keychain/ledger/ledger_keychain.go | 14 +- keychain/ledger/ledger_keychain_test.go | 7 +- utils/txs.go | 52 ++ utils/txs_test.go | 709 ++++++++++++++++++ 16 files changed, 934 insertions(+), 1228 deletions(-) delete mode 100644 interchain/signature_aggregator_types.go delete mode 100644 keychain/cubesigner/README.md delete mode 100644 keychain/cubesigner/cubesigner_client.go delete mode 100644 keychain/cubesigner/cubesigner_keychain.go delete mode 100644 keychain/cubesigner/cubesigner_keychain_test.go delete mode 100644 keychain/cubesigner/cubesignermock/cubesigner_client.go delete mode 100644 keychain/cubesigner/mocks_generate_test.go create mode 100644 utils/txs.go create mode 100644 utils/txs_test.go diff --git a/constants/constants.go b/constants/constants.go index 43b8a51..1672ece 100644 --- a/constants/constants.go +++ b/constants/constants.go @@ -15,3 +15,17 @@ const ( SignatureTimeout = 5 * time.Minute ) + +// ChainAlias represents a blockchain alias identifier +type ChainAlias string + +const ( + // PChainAlias is the alias for P Chain + PChainAlias ChainAlias = "P" + // XChainAlias is the alias for X Chain + XChainAlias ChainAlias = "X" + // CChainAlias is the alias for C Chain + CChainAlias ChainAlias = "C" + // UndefinedAlias is used for undefined chains + UndefinedAlias ChainAlias = "" +) diff --git a/evm/evm.go b/evm/evm.go index e647c5a..1e8f3f7 100644 --- a/evm/evm.go +++ b/evm/evm.go @@ -12,7 +12,6 @@ import ( "strings" "time" - "github.com/ava-labs/avalanchego/vms/evm/predicate" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/crypto" @@ -21,6 +20,7 @@ import ( "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/plugin/evm/upgrade/legacy" "github.com/ava-labs/subnet-evm/precompile/contracts/warp" + "github.com/ava-labs/subnet-evm/predicate" "github.com/ava-labs/avalanche-tooling-sdk-go/constants" "github.com/ava-labs/avalanche-tooling-sdk-go/utils" @@ -28,6 +28,7 @@ import ( avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" ethereum "github.com/ava-labs/libevm" ethparams "github.com/ava-labs/libevm/params" + subnetEvmUtils "github.com/ava-labs/subnet-evm/utils" ) const ( @@ -530,7 +531,7 @@ func (client Client) TransactWithWarpMessage( accessList := types.AccessList{ types.AccessTuple{ Address: warp.ContractAddress, - StorageKeys: predicate.New(warpMessage.Bytes()), + StorageKeys: subnetEvmUtils.BytesToHashSlice(predicate.PackPredicate(warpMessage.Bytes())), }, } msg := ethereum.CallMsg{ diff --git a/go.mod b/go.mod index ad9fa16..7d66b31 100644 --- a/go.mod +++ b/go.mod @@ -1,48 +1,32 @@ module github.com/ava-labs/avalanche-tooling-sdk-go -go 1.24.8 +go 1.23.11 + +toolchain go1.24.7 require ( - github.com/ava-labs/avalanchego v1.13.6-0.20251010155017-cd3e6bee30ab - github.com/ava-labs/coreth v0.15.4-rc.3.0.20251002221438-a857a64c28ea + github.com/ava-labs/avalanchego v1.13.3-0.20250701190537-839ace23368a + github.com/ava-labs/coreth v0.15.2-rc.0.0.20250620163936-0058a7ec6d9d + github.com/ava-labs/icm-services v1.4.1-0.20250729175537-a74c15880023 github.com/ava-labs/ledger-avalanche-go v1.1.0 - github.com/ava-labs/libevm v1.13.15-0.20251002164226-35926db4d661 - github.com/ava-labs/subnet-evm v0.7.10-0.20251010143515-0e03d5e4c9c9 + github.com/ava-labs/libevm v1.13.14-0.3.0.rc.1 + github.com/ava-labs/subnet-evm v0.7.5 github.com/cavaliergopher/grab/v3 v3.0.1 - github.com/cubist-labs/cubesigner-go-sdk v0.0.16 - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.10.0 github.com/tyler-smith/go-bip32 v1.0.0 - go.uber.org/mock v0.6.0 + go.uber.org/mock v0.5.2 go.uber.org/zap v1.27.0 - golang.org/x/exp v0.0.0-20251002181428-27f1f14c8bb9 + golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 ) require ( - connectrpc.com/connect v1.19.1 // indirect - connectrpc.com/grpcreflect v1.3.0 // indirect github.com/DataDog/zstd v1.5.2 // indirect github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e // indirect github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/StephenButtolph/canoto v0.17.2 // indirect + github.com/StephenButtolph/canoto v0.15.0 // indirect github.com/VictoriaMetrics/fastcache v1.12.1 // indirect - github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect - github.com/ava-labs/firewood-go-ethhash/ffi v0.0.12 // indirect - github.com/aws/aws-sdk-go-v2 v1.39.2 // indirect - github.com/aws/aws-sdk-go-v2/config v1.31.12 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.18.16 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.9 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9 // indirect - github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.19 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.29.6 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.38.6 // indirect - github.com/aws/smithy-go v1.23.0 // indirect + github.com/ava-labs/ledger-avalanche/go v0.0.0-20241009183145-e6f90a8a1a60 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.10.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.5 // indirect @@ -58,16 +42,18 @@ require ( github.com/consensys/bavard v0.1.13 // indirect github.com/consensys/gnark-crypto v0.12.1 // indirect github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 // indirect - github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect + github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/deckarep/golang-set/v2 v2.1.0 // indirect - github.com/ethereum/c-kzg-4844 v1.0.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect + github.com/ethereum/c-kzg-4844 v0.4.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 // indirect github.com/getsentry/sentry-go v0.35.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-viper/mapstructure/v2 v2.3.0 // indirect github.com/gofrs/flock v0.12.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect @@ -78,7 +64,8 @@ require ( github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/rpc v1.2.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect github.com/holiman/uint256 v1.2.4 // indirect github.com/klauspost/compress v1.18.0 // indirect @@ -88,20 +75,27 @@ require ( github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/mr-tron/base58 v1.2.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/oapi-codegen/runtime v1.1.1 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/pingcap/errors v0.11.4 // indirect github.com/pires/go-proxyproto v0.6.2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.23.2 // indirect + github.com/prometheus/client_golang v1.23.0 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.66.1 // indirect + github.com/prometheus/common v0.65.0 // indirect github.com/prometheus/procfs v0.16.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rs/cors v1.11.1 // indirect + github.com/sagikazarmark/locafero v0.9.0 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect - github.com/spf13/cast v1.10.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.14.0 // indirect + github.com/spf13/cast v1.9.2 // indirect + github.com/spf13/pflag v1.0.7 // indirect + github.com/spf13/viper v1.20.1 // indirect + github.com/subosito/gotenv v1.6.0 // indirect github.com/supranational/blst v0.3.14 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect github.com/tklauser/go-sysconf v0.3.15 // indirect @@ -110,29 +104,28 @@ require ( github.com/zondax/golem v0.27.0 // indirect github.com/zondax/hid v0.9.2 // indirect github.com/zondax/ledger-go v1.0.1 // indirect - go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/otel v1.38.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/otel v1.37.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 // indirect - go.opentelemetry.io/otel/metric v1.38.0 // indirect - go.opentelemetry.io/otel/sdk v1.38.0 // indirect - go.opentelemetry.io/otel/trace v1.38.0 // indirect - go.opentelemetry.io/proto/otlp v1.8.0 // indirect + go.opentelemetry.io/otel/metric v1.37.0 // indirect + go.opentelemetry.io/otel/sdk v1.37.0 // indirect + go.opentelemetry.io/otel/trace v1.37.0 // indirect + go.opentelemetry.io/proto/otlp v1.7.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.yaml.in/yaml/v2 v2.4.2 // indirect - golang.org/x/crypto v0.42.0 // indirect - golang.org/x/net v0.45.0 // indirect - golang.org/x/sync v0.17.0 // indirect - golang.org/x/sys v0.36.0 // indirect - golang.org/x/term v0.35.0 // indirect - golang.org/x/text v0.29.0 // indirect + golang.org/x/crypto v0.41.0 // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/term v0.34.0 // indirect + golang.org/x/text v0.28.0 // indirect golang.org/x/time v0.12.0 // indirect gonum.org/v1/gonum v0.16.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect - google.golang.org/grpc v1.76.0 // indirect - google.golang.org/protobuf v1.36.10 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c // indirect + google.golang.org/grpc v1.75.0 // indirect + google.golang.org/protobuf v1.36.8 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect diff --git a/go.sum b/go.sum index 8d0cb5d..fef7ecf 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14= -connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w= -connectrpc.com/grpcreflect v1.3.0 h1:Y4V+ACf8/vOb1XOc251Qun7jMB75gCUNw6llvB9csXc= -connectrpc.com/grpcreflect v1.3.0/go.mod h1:nfloOtCS8VUQOQ1+GTdFzVg2CJo4ZGaat8JIovCtDYs= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= -github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= @@ -18,65 +14,35 @@ github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec/go.mod h1 github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= -github.com/StephenButtolph/canoto v0.17.2 h1:kRLJwtYk0bzdGEeEvwHaVmmDm0HFHxrS0VlVN5Hyo7U= -github.com/StephenButtolph/canoto v0.17.2/go.mod h1:IcnAHC6nJUfQFVR9y60ko2ecUqqHHSB6UwI9NnBFZnE= +github.com/StephenButtolph/canoto v0.15.0 h1:3iGdyTSQZ7/y09WaJCe0O/HIi53ZyTrnmVzfCqt64mM= +github.com/StephenButtolph/canoto v0.15.0/go.mod h1:IcnAHC6nJUfQFVR9y60ko2ecUqqHHSB6UwI9NnBFZnE= github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= 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/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= -github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/ava-labs/avalanchego v1.13.6-0.20251010155017-cd3e6bee30ab h1:lS6N4VvIP6ihFWwP7ytGF9+AhCJoaVcsHNLg/LJD/Hw= -github.com/ava-labs/avalanchego v1.13.6-0.20251010155017-cd3e6bee30ab/go.mod h1:EL0MGbL2liE9jp4QtCHR2thkNl8hCkD26DJ+7cmcaqs= -github.com/ava-labs/coreth v0.15.4-rc.3.0.20251002221438-a857a64c28ea h1:vrHUSx6hlQgdVufhtT9LT9i7eHZcWmBEjH9cBozDLuc= -github.com/ava-labs/coreth v0.15.4-rc.3.0.20251002221438-a857a64c28ea/go.mod h1:y/14LplmA0lLwIDlKiGAZ8OlxQ7OxhaU2dfkYcviLPM= -github.com/ava-labs/firewood-go-ethhash/ffi v0.0.12 h1:aMcrLbpJ/dyu2kZDf/Di/4JIWsUcYPyTDKymiHpejt0= -github.com/ava-labs/firewood-go-ethhash/ffi v0.0.12/go.mod h1:cq89ua3iiZ5wPBALTEQS5eG8DIZcs7ov6OiL4YR1BVY= +github.com/ava-labs/avalanchego v1.13.3-0.20250701190537-839ace23368a h1:mLzIwC2BZXqZiFEiBB2myEAhU9dfYroQ6pNYikttrYw= +github.com/ava-labs/avalanchego v1.13.3-0.20250701190537-839ace23368a/go.mod h1:s7W/kim5L6hiD2PB1v/Ozy1ZZyoLQ4H6mxVO0aMnxng= +github.com/ava-labs/coreth v0.15.2-rc.0.0.20250620163936-0058a7ec6d9d h1:nZA7nEhlf2uWM1doZz4WFiMkZVZ0iH+c3IebokIO1sk= +github.com/ava-labs/coreth v0.15.2-rc.0.0.20250620163936-0058a7ec6d9d/go.mod h1:Mk5g2ZI9lEbxA67qikOtUrq71Yr389B34S+Ddsa51K4= +github.com/ava-labs/icm-services v1.4.1-0.20250729175537-a74c15880023 h1:6WaIMdc35U7mNOmWJAO0lthvsM8e38Jl+mq/RiUopsU= +github.com/ava-labs/icm-services v1.4.1-0.20250729175537-a74c15880023/go.mod h1:17BcQ5NzeHSo80tIKaiP8MsxR7UJTeSQNmF3+cocXe8= github.com/ava-labs/ledger-avalanche-go v1.1.0 h1:OkscKtb/gX20HBt8RyAtwXLrQnCEls5SzWGieE7NoNM= github.com/ava-labs/ledger-avalanche-go v1.1.0/go.mod h1:mAlG9ptnPjvNoLGLHXnM3slGY8ewvBJtJNVTEjG8KvI= -github.com/ava-labs/libevm v1.13.15-0.20251002164226-35926db4d661 h1:lt4yQE1HMvxWrdD5RFj+h9kWUsZK2rmNohvkeQsbG9M= -github.com/ava-labs/libevm v1.13.15-0.20251002164226-35926db4d661/go.mod h1:ivRC/KojP8sai7j8WnpXIReQpcRklL2bIzoysnjpARQ= -github.com/ava-labs/subnet-evm v0.7.10-0.20251010143515-0e03d5e4c9c9 h1:qRKllyK9es8t8u/Q8QLPtvQiHmL1Ar2f60VYw4a3HMo= -github.com/ava-labs/subnet-evm v0.7.10-0.20251010143515-0e03d5e4c9c9/go.mod h1:lcCfer4eo/Po0uVaTsxGFNMpYYpMBYAdKysaeVTJDFc= -github.com/aws/aws-sdk-go-v2 v1.39.2 h1:EJLg8IdbzgeD7xgvZ+I8M1e0fL0ptn/M47lianzth0I= -github.com/aws/aws-sdk-go-v2 v1.39.2/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY= -github.com/aws/aws-sdk-go-v2/config v1.31.12 h1:pYM1Qgy0dKZLHX2cXslNacbcEFMkDMl+Bcj5ROuS6p8= -github.com/aws/aws-sdk-go-v2/config v1.31.12/go.mod h1:/MM0dyD7KSDPR+39p9ZNVKaHDLb9qnfDurvVS2KAhN8= -github.com/aws/aws-sdk-go-v2/credentials v1.18.16 h1:4JHirI4zp958zC026Sm+V4pSDwW4pwLefKrc0bF2lwI= -github.com/aws/aws-sdk-go-v2/credentials v1.18.16/go.mod h1:qQMtGx9OSw7ty1yLclzLxXCRbrkjWAM7JnObZjmCB7I= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.9 h1:Mv4Bc0mWmv6oDuSWTKnk+wgeqPL5DRFu5bQL9BGPQ8Y= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.9/go.mod h1:IKlKfRppK2a1y0gy1yH6zD+yX5uplJ6UuPlgd48dJiQ= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9 h1:se2vOWGD3dWQUtfn4wEjRQJb1HK1XsNIt825gskZ970= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9/go.mod h1:hijCGH2VfbZQxqCDN7bwz/4dzxV+hkyhjawAtdPWKZA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9 h1:6RBnKZLkJM4hQ+kN6E7yWFveOTg8NLPHAkqrs4ZPlTU= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9/go.mod h1:V9rQKRmK7AWuEsOMnHzKj8WyrIir1yUJbZxDuZLFvXI= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 h1:oegbebPEMA/1Jny7kvwejowCaHz1FWZAQ94WXFNCyTM= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1/go.mod h1:kemo5Myr9ac0U9JfSjMo9yHLtw+pECEHsFtJ9tqCEI8= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9 h1:5r34CgVOD4WZudeEKZ9/iKpiT6cM1JyEROpXjOcdWv8= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9/go.mod h1:dB12CEbNWPbzO2uC6QSWHteqOg4JfBVJOojbAoAUb5I= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.19 h1:O2xbipq7k1kTct69V7mFidwTagld9c/6iyK+3yo+QNg= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.19/go.mod h1:CxTOwBy2Qs8/+yV7fkz4eZB1RB5qeWaW9SvznvFLgRA= -github.com/aws/aws-sdk-go-v2/service/sso v1.29.6 h1:A1oRkiSQOWstGh61y4Wc/yQ04sqrQZr1Si/oAXj20/s= -github.com/aws/aws-sdk-go-v2/service/sso v1.29.6/go.mod h1:5PfYspyCU5Vw1wNPsxi15LZovOnULudOQuVxphSflQA= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1 h1:5fm5RTONng73/QA73LhCNR7UT9RpFH3hR6HWL6bIgVY= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1/go.mod h1:xBEjWD13h+6nq+z4AkqSfSvqRKFgDIQeaMguAJndOWo= -github.com/aws/aws-sdk-go-v2/service/sts v1.38.6 h1:p3jIvqYwUZgu/XYeI48bJxOhvm47hZb5HUQ0tn6Q9kA= -github.com/aws/aws-sdk-go-v2/service/sts v1.38.6/go.mod h1:WtKK+ppze5yKPkZ0XwqIVWD4beCwv056ZbPQNoeHqM8= -github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE= -github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= +github.com/ava-labs/ledger-avalanche/go v0.0.0-20241009183145-e6f90a8a1a60 h1:EL66gtXOAwR/4KYBjOV03LTWgkEXvLePribLlJNu4g0= +github.com/ava-labs/ledger-avalanche/go v0.0.0-20241009183145-e6f90a8a1a60/go.mod h1:/7qKobTfbzBu7eSTVaXMTr56yTYk4j2Px6/8G+idxHo= +github.com/ava-labs/libevm v1.13.14-0.3.0.rc.1 h1:vBMYo+Iazw0rGTr+cwjkBdh5eadLPlv4ywI4lKye3CA= +github.com/ava-labs/libevm v1.13.14-0.3.0.rc.1/go.mod h1:+Iol+sVQ1KyoBsHf3veyrBmHCXr3xXRWq6ZXkgVfNLU= +github.com/ava-labs/subnet-evm v0.7.5 h1:2lEGhTLR4nirTD5031dIJUJVC1FfCSF6ihz22TbJDug= +github.com/ava-labs/subnet-evm v0.7.5/go.mod h1:fR6+mKlylACo0O47kY4VptfEeLYVcshWgVtiiR/0kOE= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= -github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= github.com/btcsuite/btcd v0.23.0 h1:V2/ZgjfDFIygAX3ZapeigkVBoVUtOJKSwrhZdlpSvaA= @@ -90,9 +56,8 @@ github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUB github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -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/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= @@ -145,15 +110,13 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= -github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= +github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= -github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI= -github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= +github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= +github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cubist-labs/cubesigner-go-sdk v0.0.16 h1:X7kPk4BhVggw/XtKXPJcT1kgnV0v70nIdqn+wAnJgqU= -github.com/cubist-labs/cubesigner-go-sdk v0.0.16/go.mod h1:aSYPLNkIt10+QQiNI4ctE+wQ1IoJdUqlUlLCmwYXk2w= 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= @@ -177,8 +140,8 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= -github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= -github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= +github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -212,6 +175,8 @@ github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiU github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk= +github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= @@ -271,13 +236,15 @@ github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36j github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4= github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= @@ -305,7 +272,6 @@ github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlT github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8= github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE= @@ -338,8 +304,8 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.8/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.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= @@ -378,8 +344,6 @@ github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OS github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= -github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= 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= @@ -396,9 +360,12 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa 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.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.38.0 h1:c/WX+w8SLAinvuKKQFh77WEucCnPk4j2OTUr7lt7BeY= +github.com/onsi/gomega v1.38.0/go.mod h1:OcXcwId0b9QsE7Y49u+BTrL4IdKOBOKnD6VQNTJEB6o= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8= @@ -410,13 +377,13 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= -github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= +github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= -github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= +github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= +github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -434,6 +401,8 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k= +github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk= github.com/sanity-io/litter v1.5.1 h1:dwnrSypP6q56o3lFxTU+t2fwQ9A+U5qrXVO4Qg9KwVU= github.com/sanity-io/litter v1.5.1/go.mod h1:5Z71SvaYy5kcGtyglXOC9rrUi3c1E8CamFWjQsazTh0= github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= @@ -443,15 +412,22 @@ github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMT github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= +github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= -github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE= +github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= +github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= +github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= +github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -462,8 +438,10 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/supranational/blst v0.3.14 h1:xNMoHRJOTwMn63ip6qoWJ2Ymgvj7E2b9jY2FAwY+qRo= github.com/supranational/blst v0.3.14/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= @@ -512,36 +490,34 @@ github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= github.com/zondax/ledger-go v1.0.1 h1:Ks/2tz/dOF+dbRynfZ0dEhcdL1lqw43Sa0zMXHpQ3aQ= github.com/zondax/ledger-go v1.0.1/go.mod h1:j7IgMY39f30apthJYMd1YsHZRqdyu4KbVmUp0nU78X0= -go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= -go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= -go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 h1:EtFWSnwW9hGObjkIdmlnWSydO+Qs8OwzfzXLUPg4xOc= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0/go.mod h1:QjUEoiGCPkvFZ/MjK6ZZfNOS6mfVEVKYE99dFhuN2LI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0= -go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= -go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= -go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= -go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= -go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= -go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= -go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= -go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= -go.opentelemetry.io/proto/otlp v1.8.0 h1:fRAZQDcAFHySxpJ1TwlA1cJ4tvcrw7nXl9xWWC8N5CE= -go.opentelemetry.io/proto/otlp v1.8.0/go.mod h1:tIeYOeNBU4cvmPqpaji1P+KbB4Oloai8wN4rWzRrFF0= +go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= +go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= +go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= +go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= +go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= +go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os= +go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= -go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= +go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= +go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= -go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= golang.org/x/crypto v0.0.0-20170613210332-850760c427c5/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -552,11 +528,11 @@ golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= -golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20251002181428-27f1f14c8bb9 h1:TQwNpfvNkxAVlItJf6Cr5JTsVZoC/Sj7K3OZv2Pc14A= -golang.org/x/exp v0.0.0-20251002181428-27f1f14c8bb9/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk= +golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= +golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -565,8 +541,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= -golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= +golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= +golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -590,8 +566,8 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= -golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -600,8 +576,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -637,20 +613,20 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= -golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= -golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= +golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= +golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= -golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= @@ -682,18 +658,18 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 h1:d8Nakh1G+ur7+P3GcMjpRDEkoLUcLW2iU92XVqR+XMQ= -google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090/go.mod h1:U8EXRNSd8sUYyDfs/It7KVWodQr+Hf9xtxyxWudSwEw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 h1:/OQuEa4YWtDt7uQWHd3q3sUMb+QOLQUg1xa8CEsRv5w= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= +google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:FiusG7LWj+4byqhbvmB+Q93B/mOxJLN2DTozDuZm4EU= +google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c h1:qXWI/sQtv5UKboZ/zUk7h+mrf/lXORyI+n9DKDAusdg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A= -google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c= +google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= +google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -705,8 +681,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= +google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/interchain/signature-aggregator.go b/interchain/signature-aggregator.go index 132b0d0..3ea2c86 100644 --- a/interchain/signature-aggregator.go +++ b/interchain/signature-aggregator.go @@ -13,6 +13,7 @@ import ( "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/vms/platformvm/warp" + "github.com/ava-labs/icm-services/signature-aggregator/api" "go.uber.org/zap" ) @@ -38,7 +39,7 @@ func SignMessage( } else if quorumPercentage > 100 { return nil, fmt.Errorf("quorum percentage cannot be greater than 100") } - request := AggregateSignatureRequest{ + request := api.AggregateSignatureRequest{ Message: message, SigningSubnetID: signingSubnetID, QuorumPercentage: quorumPercentage, @@ -117,7 +118,7 @@ func SignMessage( continue } - var response AggregateSignatureResponse + var response api.AggregateSignatureResponse if err := json.Unmarshal(body, &response); err != nil { lastErr = fmt.Errorf("failed to parse response: %w", err) logger.Error("Error parsing response", diff --git a/interchain/signature_aggregator_types.go b/interchain/signature_aggregator_types.go deleted file mode 100644 index 48923dd..0000000 --- a/interchain/signature_aggregator_types.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package interchain - -// AggregateSignatureRequest is the request structure for the signature aggregator API. -// This is a local copy to avoid dependency issues with icm-services. -type AggregateSignatureRequest struct { - Message string `json:"message"` - SigningSubnetID string `json:"signingSubnetID"` - QuorumPercentage uint64 `json:"quorumPercentage"` - Justification string `json:"justification"` -} - -// AggregateSignatureResponse is the response structure from the signature aggregator API. -// This is a local copy to avoid dependency issues with icm-services. -type AggregateSignatureResponse struct { - SignedMessage string `json:"signedMessage"` -} diff --git a/keychain/cubesigner/README.md b/keychain/cubesigner/README.md deleted file mode 100644 index 604ffa5..0000000 --- a/keychain/cubesigner/README.md +++ /dev/null @@ -1,284 +0,0 @@ -# CubeSigner Keychain - -This Go package provides a keychain implementation for Avalanche that integrates with [CubeSigner](https://www.cubist.dev/), a remote signing service for secure key management. - -CubeSigner enables secure transaction signing without exposing private keys locally, supporting both Avalanche native chains (P-Chain, X-Chain, C-Chain) and Ethereum-compatible chains. - -## Features - -* Remote transaction signing via CubeSigner API -* Support for P-Chain, X-Chain, and C-Chain operations -* Compatible with Avalanche's `keychain.Keychain` and `c.EthKeychain` interfaces -* Automatic key validation and format conversion -* Support for both hash signing and serialized transaction signing - -## Usage Example - -### Creating a Subnet - -```go -package main - -import ( - "context" - "fmt" - "log" - "os" - - "github.com/ava-labs/avalanche-tooling-sdk-go/keychain/cubesigner" - "github.com/ava-labs/avalanchego/vms/secp256k1fx" - "github.com/ava-labs/avalanchego/wallet/subnet/primary" - "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" - "github.com/cubist-labs/cubesigner-go-sdk/client" - "github.com/cubist-labs/cubesigner-go-sdk/session" -) - -func main() { - // Get key ID from environment - keyID := os.Getenv("CUBESIGNER_KEY_ID") - if keyID == "" { - log.Fatal("CUBESIGNER_KEY_ID environment variable is required") - } - - // Initialize CubeSigner client - sessionFile := "session.json" - manager, err := session.NewJsonSessionManager(&sessionFile) - if err != nil { - log.Fatalf("Failed to create session manager: %v", err) - } - - apiClient, err := client.NewApiClient(manager) - if err != nil { - log.Fatalf("Failed to create API client: %v", err) - } - - // Create CubeSigner keychain - kc, err := cubesigner.NewKeychain(apiClient, []string{keyID}) - if err != nil { - log.Fatalf("Failed to create keychain: %v", err) - } - - // Create primary wallet on Fuji testnet - ctx := context.Background() - wallet, err := primary.MakeWallet( - ctx, - primary.FujiAPIURI, - kc, - kc, - primary.WalletConfig{}, - ) - if err != nil { - log.Fatalf("Failed to create wallet: %v", err) - } - - // Create subnet with threshold of 1 - subnetOwnerAddrs := kc.Addresses().List() - owner := &secp256k1fx.OutputOwners{ - Threshold: 1, - Addrs: subnetOwnerAddrs, - } - - // Issue create subnet transaction - createSubnetTx, err := wallet.P().IssueCreateSubnetTx( - owner, - common.WithContext(ctx), - ) - if err != nil { - log.Fatalf("Failed to create subnet: %v", err) - } - - fmt.Printf("Subnet created with ID: %s\n", createSubnetTx.ID()) -} -``` - -### Cross-Chain Transfer (P-Chain to C-Chain) - -```go -package main - -import ( - "context" - "fmt" - "log" - "os" - "time" - - "github.com/ava-labs/avalanche-tooling-sdk-go/keychain/cubesigner" - "github.com/ava-labs/avalanchego/utils/constants" - "github.com/ava-labs/avalanchego/utils/units" - "github.com/ava-labs/avalanchego/vms/components/avax" - "github.com/ava-labs/avalanchego/vms/secp256k1fx" - "github.com/ava-labs/avalanchego/wallet/subnet/primary" - "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" - "github.com/cubist-labs/cubesigner-go-sdk/client" - "github.com/cubist-labs/cubesigner-go-sdk/session" -) - -func main() { - // Get key IDs from environment - pChainKeyID := os.Getenv("PCHAIN_KEY_ID") - cChainKeyID := os.Getenv("CCHAIN_KEY_ID") - if pChainKeyID == "" || cChainKeyID == "" { - log.Fatal("PCHAIN_KEY_ID and CCHAIN_KEY_ID environment variables are required") - } - - // Initialize CubeSigner client - sessionFile := "session.json" - manager, err := session.NewJsonSessionManager(&sessionFile) - if err != nil { - log.Fatalf("Failed to create session manager: %v", err) - } - - apiClient, err := client.NewApiClient(manager) - if err != nil { - log.Fatalf("Failed to create API client: %v", err) - } - - // Create keychains - kcPChain, err := cubesigner.NewKeychain(apiClient, []string{pChainKeyID}) - if err != nil { - log.Fatalf("Failed to create P-chain keychain: %v", err) - } - - kcCChain, err := cubesigner.NewKeychain(apiClient, []string{cChainKeyID}) - if err != nil { - log.Fatalf("Failed to create C-chain keychain: %v", err) - } - - // Create wallet for P-chain operations - ctx := context.Background() - walletPChain, err := primary.MakeWallet( - ctx, - primary.FujiAPIURI, - kcPChain, - kcPChain, - primary.WalletConfig{}, - ) - if err != nil { - log.Fatalf("Failed to create P-chain wallet: %v", err) - } - - // Get chain and asset IDs - cChainID := walletPChain.C().Builder().Context().BlockchainID - avaxAssetID := walletPChain.P().Builder().Context().AVAXAssetID - - // Export 0.5 AVAX from P-chain to C-chain - exportOwner := &secp256k1fx.OutputOwners{ - Threshold: 1, - Addrs: kcCChain.Addresses().List(), - } - - exportTx, err := walletPChain.P().IssueExportTx( - cChainID, - []*avax.TransferableOutput{{ - Asset: avax.Asset{ID: avaxAssetID}, - Out: &secp256k1fx.TransferOutput{ - Amt: units.Avax / 2, // 0.5 AVAX - OutputOwners: *exportOwner, - }, - }}, - common.WithContext(ctx), - ) - if err != nil { - log.Fatalf("Failed to export from P-chain: %v", err) - } - - fmt.Printf("Exported from P-chain, tx ID: %s\n", exportTx.ID()) - - // Wait for export to be processed - time.Sleep(10 * time.Second) - - // Create wallet for C-chain operations - walletCChain, err := primary.MakeWallet( - ctx, - primary.FujiAPIURI, - kcCChain, - kcCChain, - primary.WalletConfig{}, - ) - if err != nil { - log.Fatalf("Failed to create C-chain wallet: %v", err) - } - - // Import on C-chain - cChainAddr := kcCChain.EthAddresses().List()[0] - importTx, err := walletCChain.C().IssueImportTx( - constants.PlatformChainID, - cChainAddr, - common.WithContext(ctx), - ) - if err != nil { - log.Fatalf("Failed to import on C-chain: %v", err) - } - - fmt.Printf("Imported on C-chain, tx ID: %s\n", importTx.ID()) -} -``` - -## Key Features - -### Supported Key Types - -The keychain supports CubeSigner keys with the following types: -- `SecpAvaAddr` - Avalanche mainnet secp256k1 keys -- `SecpAvaTestAddr` - Avalanche testnet secp256k1 keys -- `SecpEthAddr` - Ethereum secp256k1 keys - -### Signing Methods - -#### 1. Serialized Transaction Signing (Recommended) - -Used automatically by the Avalanche wallet for P-Chain and X-Chain transactions. Requires: -- `ChainAlias` option: "P", "X", or "C" -- `NetworkID` option: Network ID - -```go -import "github.com/ava-labs/avalanchego/utils/crypto/keychain" - -// Example with signing options -signer, _ := kc.Get(address) -signature, err := signer.Sign( - unsignedTxBytes, - keychain.WithChainAlias("P"), - keychain.WithNetworkID(constants.FujiID), -) -``` - -#### 2. Hash Signing - -Used for signing arbitrary data hashed using Keccak-256: - -```go -signer, _ := kc.Get(address) -signature, err := signer.SignHash(hashBytes) -``` - -## Package Structure - -``` -keychain/cubesigner/ -├── cubesigner.go # CubeSigner client interface definition -├── cubesigner_keychain.go # Keychain and signer implementations -├── cubesigner_keychain_test.go # Tests for keychain implementation -├── mocks_generate_test.go # Mock generation directives -├── cubesignermock/ -│ └── cubesigner_client.go # Generated mock for CubeSignerClient interface -└── README.md # This file -``` - -**Core Files:** -- **cubesigner_keychain.go**: Implements `keychain.Keychain` and `c.EthKeychain` interfaces, handles key validation, format conversion, and transaction signing via CubeSigner API - -## Integration with Avalanche Wallet - -The CubeSigner keychain implements both `keychain.Keychain` and `c.EthKeychain` interfaces, making it compatible with the Avalanche primary wallet: - -```go -wallet, err := primary.MakeWallet( - ctx, - endpoint, - cubesignerKeychain, // For P/X chain operations - cubesignerKeychain, // For C chain operations - primary.WalletConfig{}, -) -``` diff --git a/keychain/cubesigner/cubesigner_client.go b/keychain/cubesigner/cubesigner_client.go deleted file mode 100644 index b40e9e9..0000000 --- a/keychain/cubesigner/cubesigner_client.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package cubesigner - -import ( - "github.com/cubist-labs/cubesigner-go-sdk/client" - "github.com/cubist-labs/cubesigner-go-sdk/models" -) - -// CubeSignerClient defines the interface for CubeSigner client operations -// needed by the keychain implementation -type CubeSignerClient interface { - // GetKeyInOrg retrieves key information for the given keyID from the CubeSigner organization. - // It returns the key metadata, including public key and key type. - GetKeyInOrg(keyID string) (*models.KeyInfo, error) - - // BlobSign signs arbitrary data using the specified keyID. - // request contains the data to be signed. - BlobSign(keyID string, request models.BlobSignRequest, receipts ...*client.MfaReceipt) (*client.CubeSignerResponse[models.SignResponse], error) - - // AvaSerializedTxSign signs Avalanche transactions using the specified chainAlias (P/X/C), and materialID (address). - // request contains the serialized transaction data to be signed. - AvaSerializedTxSign(chainAlias, materialID string, request models.AvaSerializedTxSignRequest, receipts ...*client.MfaReceipt) (*client.CubeSignerResponse[models.SignResponse], error) -} diff --git a/keychain/cubesigner/cubesigner_keychain.go b/keychain/cubesigner/cubesigner_keychain.go deleted file mode 100644 index b2eaf8c..0000000 --- a/keychain/cubesigner/cubesigner_keychain.go +++ /dev/null @@ -1,272 +0,0 @@ -// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package cubesigner - -import ( - "encoding/base64" - "encoding/hex" - "errors" - "fmt" - "strings" - - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/utils/constants" - "github.com/ava-labs/avalanchego/utils/crypto/keychain" - "github.com/ava-labs/avalanchego/utils/formatting/address" - "github.com/ava-labs/avalanchego/utils/set" - "github.com/ava-labs/avalanchego/wallet/chain/c" - "github.com/ava-labs/libevm/common" - "github.com/cubist-labs/cubesigner-go-sdk/client" - "github.com/cubist-labs/cubesigner-go-sdk/models" - "golang.org/x/exp/maps" - - avasecp256k1 "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" - secp256k1 "github.com/decred/dcrd/dcrec/secp256k1/v4" -) - -var ( - _ keychain.Keychain = (*Keychain)(nil) - _ c.EthKeychain = (*Keychain)(nil) - _ keychain.Signer = (*cubesignerSigner)(nil) - _ CubeSignerClient = (*client.ApiClient)(nil) - - ErrNoKeysProvided = errors.New("you need to provide at least one key to create a server keychain") - ErrEmptySignatureFromServer = errors.New("empty signature obtained from server") - ErrChainAliasMissing = errors.New("chainAlias must be specified in options for CubeSigner") - ErrInvalidChainAlias = errors.New("chainAlias must be 'P', 'X' or 'C' for CubeSigner") - ErrNetworkIDMissing = errors.New("network ID must be specified in options for CubeSigner for P/X chain") - ErrUnsupportedKeyType = errors.New("unsupported key type") - ErrInvalidPublicKey = errors.New("invalid public key format") -) - -const ( - // UncompressedPublicKeyLength is the expected length of an uncompressed secp256k1 public key in bytes. - // This includes the prefix byte (1 byte) plus the X and Y coordinates (32 bytes each). - UncompressedPublicKeyLength = 65 - // UncompressedPublicKeyPrefix is the prefix byte for uncompressed secp256k1 public keys. - // This byte indicates that the key is in uncompressed format. - UncompressedPublicKeyPrefix = 0x04 -) - -// keyInfo holds both the public key and keyID for a CubeSigner key. -type keyInfo struct { - pubKey *avasecp256k1.PublicKey // The Avalanche public key derived from CubeSigner - keyID string // The CubeSigner key identifier -} - -// Keychain provides an abstraction over CubeSigner remote signing capabilities. -type Keychain struct { - cubesignerClient CubeSignerClient // Client for CubeSigner API operations - avaAddrToKeyInfo map[ids.ShortID]*keyInfo // Maps Avalanche addresses to key info - ethAddrToKeyInfo map[common.Address]*keyInfo // Maps Ethereum addresses to key info -} - -// processKey obtains and processes key information from CubeSigner. -// It validates that the key exists in the CubeSigner organization, verifies -// that the key type is supported (secp256k1 for Avalanche/Ethereum), and -// converts the public key from hex format to an Avalanche public key. -func processKey( - cubesignerClient CubeSignerClient, - keyID string, -) (*avasecp256k1.PublicKey, error) { - // Validate key exists - keyInfo, err := cubesignerClient.GetKeyInOrg(keyID) - if err != nil { - return nil, fmt.Errorf("could not find server key %s: %w", keyID, err) - } - - // Validate key type - switch keyInfo.KeyType { - case models.SecpAvaAddr, models.SecpAvaTestAddr, models.SecpEthAddr: - // Supported key types - default: - return nil, fmt.Errorf("keytype %s of server key %s: %w", keyInfo.KeyType, keyID, ErrUnsupportedKeyType) - } - - // get public key - pubKeyHex := strings.TrimPrefix(keyInfo.PublicKey, "0x") - pubKeyBytes, err := hex.DecodeString(pubKeyHex) - if err != nil { - return nil, fmt.Errorf("%w: failed to decode public key for server key %s: %w", ErrInvalidPublicKey, keyID, err) - } - if len(pubKeyBytes) != UncompressedPublicKeyLength { - return nil, fmt.Errorf("invalid public key length for server key %s: expected %d bytes, got %d", keyID, UncompressedPublicKeyLength, len(pubKeyBytes)) - } - if pubKeyBytes[0] != UncompressedPublicKeyPrefix { - return nil, fmt.Errorf("invalid public key format for server key %s: expected uncompressed format (0x%02x prefix), got 0x%02x", keyID, UncompressedPublicKeyPrefix, pubKeyBytes[0]) - } - pubKey, err := secp256k1.ParsePubKey(pubKeyBytes) - if err != nil { - return nil, fmt.Errorf("invalid public key format for server key %s: %w", keyID, err) - } - avaPubKey, err := avasecp256k1.ToPublicKey(pubKey.SerializeCompressed()) - if err != nil { - return nil, fmt.Errorf("invalid public key format for server key %s: %w", keyID, err) - } - - return avaPubKey, nil -} - -// NewKeychain creates a new keychain abstraction over a CubeSigner connection. -// It validates that all provided keyIDs exist in the CubeSigner organization and returns -// a keychain that can be used to sign transactions using those keys. -func NewKeychain( - cubesignerClient CubeSignerClient, - keyIDs []string, -) (*Keychain, error) { - if len(keyIDs) == 0 { - return nil, ErrNoKeysProvided - } - - avaAddrToKeyInfo := map[ids.ShortID]*keyInfo{} - ethAddrToKeyInfo := map[common.Address]*keyInfo{} - - for _, keyID := range keyIDs { - avaPubKey, err := processKey(cubesignerClient, keyID) - if err != nil { - return nil, err - } - - keyInf := &keyInfo{ - pubKey: avaPubKey, - keyID: keyID, - } - - avaAddrToKeyInfo[avaPubKey.Address()] = keyInf - ethAddrToKeyInfo[avaPubKey.EthAddress()] = keyInf - } - - return &Keychain{ - cubesignerClient: cubesignerClient, - avaAddrToKeyInfo: avaAddrToKeyInfo, - ethAddrToKeyInfo: ethAddrToKeyInfo, - }, nil -} - -// Addresses returns the set of Avalanche addresses that this keychain can sign for. -func (kc *Keychain) Addresses() set.Set[ids.ShortID] { - return set.Of(maps.Keys(kc.avaAddrToKeyInfo)...) -} - -// Get returns a signer for the given Avalanche address, if it exists in this keychain. -func (kc *Keychain) Get(addr ids.ShortID) (keychain.Signer, bool) { - keyInf, found := kc.avaAddrToKeyInfo[addr] - if !found { - return nil, false - } - return &cubesignerSigner{ - cubesignerClient: kc.cubesignerClient, - pubKey: keyInf.pubKey, - keyID: keyInf.keyID, - }, true -} - -// EthAddresses returns the set of Ethereum addresses that this keychain can sign for. -func (kc *Keychain) EthAddresses() set.Set[common.Address] { - return set.Of(maps.Keys(kc.ethAddrToKeyInfo)...) -} - -// GetEth returns a signer for the given Ethereum address, if it exists in this keychain. -func (kc *Keychain) GetEth(addr common.Address) (keychain.Signer, bool) { - keyInf, found := kc.ethAddrToKeyInfo[addr] - if !found { - return nil, false - } - return &cubesignerSigner{ - cubesignerClient: kc.cubesignerClient, - pubKey: keyInf.pubKey, - keyID: keyInf.keyID, - }, true -} - -// cubesignerAvagoSigner is an abstraction of the underlying cubesigner connection, -// to be able sign for a specific address -type cubesignerSigner struct { - cubesignerClient CubeSignerClient - pubKey *avasecp256k1.PublicKey - keyID string -} - -// processSignatureResponse is a helper function that processes the common response -// pattern from CubeSigner signing operations. It decodes the hex signature and validates its length. -func processSignatureResponse(signatureHex string) ([]byte, error) { - signatureBytes, err := hex.DecodeString(strings.TrimPrefix(signatureHex, "0x")) - if err != nil { - return nil, fmt.Errorf("failed to decode server's signature: %w", err) - } - if len(signatureBytes) != avasecp256k1.SignatureLen { - return nil, fmt.Errorf("invalid server's signature length: expected %d bytes, got %d", avasecp256k1.SignatureLen, len(signatureBytes)) - } - return signatureBytes, nil -} - -// SignHash signs the given hash using CubeSigner's BlobSign API. -// It expects to receive a hash of the unsigned transaction bytes. -func (s *cubesignerSigner) SignHash(b []byte) ([]byte, error) { - response, err := s.cubesignerClient.BlobSign( - s.keyID, - models.BlobSignRequest{ - MessageBase64: base64.StdEncoding.EncodeToString(b), - }, - ) - if err != nil { - return nil, fmt.Errorf("server signing err: %w", err) - } - if response.ResponseData == nil { - return nil, ErrEmptySignatureFromServer - } - return processSignatureResponse(response.ResponseData.Signature) -} - -// Sign signs the given payload according to the given signing options. -// It expects to receive the unsigned transaction bytes and requires ChainAlias and NetworkID -// to be specified in the signing options for CubeSigner's AvaSerializedTxSign API. -func (s *cubesignerSigner) Sign(b []byte, opts ...keychain.SigningOption) ([]byte, error) { - options := &keychain.SigningOptions{} - for _, opt := range opts { - opt(options) - } - // Require chainAlias and network from options - if options.ChainAlias == "" { - return nil, ErrChainAliasMissing - } - if options.ChainAlias != "P" && options.ChainAlias != "X" && options.ChainAlias != "C" { - return nil, fmt.Errorf("%w, got %q", ErrInvalidChainAlias, options.ChainAlias) - } - if options.ChainAlias != "C" && options.NetworkID == 0 { - return nil, ErrNetworkIDMissing - } - var materialID string - if options.ChainAlias == "C" { - materialID = s.pubKey.EthAddress().Hex() - } else { - hrp := constants.GetHRP(options.NetworkID) - addr := s.pubKey.Address() - var err error - materialID, err = address.FormatBech32(hrp, addr.Bytes()) - if err != nil { - return nil, fmt.Errorf("failed to format %s address %v as Bech32: %w", hrp, addr, err) - } - } - - response, err := s.cubesignerClient.AvaSerializedTxSign( - options.ChainAlias, - materialID, - models.AvaSerializedTxSignRequest{ - Tx: "0x" + hex.EncodeToString(b), - }, - ) - if err != nil { - return nil, fmt.Errorf("server signing err: %w", err) - } - if response.ResponseData == nil { - return nil, ErrEmptySignatureFromServer - } - return processSignatureResponse(response.ResponseData.Signature) -} - -// Address returns the Avalanche address associated with this signer. -func (s *cubesignerSigner) Address() ids.ShortID { - return s.pubKey.Address() -} diff --git a/keychain/cubesigner/cubesigner_keychain_test.go b/keychain/cubesigner/cubesigner_keychain_test.go deleted file mode 100644 index 98679ea..0000000 --- a/keychain/cubesigner/cubesigner_keychain_test.go +++ /dev/null @@ -1,334 +0,0 @@ -// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package cubesigner - -import ( - "errors" - "testing" - - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/utils/crypto/keychain" - "github.com/ava-labs/libevm/common" - "github.com/cubist-labs/cubesigner-go-sdk/client" - "github.com/cubist-labs/cubesigner-go-sdk/models" - "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" - - "github.com/ava-labs/avalanche-tooling-sdk-go/keychain/cubesigner/cubesignermock" -) - -var errTest = errors.New("test") - -const testKeyID = "test-key-id" - -func TestNewKeychain(t *testing.T) { - require := require.New(t) - ctrl := gomock.NewController(t) - - // user provides no keys - mockClient := cubesignermock.NewCubeSignerClient(ctrl) - _, err := NewKeychain(mockClient, []string{}) - require.ErrorIs(err, ErrNoKeysProvided) - - // client returns error when getting key info - mockClient = cubesignermock.NewCubeSignerClient(ctrl) - mockClient.EXPECT().GetKeyInOrg(testKeyID).Return(nil, errTest).Times(1) - _, err = NewKeychain(mockClient, []string{testKeyID}) - require.ErrorIs(err, errTest) - - // client returns unsupported key type - mockClient = cubesignermock.NewCubeSignerClient(ctrl) - keyInfo := &models.KeyInfo{ - KeyType: "UnsupportedType", - PublicKey: "0x04" + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", - } - mockClient.EXPECT().GetKeyInOrg(testKeyID).Return(keyInfo, nil).Times(1) - _, err = NewKeychain(mockClient, []string{testKeyID}) - require.ErrorIs(err, ErrUnsupportedKeyType) - - // client returns invalid public key format - mockClient = cubesignermock.NewCubeSignerClient(ctrl) - keyInfo = &models.KeyInfo{ - KeyType: models.SecpAvaAddr, - PublicKey: "invalid-hex", - } - mockClient.EXPECT().GetKeyInOrg(testKeyID).Return(keyInfo, nil).Times(1) - _, err = NewKeychain(mockClient, []string{testKeyID}) - require.ErrorIs(err, ErrInvalidPublicKey) - - // good path - Avalanche address - mockClient = cubesignermock.NewCubeSignerClient(ctrl) - keyInfo = &models.KeyInfo{ - KeyType: models.SecpAvaAddr, - PublicKey: "0x04" + "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", - } - mockClient.EXPECT().GetKeyInOrg(testKeyID).Return(keyInfo, nil).Times(1) - kc, err := NewKeychain(mockClient, []string{testKeyID}) - require.NoError(err) - require.NotNil(kc) - - // good path - Ethereum address - mockClient = cubesignermock.NewCubeSignerClient(ctrl) - keyInfo = &models.KeyInfo{ - KeyType: models.SecpEthAddr, - PublicKey: "0x04" + "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", - } - mockClient.EXPECT().GetKeyInOrg(testKeyID).Return(keyInfo, nil).Times(1) - kc, err = NewKeychain(mockClient, []string{testKeyID}) - require.NoError(err) - require.NotNil(kc) -} - -func TestKeychain_Addresses(t *testing.T) { - require := require.New(t) - ctrl := gomock.NewController(t) - - mockClient := cubesignermock.NewCubeSignerClient(ctrl) - - keyInfo := &models.KeyInfo{ - KeyType: models.SecpAvaAddr, - PublicKey: "0x04" + "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", - } - mockClient.EXPECT().GetKeyInOrg(testKeyID).Return(keyInfo, nil).Times(1) - - kc, err := NewKeychain(mockClient, []string{testKeyID}) - require.NoError(err) - - addresses := kc.Addresses() - require.Len(addresses, 1) -} - -func TestKeychain_Get(t *testing.T) { - require := require.New(t) - ctrl := gomock.NewController(t) - - mockClient := cubesignermock.NewCubeSignerClient(ctrl) - - keyInfo := &models.KeyInfo{ - KeyType: models.SecpAvaAddr, - PublicKey: "0x04" + "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", - } - mockClient.EXPECT().GetKeyInOrg(testKeyID).Return(keyInfo, nil).Times(1) - - kc, err := NewKeychain(mockClient, []string{testKeyID}) - require.NoError(err) - - addresses := kc.Addresses() - require.Len(addresses, 1) - - addr := addresses.List()[0] - signer, found := kc.Get(addr) - require.True(found) - require.NotNil(signer) - require.Equal(addr, signer.Address()) - - // test with non-existent address - randomAddr := ids.GenerateTestShortID() - signer, found = kc.Get(randomAddr) - require.False(found) - require.Nil(signer) -} - -func TestKeychain_EthAddresses(t *testing.T) { - require := require.New(t) - ctrl := gomock.NewController(t) - - mockClient := cubesignermock.NewCubeSignerClient(ctrl) - - keyInfo := &models.KeyInfo{ - KeyType: models.SecpEthAddr, - PublicKey: "0x04" + "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", - } - mockClient.EXPECT().GetKeyInOrg(testKeyID).Return(keyInfo, nil).Times(1) - - kc, err := NewKeychain(mockClient, []string{testKeyID}) - require.NoError(err) - - ethAddresses := kc.EthAddresses() - require.Len(ethAddresses, 1) -} - -func TestKeychain_GetEth(t *testing.T) { - require := require.New(t) - ctrl := gomock.NewController(t) - - mockClient := cubesignermock.NewCubeSignerClient(ctrl) - - keyInfo := &models.KeyInfo{ - KeyType: models.SecpEthAddr, - PublicKey: "0x04" + "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", - } - mockClient.EXPECT().GetKeyInOrg(testKeyID).Return(keyInfo, nil).Times(1) - - kc, err := NewKeychain(mockClient, []string{testKeyID}) - require.NoError(err) - - ethAddresses := kc.EthAddresses() - require.Len(ethAddresses, 1) - - ethAddr := ethAddresses.List()[0] - signer, found := kc.GetEth(ethAddr) - require.True(found) - require.NotNil(signer) - - // Test non-existent address - nonExistentAddr := common.HexToAddress("0x0000000000000000000000000000000000000000") - signer, found = kc.GetEth(nonExistentAddr) - require.False(found) - require.Nil(signer) -} - -func TestCubesignerSigner_SignHash(t *testing.T) { - require := require.New(t) - ctrl := gomock.NewController(t) - - mockClient := cubesignermock.NewCubeSignerClient(ctrl) - - keyInfo := &models.KeyInfo{ - KeyType: models.SecpAvaAddr, - PublicKey: "0x04" + "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", - } - mockClient.EXPECT().GetKeyInOrg(testKeyID).Return(keyInfo, nil).Times(1) - - kc, err := NewKeychain(mockClient, []string{testKeyID}) - require.NoError(err) - - addresses := kc.Addresses() - addr := addresses.List()[0] - signer, found := kc.Get(addr) - require.True(found) - - hash := []byte("test-hash") - - // test successful signing - response := &client.CubeSignerResponse[models.SignResponse]{ - ResponseData: &models.SignResponse{ - Signature: "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef01", - }, - } - mockClient.EXPECT().BlobSign(testKeyID, gomock.Any()).Return(response, nil).Times(1) - - signature, err := signer.SignHash(hash) - require.NoError(err) - require.NotNil(signature) - - // test client error - mockClient.EXPECT().BlobSign(testKeyID, gomock.Any()).Return(nil, errTest).Times(1) - _, err = signer.SignHash(hash) - require.ErrorIs(err, errTest) - - // test empty response - emptyResponse := &client.CubeSignerResponse[models.SignResponse]{ - ResponseData: nil, - } - mockClient.EXPECT().BlobSign(testKeyID, gomock.Any()).Return(emptyResponse, nil).Times(1) - _, err = signer.SignHash(hash) - require.ErrorIs(err, ErrEmptySignatureFromServer) -} - -func TestCubesignerSigner_Sign(t *testing.T) { - require := require.New(t) - ctrl := gomock.NewController(t) - - mockClient := cubesignermock.NewCubeSignerClient(ctrl) - - keyInfo := &models.KeyInfo{ - KeyType: models.SecpAvaAddr, - PublicKey: "0x04" + "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", - } - mockClient.EXPECT().GetKeyInOrg(testKeyID).Return(keyInfo, nil).Times(1) - - kc, err := NewKeychain(mockClient, []string{testKeyID}) - require.NoError(err) - - addresses := kc.Addresses() - addr := addresses.List()[0] - signer, found := kc.Get(addr) - require.True(found) - - txBytes := []byte("test-transaction-bytes") - - // Test missing chain alias - _, err = signer.Sign(txBytes) - require.ErrorIs(err, ErrChainAliasMissing) - - // Test invalid chain alias - _, err = signer.Sign(txBytes, keychain.WithChainAlias("invalid")) - require.ErrorIs(err, ErrInvalidChainAlias) - - // Test missing network ID - _, err = signer.Sign(txBytes, keychain.WithChainAlias("P")) - require.ErrorIs(err, ErrNetworkIDMissing) - - // Test successful P-chain signing - response := &client.CubeSignerResponse[models.SignResponse]{ - ResponseData: &models.SignResponse{ - Signature: "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef01", - }, - } - mockClient.EXPECT().AvaSerializedTxSign( - "P", - gomock.Any(), // materialID (Bech32 address) - gomock.Any(), // request with hex-encoded tx - ).Return(response, nil).Times(1) - - signature, err := signer.Sign(txBytes, - keychain.WithChainAlias("P"), - keychain.WithNetworkID(1)) - require.NoError(err) - require.NotNil(signature) - - // Test successful X-chain signing - mockClient.EXPECT().AvaSerializedTxSign( - "X", - gomock.Any(), // materialID (Bech32 address) - gomock.Any(), // request with hex-encoded tx - ).Return(response, nil).Times(1) - - signature, err = signer.Sign(txBytes, - keychain.WithChainAlias("X"), - keychain.WithNetworkID(1)) - require.NoError(err) - require.NotNil(signature) - - // Test successful C-chain signing - mockClient.EXPECT().AvaSerializedTxSign( - "C", - gomock.Any(), // materialID (Eth address) - gomock.Any(), // request with hex-encoded tx - ).Return(response, nil).Times(1) - - signature, err = signer.Sign(txBytes, - keychain.WithChainAlias("C"), - keychain.WithNetworkID(1)) - require.NoError(err) - require.NotNil(signature) - - // Test client error - mockClient.EXPECT().AvaSerializedTxSign( - gomock.Any(), - gomock.Any(), - gomock.Any(), - ).Return(nil, errTest).Times(1) - - _, err = signer.Sign(txBytes, - keychain.WithChainAlias("P"), - keychain.WithNetworkID(1)) - require.ErrorIs(err, errTest) - - // Test empty response - emptyResponse := &client.CubeSignerResponse[models.SignResponse]{ - ResponseData: nil, - } - mockClient.EXPECT().AvaSerializedTxSign( - gomock.Any(), - gomock.Any(), - gomock.Any(), - ).Return(emptyResponse, nil).Times(1) - - _, err = signer.Sign(txBytes, - keychain.WithChainAlias("P"), - keychain.WithNetworkID(1)) - require.ErrorIs(err, ErrEmptySignatureFromServer) -} diff --git a/keychain/cubesigner/cubesignermock/cubesigner_client.go b/keychain/cubesigner/cubesignermock/cubesigner_client.go deleted file mode 100644 index f1b15c9..0000000 --- a/keychain/cubesigner/cubesignermock/cubesigner_client.go +++ /dev/null @@ -1,97 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/ava-labs/avalanche-tooling-sdk-go/keychain/cubesigner (interfaces: CubeSignerClient) -// -// Generated by this command: -// -// mockgen -package=cubesignermock -destination=cubesignermock/cubesigner_client.go -mock_names=CubeSignerClient=CubeSignerClient . CubeSignerClient -// - -// Package cubesignermock is a generated GoMock package. -package cubesignermock - -import ( - reflect "reflect" - - client "github.com/cubist-labs/cubesigner-go-sdk/client" - models "github.com/cubist-labs/cubesigner-go-sdk/models" - gomock "go.uber.org/mock/gomock" -) - -// CubeSignerClient is a mock of CubeSignerClient interface. -type CubeSignerClient struct { - ctrl *gomock.Controller - recorder *CubeSignerClientMockRecorder - isgomock struct{} -} - -// CubeSignerClientMockRecorder is the mock recorder for CubeSignerClient. -type CubeSignerClientMockRecorder struct { - mock *CubeSignerClient -} - -// NewCubeSignerClient creates a new mock instance. -func NewCubeSignerClient(ctrl *gomock.Controller) *CubeSignerClient { - mock := &CubeSignerClient{ctrl: ctrl} - mock.recorder = &CubeSignerClientMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *CubeSignerClient) EXPECT() *CubeSignerClientMockRecorder { - return m.recorder -} - -// AvaSerializedTxSign mocks base method. -func (m *CubeSignerClient) AvaSerializedTxSign(chainAlias, materialID string, request models.AvaSerializedTxSignRequest, receipts ...*client.MfaReceipt) (*client.CubeSignerResponse[models.SignResponse], error) { - m.ctrl.T.Helper() - varargs := []any{chainAlias, materialID, request} - for _, a := range receipts { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "AvaSerializedTxSign", varargs...) - ret0, _ := ret[0].(*client.CubeSignerResponse[models.SignResponse]) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// AvaSerializedTxSign indicates an expected call of AvaSerializedTxSign. -func (mr *CubeSignerClientMockRecorder) AvaSerializedTxSign(chainAlias, materialID, request any, receipts ...any) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]any{chainAlias, materialID, request}, receipts...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AvaSerializedTxSign", reflect.TypeOf((*CubeSignerClient)(nil).AvaSerializedTxSign), varargs...) -} - -// BlobSign mocks base method. -func (m *CubeSignerClient) BlobSign(keyID string, request models.BlobSignRequest, receipts ...*client.MfaReceipt) (*client.CubeSignerResponse[models.SignResponse], error) { - m.ctrl.T.Helper() - varargs := []any{keyID, request} - for _, a := range receipts { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "BlobSign", varargs...) - ret0, _ := ret[0].(*client.CubeSignerResponse[models.SignResponse]) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// BlobSign indicates an expected call of BlobSign. -func (mr *CubeSignerClientMockRecorder) BlobSign(keyID, request any, receipts ...any) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]any{keyID, request}, receipts...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BlobSign", reflect.TypeOf((*CubeSignerClient)(nil).BlobSign), varargs...) -} - -// GetKeyInOrg mocks base method. -func (m *CubeSignerClient) GetKeyInOrg(keyID string) (*models.KeyInfo, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetKeyInOrg", keyID) - ret0, _ := ret[0].(*models.KeyInfo) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetKeyInOrg indicates an expected call of GetKeyInOrg. -func (mr *CubeSignerClientMockRecorder) GetKeyInOrg(keyID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetKeyInOrg", reflect.TypeOf((*CubeSignerClient)(nil).GetKeyInOrg), keyID) -} diff --git a/keychain/cubesigner/mocks_generate_test.go b/keychain/cubesigner/mocks_generate_test.go deleted file mode 100644 index dbe08ac..0000000 --- a/keychain/cubesigner/mocks_generate_test.go +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package cubesigner - -//go:generate go run go.uber.org/mock/mockgen -package=${GOPACKAGE}mock -destination=${GOPACKAGE}mock/cubesigner_client.go -mock_names=CubeSignerClient=CubeSignerClient . CubeSignerClient diff --git a/keychain/ledger/ledger_keychain.go b/keychain/ledger/ledger_keychain.go index 380d268..902af4e 100644 --- a/keychain/ledger/ledger_keychain.go +++ b/keychain/ledger/ledger_keychain.go @@ -17,6 +17,9 @@ import ( "github.com/ava-labs/avalanchego/wallet/chain/c" "github.com/ava-labs/libevm/common" "golang.org/x/exp/maps" + + "github.com/ava-labs/avalanche-tooling-sdk-go/constants" + "github.com/ava-labs/avalanche-tooling-sdk-go/utils" ) var ( @@ -162,14 +165,9 @@ func (l *ledgerSigner) SignHash(b []byte) ([]byte, error) { } // expects to receive the unsigned tx bytes -func (l *ledgerSigner) Sign(b []byte, opts ...keychain.SigningOption) ([]byte, error) { - options := &keychain.SigningOptions{} - for _, opt := range opts { - opt(options) - } - - // For P-Chain transactions that require hash signing on the Ledger device - if options.ChainAlias == "P" { +func (l *ledgerSigner) Sign(b []byte) ([]byte, error) { + // Auto-detect chain and check if P-Chain transaction requires hash signing + if utils.AutoDetectChain(b) == constants.PChainAlias { useSignHash, err := shouldUseSignHash(b) if err != nil { return nil, err diff --git a/keychain/ledger/ledger_keychain_test.go b/keychain/ledger/ledger_keychain_test.go index 9638003..33adf24 100644 --- a/keychain/ledger/ledger_keychain_test.go +++ b/keychain/ledger/ledger_keychain_test.go @@ -9,7 +9,6 @@ import ( "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/utils/crypto/keychain" "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" "github.com/ava-labs/avalanchego/utils/hashing" "github.com/ava-labs/avalanchego/vms/components/avax" @@ -742,7 +741,7 @@ func TestLedgerSigner_Sign_WithPChainTransferSubnetOwnership(t *testing.T) { s, b := kc.Get(addr) require.True(b) - signature, err := s.Sign(unsignedBytes, keychain.WithChainAlias("P")) + signature, err := s.Sign(unsignedBytes) require.NoError(err) require.Equal(expectedSignature, signature) } @@ -781,7 +780,7 @@ func TestLedgerSigner_Sign_WithPChainNonTransferSubnetOwnership(t *testing.T) { s, b := kc.Get(addr) require.True(b) - signature, err := s.Sign(unsignedBytes, keychain.WithChainAlias("P")) + signature, err := s.Sign(unsignedBytes) require.NoError(err) require.Equal(expectedSignature, signature) } @@ -835,7 +834,7 @@ func TestLedgerSigner_Sign_WithNonPChain(t *testing.T) { s, b := kc.Get(addr) require.True(b) - signature, err := s.Sign(toSign, keychain.WithChainAlias("X")) + signature, err := s.Sign(toSign) require.NoError(err) require.Equal(expectedSignature, signature) } diff --git a/utils/txs.go b/utils/txs.go new file mode 100644 index 0000000..a5976c1 --- /dev/null +++ b/utils/txs.go @@ -0,0 +1,52 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package utils + +import ( + "github.com/ava-labs/avalanchego/wallet/chain/x/builder" + "github.com/ava-labs/coreth/plugin/evm/atomic" + + "github.com/ava-labs/avalanche-tooling-sdk-go/constants" + + avmtxs "github.com/ava-labs/avalanchego/vms/avm/txs" + platformvmtxs "github.com/ava-labs/avalanchego/vms/platformvm/txs" +) + +// AutoDetectChain attempts to determine which chain a transaction belongs to by analyzing its bytes. +// It returns UndefinedAlias if it cannot determine the chain or if multiple chains are possible (codec collision). +func AutoDetectChain(txBytes []byte) constants.ChainAlias { + matchCount := 0 + detectedChain := constants.UndefinedAlias + + // Try to unmarshal as P-chain transaction + var unsignedPTx platformvmtxs.UnsignedTx + _, err := platformvmtxs.Codec.Unmarshal(txBytes, &unsignedPTx) + if err == nil { + matchCount++ + detectedChain = constants.PChainAlias + } + + // Try to unmarshal as X-chain transaction + var unsignedXTx avmtxs.UnsignedTx + _, err = builder.Parser.Codec().Unmarshal(txBytes, &unsignedXTx) + if err == nil { + matchCount++ + detectedChain = constants.XChainAlias + } + + // Try to unmarshal as C-chain transaction + var unsignedCTx atomic.UnsignedAtomicTx + _, err = atomic.Codec.Unmarshal(txBytes, &unsignedCTx) + if err == nil { + matchCount++ + detectedChain = constants.CChainAlias + } + + // If more than one chain successfully unmarshaled the bytes, we have a codec collision + if matchCount > 1 { + return constants.UndefinedAlias + } + + return detectedChain +} diff --git a/utils/txs_test.go b/utils/txs_test.go new file mode 100644 index 0000000..05baed4 --- /dev/null +++ b/utils/txs_test.go @@ -0,0 +1,709 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package utils + +import ( + "testing" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/crypto/bls/signer/localsigner" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/components/verify" + "github.com/ava-labs/avalanchego/vms/platformvm/signer" + "github.com/ava-labs/avalanchego/vms/platformvm/warp" + "github.com/ava-labs/avalanchego/vms/platformvm/warp/message" + "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/avalanchego/wallet/chain/x/builder" + "github.com/ava-labs/coreth/plugin/evm/atomic" + "github.com/ava-labs/libevm/common" + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanche-tooling-sdk-go/constants" + + avmtxs "github.com/ava-labs/avalanchego/vms/avm/txs" + platformvmtxs "github.com/ava-labs/avalanchego/vms/platformvm/txs" +) + +func TestAutoDetectChainPChainTxs(t *testing.T) { + require := require.New(t) + + addr := ids.ShortID{ + 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, + 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, + 0x44, 0x55, 0x66, 0x77, + } + + txID := ids.ID{ + 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, + 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, + 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, + 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, + } + + avaxAssetID, err := ids.FromString("FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z") + require.NoError(err) + + baseTxData := platformvmtxs.BaseTx{ + BaseTx: avax.BaseTx{ + NetworkID: 1, + BlockchainID: ids.GenerateTestID(), + Ins: []*avax.TransferableInput{ + { + UTXOID: avax.UTXOID{ + TxID: txID, + OutputIndex: 1, + }, + Asset: avax.Asset{ + ID: avaxAssetID, + }, + In: &secp256k1fx.TransferInput{ + Amt: 1000000, + Input: secp256k1fx.Input{ + SigIndices: []uint32{0}, + }, + }, + }, + }, + Outs: []*avax.TransferableOutput{}, + }, + } + + // Generate BLS key for ConvertSubnetToL1Tx + sk, err := localsigner.New() + require.NoError(err) + + pop, err := signer.NewProofOfPossession(sk) + require.NoError(err) + + validators := []*platformvmtxs.ConvertSubnetToL1Validator{ + { + NodeID: ids.GenerateTestNodeID().Bytes(), + Weight: 1000, + Balance: 100000, + Signer: *pop, + RemainingBalanceOwner: message.PChainOwner{ + Threshold: 1, + Addresses: []ids.ShortID{addr}, + }, + DeactivationOwner: message.PChainOwner{ + Threshold: 1, + Addresses: []ids.ShortID{addr}, + }, + }, + } + + // Create warp message for RegisterL1ValidatorTx test + subnetID := ids.GenerateTestID() + nodeID := ids.GenerateTestNodeID() + blsPublicKey := [48]byte{} + expiry := uint64(1234567890) + weight := uint64(1000) + + balanceOwners := message.PChainOwner{ + Threshold: 1, + Addresses: []ids.ShortID{addr}, + } + disableOwners := message.PChainOwner{ + Threshold: 1, + Addresses: []ids.ShortID{addr}, + } + + addressedCallPayload, err := message.NewRegisterL1Validator( + subnetID, + nodeID, + blsPublicKey, + expiry, + balanceOwners, + disableOwners, + weight, + ) + require.NoError(err) + + managerAddress := ids.ShortID{} + addressedCall, err := payload.NewAddressedCall( + managerAddress[:], + addressedCallPayload.Bytes(), + ) + require.NoError(err) + + unsignedWarpMessage, err := warp.NewUnsignedMessage( + 1, + ids.GenerateTestID(), + addressedCall.Bytes(), + ) + require.NoError(err) + + emptySignature := &warp.BitSetSignature{ + Signers: []byte{}, + Signature: [96]byte{}, + } + + signedWarpMessage, err := warp.NewMessage( + unsignedWarpMessage, + emptySignature, + ) + require.NoError(err) + + testCases := []struct { + name string + tx platformvmtxs.UnsignedTx + expectedChain constants.ChainAlias + }{ + { + name: "BaseTx should be detected as P-Chain", + tx: &platformvmtxs.BaseTx{BaseTx: baseTxData.BaseTx}, + expectedChain: constants.PChainAlias, + }, + { + name: "AdvanceTimeTx should be detected as P-Chain", + tx: &platformvmtxs.AdvanceTimeTx{ + Time: 1234567890, + }, + expectedChain: constants.PChainAlias, + }, + { + name: "RewardValidatorTx should be detected as P-Chain", + tx: &platformvmtxs.RewardValidatorTx{ + TxID: txID, + }, + expectedChain: constants.PChainAlias, + }, + { + name: "AddValidatorTx should be detected as P-Chain", + tx: &platformvmtxs.AddValidatorTx{ + BaseTx: baseTxData, + StakeOuts: []*avax.TransferableOutput{}, + RewardsOwner: &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{addr}, + }, + }, + expectedChain: constants.PChainAlias, + }, + { + name: "AddSubnetValidatorTx should be detected as P-Chain", + tx: &platformvmtxs.AddSubnetValidatorTx{ + BaseTx: baseTxData, + SubnetAuth: &secp256k1fx.Input{SigIndices: []uint32{0}}, + }, + expectedChain: constants.PChainAlias, + }, + { + name: "AddDelegatorTx should be detected as P-Chain", + tx: &platformvmtxs.AddDelegatorTx{ + BaseTx: baseTxData, + StakeOuts: []*avax.TransferableOutput{}, + DelegationRewardsOwner: &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{addr}, + }, + }, + expectedChain: constants.PChainAlias, + }, + { + name: "RemoveSubnetValidatorTx should be detected as P-Chain", + tx: &platformvmtxs.RemoveSubnetValidatorTx{ + BaseTx: baseTxData, + SubnetAuth: &secp256k1fx.Input{SigIndices: []uint32{0}}, + }, + expectedChain: constants.PChainAlias, + }, + { + name: "TransformSubnetTx should be detected as P-Chain", + tx: &platformvmtxs.TransformSubnetTx{ + BaseTx: baseTxData, + SubnetAuth: &secp256k1fx.Input{SigIndices: []uint32{0}}, + }, + expectedChain: constants.PChainAlias, + }, + { + name: "AddPermissionlessValidatorTx should be detected as P-Chain", + tx: &platformvmtxs.AddPermissionlessValidatorTx{ + BaseTx: baseTxData, + Signer: &signer.Empty{}, + StakeOuts: []*avax.TransferableOutput{}, + ValidatorRewardsOwner: &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{addr}, + }, + DelegatorRewardsOwner: &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{addr}, + }, + }, + expectedChain: constants.PChainAlias, + }, + { + name: "AddPermissionlessDelegatorTx should be detected as P-Chain", + tx: &platformvmtxs.AddPermissionlessDelegatorTx{ + BaseTx: baseTxData, + StakeOuts: []*avax.TransferableOutput{}, + DelegationRewardsOwner: &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{addr}, + }, + }, + expectedChain: constants.PChainAlias, + }, + { + name: "TransferSubnetOwnershipTx should be detected as P-Chain", + tx: &platformvmtxs.TransferSubnetOwnershipTx{ + BaseTx: baseTxData, + Subnet: ids.GenerateTestID(), + Owner: &secp256k1fx.OutputOwners{ + Locktime: 0, + Threshold: 1, + Addrs: []ids.ShortID{addr}, + }, + SubnetAuth: &secp256k1fx.Input{ + SigIndices: []uint32{0}, + }, + }, + expectedChain: constants.PChainAlias, + }, + { + name: "CreateSubnetTx should be detected as P-Chain", + tx: &platformvmtxs.CreateSubnetTx{ + BaseTx: baseTxData, + Owner: &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{addr}, + }, + }, + expectedChain: constants.PChainAlias, + }, + { + name: "CreateChainTx should be detected as P-Chain", + tx: &platformvmtxs.CreateChainTx{ + BaseTx: baseTxData, + SubnetID: ids.GenerateTestID(), + ChainName: "test-chain", + VMID: ids.GenerateTestID(), + SubnetAuth: &secp256k1fx.Input{ + SigIndices: []uint32{0}, + }, + }, + expectedChain: constants.PChainAlias, + }, + { + name: "ImportTx should be detected as P-Chain", + tx: &platformvmtxs.ImportTx{ + BaseTx: baseTxData, + SourceChain: ids.GenerateTestID(), + }, + expectedChain: constants.PChainAlias, + }, + { + name: "ExportTx should be detected as P-Chain", + tx: &platformvmtxs.ExportTx{ + BaseTx: baseTxData, + DestinationChain: ids.GenerateTestID(), + }, + expectedChain: constants.PChainAlias, + }, + { + name: "RegisterL1ValidatorTx should be detected as P-Chain", + tx: &platformvmtxs.RegisterL1ValidatorTx{ + BaseTx: baseTxData, + Balance: 1000, + }, + expectedChain: constants.PChainAlias, + }, + { + name: "SetL1ValidatorWeightTx should be detected as P-Chain", + tx: &platformvmtxs.SetL1ValidatorWeightTx{ + BaseTx: baseTxData, + }, + expectedChain: constants.PChainAlias, + }, + { + name: "IncreaseL1ValidatorBalanceTx should be detected as P-Chain", + tx: &platformvmtxs.IncreaseL1ValidatorBalanceTx{ + BaseTx: baseTxData, + ValidationID: ids.GenerateTestID(), + }, + expectedChain: constants.PChainAlias, + }, + { + name: "DisableL1ValidatorTx should be detected as P-Chain", + tx: &platformvmtxs.DisableL1ValidatorTx{ + BaseTx: baseTxData, + ValidationID: ids.GenerateTestID(), + DisableAuth: &secp256k1fx.Input{ + SigIndices: []uint32{0}, + }, + }, + expectedChain: constants.PChainAlias, + }, + { + name: "ConvertSubnetToL1Tx should be detected as P-Chain", + tx: &platformvmtxs.ConvertSubnetToL1Tx{ + BaseTx: baseTxData, + Subnet: ids.GenerateTestID(), + ChainID: ids.GenerateTestID(), + Address: []byte{1, 2, 3, 4}, + Validators: validators, + SubnetAuth: &secp256k1fx.Input{SigIndices: []uint32{0}}, + }, + expectedChain: constants.PChainAlias, + }, + { + name: "RegisterL1ValidatorTx with warp message should be detected as P-Chain", + tx: &platformvmtxs.RegisterL1ValidatorTx{ + BaseTx: baseTxData, + Balance: 100000, + Message: signedWarpMessage.Bytes(), + }, + expectedChain: constants.PChainAlias, + }, + { + name: "Invalid bytes should return UndefinedAlias", + tx: nil, + expectedChain: constants.UndefinedAlias, + }, + { + name: "Empty bytes should return UndefinedAlias", + tx: nil, + expectedChain: constants.UndefinedAlias, + }, + } + + for i, tc := range testCases { + t.Run(tc.name, func(*testing.T) { + var unsignedBytes []byte + var err error + + // Handle special cases for invalid/empty bytes tests + if tc.tx == nil { + if i == len(testCases)-2 { + // Invalid bytes test + unsignedBytes = []byte{0xFF, 0xFF, 0xFF} + } else { + // Empty bytes test + unsignedBytes = []byte{} + } + } else { + unsignedBytes, err = platformvmtxs.Codec.Marshal(platformvmtxs.CodecVersion, &tc.tx) + require.NoError(err) + } + + result := AutoDetectChain(unsignedBytes) + require.Equal(tc.expectedChain, result) + }) + } +} + +func TestAutoDetectChainXChainTxs(t *testing.T) { + require := require.New(t) + + addr := ids.ShortID{ + 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, + 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, + 0x44, 0x55, 0x66, 0x77, + } + + txID := ids.ID{ + 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, + 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, + 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, + 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, + } + + avaxAssetID, err := ids.FromString("FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z") + require.NoError(err) + + baseTxData := avax.BaseTx{ + NetworkID: 1, + BlockchainID: ids.GenerateTestID(), + Ins: []*avax.TransferableInput{ + { + UTXOID: avax.UTXOID{ + TxID: txID, + OutputIndex: 1, + }, + Asset: avax.Asset{ + ID: avaxAssetID, + }, + In: &secp256k1fx.TransferInput{ + Amt: 1000000, + Input: secp256k1fx.Input{ + SigIndices: []uint32{0}, + }, + }, + }, + }, + Outs: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: avaxAssetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 500000, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{addr}, + }, + }, + }, + }, + } + + testCases := []struct { + name string + buildTx func() (avmtxs.UnsignedTx, error) + expectedChain constants.ChainAlias + }{ + { + name: "BaseTx should be detected as X-Chain", + buildTx: func() (avmtxs.UnsignedTx, error) { + return &avmtxs.BaseTx{ + BaseTx: baseTxData, + }, nil + }, + expectedChain: constants.XChainAlias, + }, + { + name: "CreateAssetTx should be detected as X-Chain", + buildTx: func() (avmtxs.UnsignedTx, error) { + transferOutput := &secp256k1fx.TransferOutput{ + Amt: 1000000, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{addr}, + }, + } + return &avmtxs.CreateAssetTx{ + BaseTx: avmtxs.BaseTx{BaseTx: baseTxData}, + Name: "Test Asset", + Symbol: "TST", + Denomination: 8, + States: []*avmtxs.InitialState{ + { + FxIndex: 0, + Outs: []verify.State{transferOutput}, + }, + }, + }, nil + }, + expectedChain: constants.XChainAlias, + }, + { + name: "OperationTx should be detected as X-Chain", + buildTx: func() (avmtxs.UnsignedTx, error) { + return &avmtxs.OperationTx{ + BaseTx: avmtxs.BaseTx{BaseTx: baseTxData}, + Ops: []*avmtxs.Operation{ + { + Asset: avax.Asset{ + ID: avaxAssetID, + }, + UTXOIDs: []*avax.UTXOID{ + { + TxID: txID, + OutputIndex: 0, + }, + }, + Op: &secp256k1fx.MintOperation{ + MintInput: secp256k1fx.Input{ + SigIndices: []uint32{0}, + }, + MintOutput: secp256k1fx.MintOutput{ + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{addr}, + }, + }, + TransferOutput: secp256k1fx.TransferOutput{ + Amt: 100000, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{addr}, + }, + }, + }, + }, + }, + }, nil + }, + expectedChain: constants.XChainAlias, + }, + { + name: "ImportTx should be detected as X-Chain", + buildTx: func() (avmtxs.UnsignedTx, error) { + return &avmtxs.ImportTx{ + BaseTx: avmtxs.BaseTx{BaseTx: baseTxData}, + SourceChain: ids.GenerateTestID(), + ImportedIns: []*avax.TransferableInput{ + { + UTXOID: avax.UTXOID{ + TxID: txID, + OutputIndex: 2, + }, + Asset: avax.Asset{ + ID: avaxAssetID, + }, + In: &secp256k1fx.TransferInput{ + Amt: 2000000, + Input: secp256k1fx.Input{ + SigIndices: []uint32{0}, + }, + }, + }, + }, + }, nil + }, + expectedChain: constants.XChainAlias, + }, + { + name: "ExportTx should be detected as X-Chain", + buildTx: func() (avmtxs.UnsignedTx, error) { + return &avmtxs.ExportTx{ + BaseTx: avmtxs.BaseTx{BaseTx: baseTxData}, + DestinationChain: ids.GenerateTestID(), + ExportedOuts: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: avaxAssetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 300000, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{addr}, + }, + }, + }, + }, + }, nil + }, + expectedChain: constants.XChainAlias, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(*testing.T) { + unsignedTx, err := tc.buildTx() + require.NoError(err) + + // Marshal the unsigned transaction to bytes + unsignedBytes, err := builder.Parser.Codec().Marshal(avmtxs.CodecVersion, &unsignedTx) + require.NoError(err) + + result := AutoDetectChain(unsignedBytes) + require.Equal(tc.expectedChain, result) + }) + } +} + +func TestAutoDetectChainCChainTxs(t *testing.T) { + require := require.New(t) + + addr := ids.ShortID{ + 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, + 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, + 0x44, 0x55, 0x66, 0x77, + } + + txID := ids.ID{ + 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, + 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, + 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, + 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, + } + + avaxAssetID, err := ids.FromString("FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z") + require.NoError(err) + + ethAddr := common.HexToAddress("0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC") + + testCases := []struct { + name string + buildTx func() (atomic.UnsignedAtomicTx, error) + expectedChain constants.ChainAlias + }{ + { + name: "ImportTx should be detected as C-Chain", + buildTx: func() (atomic.UnsignedAtomicTx, error) { + return &atomic.UnsignedImportTx{ + NetworkID: 1, + BlockchainID: ids.GenerateTestID(), + SourceChain: ids.GenerateTestID(), + ImportedInputs: []*avax.TransferableInput{ + { + UTXOID: avax.UTXOID{ + TxID: txID, + OutputIndex: 0, + }, + Asset: avax.Asset{ + ID: avaxAssetID, + }, + In: &secp256k1fx.TransferInput{ + Amt: 1000000, + Input: secp256k1fx.Input{ + SigIndices: []uint32{0}, + }, + }, + }, + }, + Outs: []atomic.EVMOutput{ + { + Address: ethAddr, + Amount: 500000, + AssetID: avaxAssetID, + }, + }, + }, nil + }, + expectedChain: constants.CChainAlias, + }, + { + name: "ExportTx should be detected as C-Chain", + buildTx: func() (atomic.UnsignedAtomicTx, error) { + return &atomic.UnsignedExportTx{ + NetworkID: 1, + BlockchainID: ids.GenerateTestID(), + DestinationChain: ids.GenerateTestID(), + Ins: []atomic.EVMInput{ + { + Address: ethAddr, + Amount: 1000000, + AssetID: avaxAssetID, + Nonce: 0, + }, + }, + ExportedOutputs: []*avax.TransferableOutput{ + { + Asset: avax.Asset{ + ID: avaxAssetID, + }, + Out: &secp256k1fx.TransferOutput{ + Amt: 500000, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{addr}, + }, + }, + }, + }, + }, nil + }, + expectedChain: constants.CChainAlias, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(*testing.T) { + unsignedTx, err := tc.buildTx() + require.NoError(err) + + // Marshal the unsigned transaction to bytes + unsignedBytes, err := atomic.Codec.Marshal(atomic.CodecVersion, &unsignedTx) + require.NoError(err) + + result := AutoDetectChain(unsignedBytes) + require.Equal(tc.expectedChain, result) + }) + } +}