From 7b7cda516b3c2666cd0d8d274af30dd36b0aaea7 Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Mon, 16 Mar 2026 18:23:58 +0100 Subject: [PATCH 1/7] Start ksm --- go.mod | 15 ++++ go.sum | 30 +++++++ pkg/cmd/init.go | 37 ++++++-- pkg/cmd/run_node.go | 67 ++++++++------ pkg/config/config.go | 12 ++- pkg/config/config_test.go | 4 +- pkg/config/defaults.go | 2 + pkg/signer/aws/signer.go | 163 ++++++++++++++++++++++++++++++++++ pkg/signer/aws/signer_test.go | 156 ++++++++++++++++++++++++++++++++ 9 files changed, 450 insertions(+), 36 deletions(-) create mode 100644 pkg/signer/aws/signer.go create mode 100644 pkg/signer/aws/signer_test.go diff --git a/go.mod b/go.mod index 6c35bfe87b..bc25b70e03 100644 --- a/go.mod +++ b/go.mod @@ -45,6 +45,21 @@ require ( require ( github.com/armon/go-metrics v0.4.1 // indirect + github.com/aws/aws-sdk-go-v2 v1.41.4 // indirect + github.com/aws/aws-sdk-go-v2/config v1.32.12 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.19.12 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 // indirect + github.com/aws/aws-sdk-go-v2/service/kms v1.50.3 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 // indirect + github.com/aws/smithy-go v1.24.2 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/boltdb/bolt v1.3.1 // indirect diff --git a/go.sum b/go.sum index a9b47945c0..433d75d0f4 100644 --- a/go.sum +++ b/go.sum @@ -17,6 +17,36 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k= +github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= +github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0= +github.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g= +github.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8= +github.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 h1:CNXO7mvgThFGqOFgbNAP2nol2qAWBOGfqR/7tQlvLmc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 h1:tN6W/hg+pkM+tf9XDkWUbDEjGLb+raoBMFsTodcoYKw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 h1:2HvVAIq+YqgGotK6EkMf+KIEqTISmTYh5zLpYyeTo1Y= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20/go.mod h1:V4X406Y666khGa8ghKmphma/7C0DAtEQYhkq9z4vpbk= +github.com/aws/aws-sdk-go-v2/service/kms v1.50.3 h1:s/zDSG/a/Su9aX+v0Ld9cimUCdkr5FWPmBV8owaEbZY= +github.com/aws/aws-sdk-go-v2/service/kms v1.50.3/go.mod h1:/iSgiUor15ZuxFGQSTf3lA2FmKxFsQoc2tADOarQBSw= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 h1:0GFOLzEbOyZABS3PhYfBIx2rNBACYcKty+XGkTgw1ow= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.8/go.mod h1:LXypKvk85AROkKhOG6/YEcHFPoX+prKTowKnVdcaIxE= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 h1:kiIDLZ005EcKomYYITtfsjn7dtOwHDOFy7IbPXKek2o= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.13/go.mod h1:2h/xGEowcW/g38g06g3KpRWDlT+OTfxxI0o1KqayAB8= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB8KfgAEuG0dc08Bkda7NU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk= +github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= +github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= diff --git a/pkg/cmd/init.go b/pkg/cmd/init.go index f6c9a075f4..79201a63b2 100644 --- a/pkg/cmd/init.go +++ b/pkg/cmd/init.go @@ -1,6 +1,7 @@ package cmd import ( + "context" "fmt" "os" "path/filepath" @@ -8,12 +9,18 @@ import ( rollconf "github.com/evstack/ev-node/pkg/config" "github.com/evstack/ev-node/pkg/hash" "github.com/evstack/ev-node/pkg/p2p/key" + awssigner "github.com/evstack/ev-node/pkg/signer/aws" "github.com/evstack/ev-node/pkg/signer/file" ) // CreateSigner sets up the signer configuration and creates necessary files func CreateSigner(config *rollconf.Config, homePath string, passphrase string) ([]byte, error) { - if config.Signer.SignerType == "file" && config.Node.Aggregator { + if !config.Node.Aggregator { + return nil, nil + } + + switch config.Signer.SignerType { + case "file": if passphrase == "" { return nil, fmt.Errorf("passphrase is required when using local file signer") } @@ -42,11 +49,31 @@ func CreateSigner(config *rollconf.Config, homePath string, passphrase string) ( proposerAddress := hash.SumTruncated(bz) return proposerAddress, nil - } else if config.Signer.SignerType != "file" && config.Node.Aggregator { - return nil, fmt.Errorf("remote signer not implemented for aggregator nodes, use local signer instead") - } - return nil, nil + case "awskms": + // For KMS, the key is pre-provisioned in AWS. We fetch the public key + // to derive the proposer address. + signer, err := awssigner.NewKmsSigner(context.Background(), config.Signer.KmsRegion, config.Signer.KmsKeyID) + if err != nil { + return nil, fmt.Errorf("failed to initialize AWS KMS signer: %w", err) + } + + pubKey, err := signer.GetPublic() + if err != nil { + return nil, fmt.Errorf("failed to get public key from KMS: %w", err) + } + + bz, err := pubKey.Raw() + if err != nil { + return nil, fmt.Errorf("failed to get public key raw bytes: %w", err) + } + + proposerAddress := hash.SumTruncated(bz) + return proposerAddress, nil + + default: + return nil, fmt.Errorf("unknown signer type: %s", config.Signer.SignerType) + } } // LoadOrGenNodeKey creates the node key file if it doesn't exist. diff --git a/pkg/cmd/run_node.go b/pkg/cmd/run_node.go index 4a5c7577c6..52ffbdb546 100644 --- a/pkg/cmd/run_node.go +++ b/pkg/cmd/run_node.go @@ -26,6 +26,7 @@ import ( "github.com/evstack/ev-node/pkg/p2p" "github.com/evstack/ev-node/pkg/p2p/key" "github.com/evstack/ev-node/pkg/signer" + awssigner "github.com/evstack/ev-node/pkg/signer/aws" "github.com/evstack/ev-node/pkg/signer/file" "github.com/evstack/ev-node/pkg/telemetry" ) @@ -110,40 +111,50 @@ func StartNode( // Validate and load signer first (before attempting DA connection, which may fail // eagerly over WebSocket if no DA server is running). var signer signer.Signer - if nodeConfig.Signer.SignerType == "file" && (nodeConfig.Node.Aggregator && !nodeConfig.Node.BasedSequencer) { - // Get passphrase file path - passphraseFile, err := cmd.Flags().GetString(rollconf.FlagSignerPassphraseFile) - if err != nil { - return fmt.Errorf("failed to get '%s' flag: %w", rollconf.FlagSignerPassphraseFile, err) - } + if nodeConfig.Node.Aggregator && !nodeConfig.Node.BasedSequencer { + switch nodeConfig.Signer.SignerType { + case "file": + // Get passphrase file path + passphraseFile, err := cmd.Flags().GetString(rollconf.FlagSignerPassphraseFile) + if err != nil { + return fmt.Errorf("failed to get '%s' flag: %w", rollconf.FlagSignerPassphraseFile, err) + } - if passphraseFile == "" { - return fmt.Errorf("passphrase file must be provided via --evnode.signer.passphrase_file") - } + if passphraseFile == "" { + return fmt.Errorf("passphrase file must be provided via --evnode.signer.passphrase_file") + } - // Read passphrase from file - passphraseBytes, err := os.ReadFile(passphraseFile) - if err != nil { - return fmt.Errorf("failed to read passphrase from file '%s': %w", passphraseFile, err) - } - passphrase := strings.TrimSpace(string(passphraseBytes)) + // Read passphrase from file + passphraseBytes, err := os.ReadFile(passphraseFile) + if err != nil { + return fmt.Errorf("failed to read passphrase from file '%s': %w", passphraseFile, err) + } + passphrase := strings.TrimSpace(string(passphraseBytes)) - if passphrase == "" { - return fmt.Errorf("passphrase file '%s' is empty", passphraseFile) - } + if passphrase == "" { + return fmt.Errorf("passphrase file '%s' is empty", passphraseFile) + } - // Resolve signer path; allow absolute, relative to node root, or relative to CWD if resolution fails - signerPath, err := filepath.Abs(strings.TrimSuffix(nodeConfig.Signer.SignerPath, "signer.json")) - if err != nil { - return err - } + // Resolve signer path; allow absolute, relative to node root, or relative to CWD if resolution fails + signerPath, err := filepath.Abs(strings.TrimSuffix(nodeConfig.Signer.SignerPath, "signer.json")) + if err != nil { + return err + } - signer, err = file.LoadFileSystemSigner(signerPath, []byte(passphrase)) - if err != nil { - return err + signer, err = file.LoadFileSystemSigner(signerPath, []byte(passphrase)) + if err != nil { + return err + } + case "awskms": + logger.Info().Msg("initializing AWS KMS signer") + var err error + signer, err = awssigner.NewKmsSigner(ctx, nodeConfig.Signer.KmsRegion, nodeConfig.Signer.KmsKeyID) + if err != nil { + return fmt.Errorf("failed to initialize AWS KMS signer: %w", err) + } + default: + return fmt.Errorf("unknown signer type: %s", nodeConfig.Signer.SignerType) } - } else if nodeConfig.Node.Aggregator && nodeConfig.Signer.SignerType != "file" { - return fmt.Errorf("unknown signer type: %s", nodeConfig.Signer.SignerType) } blobClient, err := blobrpc.NewWSClient(ctx, nodeConfig.DA.Address, nodeConfig.DA.AuthToken, "") diff --git a/pkg/config/config.go b/pkg/config/config.go index 2ae3f0ff2d..867a51dbdf 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -140,6 +140,10 @@ const ( FlagSignerType = FlagPrefixEvnode + "signer.signer_type" // FlagSignerPath is a flag for specifying the signer path FlagSignerPath = FlagPrefixEvnode + "signer.signer_path" + // FlagSignerKmsKeyID is a flag for specifying the KMS key ID + FlagSignerKmsKeyID = FlagPrefixEvnode + "signer.kms_key_id" + // FlagSignerKmsRegion is a flag for specifying the KMS region + FlagSignerKmsRegion = FlagPrefixEvnode + "signer.kms_region" // FlagSignerPassphraseFile is a flag for specifying the file containing the signer passphrase FlagSignerPassphraseFile = FlagPrefixEvnode + "signer.passphrase_file" @@ -292,8 +296,10 @@ type P2PConfig struct { // SignerConfig contains all signer configuration parameters type SignerConfig struct { - SignerType string `mapstructure:"signer_type" yaml:"signer_type" comment:"Type of remote signer to use (file, grpc)"` + SignerType string `mapstructure:"signer_type" yaml:"signer_type" comment:"Type of remote signer to use (file, grpc, awskms)"` SignerPath string `mapstructure:"signer_path" yaml:"signer_path" comment:"Path to the signer file or address"` + KmsKeyID string `mapstructure:"kms_key_id" yaml:"kms_key_id" comment:"AWS KMS Key ID or ARN for awskms signer"` + KmsRegion string `mapstructure:"kms_region" yaml:"kms_region" comment:"AWS Region for awskms signer"` } // RPCConfig contains all RPC server configuration parameters @@ -548,8 +554,10 @@ func AddFlags(cmd *cobra.Command) { cmd.Flags().Float64(FlagTracingSampleRate, instrDef.TracingSampleRate, "trace sampling rate (0.0-1.0)") // Signer configuration flags - cmd.Flags().String(FlagSignerType, def.Signer.SignerType, "type of signer to use (file, grpc)") + cmd.Flags().String(FlagSignerType, def.Signer.SignerType, "type of signer to use (file, grpc, awskms)") cmd.Flags().String(FlagSignerPath, def.Signer.SignerPath, "path to the signer file or address") + cmd.Flags().String(FlagSignerKmsKeyID, def.Signer.KmsKeyID, "AWS KMS Key ID or ARN for awskms signer") + cmd.Flags().String(FlagSignerKmsRegion, def.Signer.KmsRegion, "AWS Region for awskms signer") cmd.Flags().String(FlagSignerPassphraseFile, "", "path to file containing the signer passphrase (required for file signer and if aggregator is enabled)") cmd.MarkFlagsMutuallyExclusive(FlagLight, FlagAggregator) diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index ae6be38118..b78cab8ad9 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -107,6 +107,8 @@ func TestAddFlags(t *testing.T) { assertFlagValue(t, flags, FlagSignerPassphraseFile, "") assertFlagValue(t, flags, FlagSignerType, "file") assertFlagValue(t, flags, FlagSignerPath, DefaultConfig().Signer.SignerPath) + assertFlagValue(t, flags, FlagSignerKmsKeyID, DefaultConfig().Signer.KmsKeyID) + assertFlagValue(t, flags, FlagSignerKmsRegion, DefaultConfig().Signer.KmsRegion) // RPC flags assertFlagValue(t, flags, FlagRPCAddress, DefaultConfig().RPC.Address) @@ -118,7 +120,7 @@ func TestAddFlags(t *testing.T) { assertFlagValue(t, flags, FlagPruningInterval, DefaultConfig().Pruning.Interval.Duration) // Count the number of flags we're explicitly checking - expectedFlagCount := 67 // Update this number if you add more flag checks above + expectedFlagCount := 69 // Update this number if you add more flag checks above // Get the actual number of flags (both regular and persistent) actualFlagCount := 0 diff --git a/pkg/config/defaults.go b/pkg/config/defaults.go index 4b959a0f95..4009e5dc50 100644 --- a/pkg/config/defaults.go +++ b/pkg/config/defaults.go @@ -93,6 +93,8 @@ func DefaultConfig() Config { Signer: SignerConfig{ SignerType: "file", SignerPath: "config", + KmsKeyID: "", + KmsRegion: "", }, RPC: RPCConfig{ Address: "127.0.0.1:7331", diff --git a/pkg/signer/aws/signer.go b/pkg/signer/aws/signer.go new file mode 100644 index 0000000000..7342be9f83 --- /dev/null +++ b/pkg/signer/aws/signer.go @@ -0,0 +1,163 @@ +// Package aws implements a signer.Signer backed by AWS KMS. +// It delegates signing to a remote KMS key and caches the public key locally. +package aws + +import ( + "context" + "crypto/ed25519" + "crypto/sha256" + "crypto/x509" + "fmt" + "sync" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + awsconfig "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" + "github.com/aws/aws-sdk-go-v2/service/kms/types" + "github.com/libp2p/go-libp2p/core/crypto" + + "github.com/evstack/ev-node/pkg/signer" +) + +// KMSClient is the subset of the AWS KMS client API that KmsSigner needs. +// This allows mocking in tests. +type KMSClient interface { + Sign(ctx context.Context, params *kms.SignInput, optFns ...func(*kms.Options)) (*kms.SignOutput, error) + GetPublicKey(ctx context.Context, params *kms.GetPublicKeyInput, optFns ...func(*kms.Options)) (*kms.GetPublicKeyOutput, error) +} + +// KmsSigner implements the signer.Signer interface using AWS KMS. +type KmsSigner struct { + client KMSClient + keyID string + mu sync.RWMutex + publicKey crypto.PubKey + address []byte +} + +var _ signer.Signer = (*KmsSigner)(nil) + +// NewKmsSigner creates a new Signer backed by an AWS KMS Ed25519 key. +// It uses the standard AWS credential chain (env vars, ~/.aws/credentials, IAM roles, etc.). +func NewKmsSigner(ctx context.Context, region string, keyID string) (*KmsSigner, error) { + if keyID == "" { + return nil, fmt.Errorf("aws kms key ID is required") + } + + cfgOpts := []func(*awsconfig.LoadOptions) error{} + if region != "" { + cfgOpts = append(cfgOpts, awsconfig.WithRegion(region)) + } + + cfg, err := awsconfig.LoadDefaultConfig(ctx, cfgOpts...) + if err != nil { + return nil, fmt.Errorf("failed to load AWS config: %w", err) + } + + client := kms.NewFromConfig(cfg) + return NewKmsSignerFromClient(ctx, client, keyID) +} + +// NewKmsSignerFromClient creates a KmsSigner from an existing KMS client. +// Useful for testing with a mock client. +func NewKmsSignerFromClient(ctx context.Context, client KMSClient, keyID string) (*KmsSigner, error) { + if keyID == "" { + return nil, fmt.Errorf("aws kms key ID is required") + } + + s := &KmsSigner{ + client: client, + keyID: keyID, + } + + // Fetch and cache the public key eagerly so we fail fast on misconfiguration. + if err := s.fetchPublicKey(ctx); err != nil { + return nil, fmt.Errorf("failed to fetch public key from KMS: %w", err) + } + + return s, nil +} + +// fetchPublicKey retrieves the public key from KMS and caches it. +func (s *KmsSigner) fetchPublicKey(ctx context.Context) error { + out, err := s.client.GetPublicKey(ctx, &kms.GetPublicKeyInput{ + KeyId: aws.String(s.keyID), + }) + if err != nil { + return fmt.Errorf("KMS GetPublicKey failed: %w", err) + } + + // AWS returns the public key as a DER-encoded X.509 SubjectPublicKeyInfo. + pub, err := x509.ParsePKIXPublicKey(out.PublicKey) + if err != nil { + return fmt.Errorf("failed to parse KMS public key: %w", err) + } + + edPubKey, ok := pub.(ed25519.PublicKey) + if !ok { + return fmt.Errorf("unsupported key type from KMS: expected ed25519, got %T", pub) + } + + cryptoPubKey, err := crypto.UnmarshalEd25519PublicKey(edPubKey) + if err != nil { + return fmt.Errorf("failed to convert to libp2p pubkey: %w", err) + } + + bz, err := cryptoPubKey.Raw() + if err != nil { + return fmt.Errorf("failed to get raw pubkey bytes: %w", err) + } + + address := sha256.Sum256(bz) + + s.mu.Lock() + defer s.mu.Unlock() + + s.publicKey = cryptoPubKey + s.address = address[:] + + return nil +} + +// Sign signs a message using the remote KMS key. +func (s *KmsSigner) Sign(message []byte) ([]byte, error) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + out, err := s.client.Sign(ctx, &kms.SignInput{ + KeyId: aws.String(s.keyID), + Message: message, + MessageType: types.MessageTypeRaw, + SigningAlgorithm: types.SigningAlgorithmSpecEd25519Sha512, + }) + if err != nil { + return nil, fmt.Errorf("KMS Sign failed: %w", err) + } + + return out.Signature, nil +} + +// GetPublic returns the cached public key. +func (s *KmsSigner) GetPublic() (crypto.PubKey, error) { + s.mu.RLock() + defer s.mu.RUnlock() + + if s.publicKey == nil { + return nil, fmt.Errorf("public key not loaded") + } + + return s.publicKey, nil +} + +// GetAddress returns the cached address derived from the public key. +func (s *KmsSigner) GetAddress() ([]byte, error) { + s.mu.RLock() + defer s.mu.RUnlock() + + if s.address == nil { + return nil, fmt.Errorf("address not loaded") + } + + return s.address, nil +} diff --git a/pkg/signer/aws/signer_test.go b/pkg/signer/aws/signer_test.go new file mode 100644 index 0000000000..5ca4c0133c --- /dev/null +++ b/pkg/signer/aws/signer_test.go @@ -0,0 +1,156 @@ +package aws + +import ( + "context" + "crypto/ed25519" + "crypto/x509" + "fmt" + "testing" + + "github.com/aws/aws-sdk-go-v2/service/kms" + "github.com/aws/aws-sdk-go-v2/service/kms/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// mockKMSClient is a test double implementing KMSClient. +type mockKMSClient struct { + pubKeyDER []byte + signFn func(ctx context.Context, params *kms.SignInput) (*kms.SignOutput, error) + getPubFn func(ctx context.Context, params *kms.GetPublicKeyInput) (*kms.GetPublicKeyOutput, error) +} + +func (m *mockKMSClient) Sign(ctx context.Context, params *kms.SignInput, _ ...func(*kms.Options)) (*kms.SignOutput, error) { + if m.signFn != nil { + return m.signFn(ctx, params) + } + return &kms.SignOutput{Signature: []byte("mock-signature")}, nil +} + +func (m *mockKMSClient) GetPublicKey(ctx context.Context, params *kms.GetPublicKeyInput, _ ...func(*kms.Options)) (*kms.GetPublicKeyOutput, error) { + if m.getPubFn != nil { + return m.getPubFn(ctx, params) + } + return &kms.GetPublicKeyOutput{ + PublicKey: m.pubKeyDER, + }, nil +} + +// generateTestEd25519DER generates an Ed25519 key pair and returns +// the public key in DER (X.509 SubjectPublicKeyInfo) format. +func generateTestEd25519DER(t *testing.T) (ed25519.PublicKey, []byte) { + t.Helper() + pub, _, err := ed25519.GenerateKey(nil) + require.NoError(t, err) + + der, err := x509.MarshalPKIXPublicKey(pub) + require.NoError(t, err) + return pub, der +} + +func TestNewKmsSignerFromClient_Success(t *testing.T) { + _, der := generateTestEd25519DER(t) + + mock := &mockKMSClient{pubKeyDER: der} + s, err := NewKmsSignerFromClient(context.Background(), mock, "arn:aws:kms:us-east-1:123456789012:key/test-key-id") + require.NoError(t, err) + require.NotNil(t, s) + + // Verify public key was cached + pubKey, err := s.GetPublic() + require.NoError(t, err) + require.NotNil(t, pubKey) + + // Verify address was cached + addr, err := s.GetAddress() + require.NoError(t, err) + assert.Len(t, addr, 32) // sha256 output +} + +func TestNewKmsSignerFromClient_EmptyKeyID(t *testing.T) { + _, err := NewKmsSignerFromClient(context.Background(), &mockKMSClient{}, "") + require.Error(t, err) + assert.Contains(t, err.Error(), "key ID is required") +} + +func TestNewKmsSignerFromClient_GetPublicKeyFails(t *testing.T) { + mock := &mockKMSClient{ + getPubFn: func(_ context.Context, _ *kms.GetPublicKeyInput) (*kms.GetPublicKeyOutput, error) { + return nil, fmt.Errorf("access denied") + }, + } + + _, err := NewKmsSignerFromClient(context.Background(), mock, "test-key") + require.Error(t, err) + assert.Contains(t, err.Error(), "access denied") +} + +func TestSign_Success(t *testing.T) { + _, der := generateTestEd25519DER(t) + + expectedSig := []byte("test-signature-bytes") + mock := &mockKMSClient{ + pubKeyDER: der, + signFn: func(_ context.Context, params *kms.SignInput) (*kms.SignOutput, error) { + assert.Equal(t, types.MessageTypeRaw, params.MessageType) + assert.Equal(t, types.SigningAlgorithmSpecEd25519Sha512, params.SigningAlgorithm) + return &kms.SignOutput{Signature: expectedSig}, nil + }, + } + + s, err := NewKmsSignerFromClient(context.Background(), mock, "test-key") + require.NoError(t, err) + + sig, err := s.Sign([]byte("hello world")) + require.NoError(t, err) + assert.Equal(t, expectedSig, sig) +} + +func TestSign_KMSFailure(t *testing.T) { + _, der := generateTestEd25519DER(t) + + mock := &mockKMSClient{ + pubKeyDER: der, + signFn: func(_ context.Context, _ *kms.SignInput) (*kms.SignOutput, error) { + return nil, fmt.Errorf("throttling exception") + }, + } + + s, err := NewKmsSignerFromClient(context.Background(), mock, "test-key") + require.NoError(t, err) + + _, err = s.Sign([]byte("hello world")) + require.Error(t, err) + assert.Contains(t, err.Error(), "KMS Sign failed") +} + +func TestGetPublic_Cached(t *testing.T) { + pub, der := generateTestEd25519DER(t) + + mock := &mockKMSClient{pubKeyDER: der} + s, err := NewKmsSignerFromClient(context.Background(), mock, "test-key") + require.NoError(t, err) + + cryptoPub, err := s.GetPublic() + require.NoError(t, err) + + raw, err := cryptoPub.Raw() + require.NoError(t, err) + assert.Equal(t, []byte(pub), raw) +} + +func TestGetAddress_Deterministic(t *testing.T) { + _, der := generateTestEd25519DER(t) + + mock := &mockKMSClient{pubKeyDER: der} + s, err := NewKmsSignerFromClient(context.Background(), mock, "test-key") + require.NoError(t, err) + + addr1, err := s.GetAddress() + require.NoError(t, err) + + addr2, err := s.GetAddress() + require.NoError(t, err) + + assert.Equal(t, addr1, addr2, "address should be deterministic") +} From ec30921f30322232cd6d507ffc54a404be2629ed Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Mon, 16 Mar 2026 18:24:08 +0100 Subject: [PATCH 2/7] Extend kms --- block/internal/executing/executor.go | 6 +- block/internal/submitting/da_submitter.go | 21 +-- .../da_submitter_integration_test.go | 4 +- .../internal/submitting/da_submitter_test.go | 2 +- .../syncing/da_retriever_strict_test.go | 7 +- block/internal/syncing/da_retriever_test.go | 2 +- block/internal/syncing/p2p_handler_test.go | 10 +- block/internal/syncing/syncer_test.go | 2 +- pkg/cmd/init.go | 69 +++------ pkg/cmd/run_node.go | 39 ++--- pkg/config/config.go | 31 +++- pkg/config/config_test.go | 6 +- pkg/config/defaults.go | 4 + pkg/signer/aws/signer.go | 136 ++++++++++++++---- pkg/signer/aws/signer_test.go | 18 +-- pkg/signer/factory/factory.go | 57 ++++++++ pkg/signer/file/README.md | 2 +- pkg/signer/file/doc.go | 2 +- pkg/signer/file/example_test.go | 5 +- pkg/signer/file/file_signer_test.go | 11 +- pkg/signer/file/local.go | 3 +- pkg/signer/noop/signer.go | 3 +- pkg/signer/noop/signer_test.go | 5 +- pkg/signer/signer.go | 5 +- pkg/sync/sync_service_test.go | 2 +- types/utils.go | 5 +- 26 files changed, 298 insertions(+), 159 deletions(-) create mode 100644 pkg/signer/factory/factory.go diff --git a/block/internal/executing/executor.go b/block/internal/executing/executor.go index 42621c024c..0f5ed5fa9f 100644 --- a/block/internal/executing/executor.go +++ b/block/internal/executing/executor.go @@ -542,7 +542,7 @@ func (e *Executor) ProduceBlock(ctx context.Context) error { // signing the header is done after applying the block // as for signing, the state of the block may be required by the signature payload provider. // For based sequencer, this will return an empty signature. - signature, _, err := e.signHeader(&header.Header) + signature, _, err := e.signHeader(ctx, &header.Header) if err != nil { return fmt.Errorf("failed to sign header: %w", err) } @@ -821,7 +821,7 @@ func (e *Executor) ApplyBlock(ctx context.Context, header types.Header, data *ty // signHeader signs the block header and returns both the signature and the // serialized header bytes (signing payload). The caller can reuse headerBytes // in SaveBlockDataFromBytes to avoid a redundant MarshalBinary call. -func (e *Executor) signHeader(header *types.Header) (types.Signature, []byte, error) { +func (e *Executor) signHeader(ctx context.Context, header *types.Header) (types.Signature, []byte, error) { // For based sequencer, return empty signature as there is no signer if e.signer == nil { return types.Signature{}, nil, nil @@ -832,7 +832,7 @@ func (e *Executor) signHeader(header *types.Header) (types.Signature, []byte, er return nil, nil, fmt.Errorf("failed to get signature payload: %w", err) } - sig, err := e.signer.Sign(bz) + sig, err := e.signer.Sign(ctx, bz) if err != nil { return nil, nil, err } diff --git a/block/internal/submitting/da_submitter.go b/block/internal/submitting/da_submitter.go index 4720d7788f..1f7edbed8d 100644 --- a/block/internal/submitting/da_submitter.go +++ b/block/internal/submitting/da_submitter.go @@ -225,7 +225,7 @@ func (s *DASubmitter) SubmitHeaders(ctx context.Context, headers []*types.Signed s.logger.Info().Int("count", len(headers)).Msg("submitting headers to DA") // Create DA envelopes with parallel signing and caching - envelopes, err := s.createDAEnvelopes(headers, marshalledHeaders, signer) + envelopes, err := s.createDAEnvelopes(ctx, headers, marshalledHeaders, signer) if err != nil { return err } @@ -256,7 +256,7 @@ func (s *DASubmitter) SubmitHeaders(ctx context.Context, headers []*types.Signed // createDAEnvelopes creates signed DA envelopes for the given headers. // It uses caching to avoid re-signing on retries and parallel signing for new envelopes. -func (s *DASubmitter) createDAEnvelopes(headers []*types.SignedHeader, marshalledHeaders [][]byte, signer signer.Signer) ([][]byte, error) { +func (s *DASubmitter) createDAEnvelopes(ctx context.Context, headers []*types.SignedHeader, marshalledHeaders [][]byte, signer signer.Signer) ([][]byte, error) { envelopes := make([][]byte, len(headers)) // First pass: check cache for already-signed envelopes @@ -284,7 +284,7 @@ func (s *DASubmitter) createDAEnvelopes(headers []*types.SignedHeader, marshalle // For small batches, sign sequentially to avoid goroutine overhead if len(needSigning) <= 2 || s.signingWorkers <= 1 { for _, i := range needSigning { - envelope, err := s.signAndCacheEnvelope(headers[i], marshalledHeaders[i], signer) + envelope, err := s.signAndCacheEnvelope(ctx, headers[i], marshalledHeaders[i], signer) if err != nil { return nil, fmt.Errorf("failed to create envelope for header %d: %w", i, err) } @@ -294,11 +294,12 @@ func (s *DASubmitter) createDAEnvelopes(headers []*types.SignedHeader, marshalle } // Parallel signing for larger batches - return s.signEnvelopesParallel(headers, marshalledHeaders, envelopes, needSigning, signer) + return s.signEnvelopesParallel(ctx, headers, marshalledHeaders, envelopes, needSigning, signer) } // signEnvelopesParallel signs envelopes in parallel using a worker pool. func (s *DASubmitter) signEnvelopesParallel( + ctx context.Context, headers []*types.SignedHeader, marshalledHeaders [][]byte, envelopes [][]byte, @@ -323,7 +324,7 @@ func (s *DASubmitter) signEnvelopesParallel( for range numWorkers { wg.Go(func() { for job := range jobs { - envelope, err := s.signAndCacheEnvelope(headers[job.index], marshalledHeaders[job.index], signer) + envelope, err := s.signAndCacheEnvelope(ctx, headers[job.index], marshalledHeaders[job.index], signer) results <- signResult{index: job.index, envelope: envelope, err: err} } }) @@ -361,9 +362,9 @@ func (s *DASubmitter) signEnvelopesParallel( } // signAndCacheEnvelope signs a single header and caches the result. -func (s *DASubmitter) signAndCacheEnvelope(header *types.SignedHeader, marshalledHeader []byte, signer signer.Signer) ([]byte, error) { +func (s *DASubmitter) signAndCacheEnvelope(ctx context.Context, header *types.SignedHeader, marshalledHeader []byte, signer signer.Signer) ([]byte, error) { // Sign the pre-marshalled header content - envelopeSignature, err := signer.Sign(marshalledHeader) + envelopeSignature, err := signer.Sign(ctx, marshalledHeader) if err != nil { return nil, fmt.Errorf("failed to sign envelope: %w", err) } @@ -426,7 +427,7 @@ func (s *DASubmitter) SubmitData(ctx context.Context, unsignedDataList []*types. } // Sign the data (cache returns unsigned SignedData structs) - signedDataList, signedDataListBz, err := s.signData(unsignedDataList, marshalledData, signer, genesis) + signedDataList, signedDataListBz, err := s.signData(ctx, unsignedDataList, marshalledData, signer, genesis) if err != nil { return fmt.Errorf("failed to sign data: %w", err) } @@ -460,7 +461,7 @@ func (s *DASubmitter) SubmitData(ctx context.Context, unsignedDataList []*types. } // signData signs unsigned SignedData structs returned from cache -func (s *DASubmitter) signData(unsignedDataList []*types.SignedData, unsignedDataListBz [][]byte, signer signer.Signer, genesis genesis.Genesis) ([]*types.SignedData, [][]byte, error) { +func (s *DASubmitter) signData(ctx context.Context, unsignedDataList []*types.SignedData, unsignedDataListBz [][]byte, signer signer.Signer, genesis genesis.Genesis) ([]*types.SignedData, [][]byte, error) { if signer == nil { return nil, nil, fmt.Errorf("signer is nil") } @@ -493,7 +494,7 @@ func (s *DASubmitter) signData(unsignedDataList []*types.SignedData, unsignedDat continue } - signature, err := signer.Sign(unsignedDataListBz[i]) + signature, err := signer.Sign(ctx, unsignedDataListBz[i]) if err != nil { return nil, nil, fmt.Errorf("failed to sign data: %w", err) } diff --git a/block/internal/submitting/da_submitter_integration_test.go b/block/internal/submitting/da_submitter_integration_test.go index bb8d0342ff..fafe4f044c 100644 --- a/block/internal/submitting/da_submitter_integration_test.go +++ b/block/internal/submitting/da_submitter_integration_test.go @@ -53,7 +53,7 @@ func TestDASubmitter_SubmitHeadersAndData_MarksInclusionAndUpdatesLastSubmitted( hdr1 := &types.SignedHeader{Header: types.Header{BaseHeader: types.BaseHeader{ChainID: gen.ChainID, Height: 1, Time: uint64(time.Now().UnixNano())}, AppHash: stateRoot, ProposerAddress: addr}, Signer: types.Signer{PubKey: pub, Address: addr}} bz1, err := types.DefaultAggregatorNodeSignatureBytesProvider(&hdr1.Header) require.NoError(t, err) - sig1, err := n.Sign(bz1) + sig1, err := n.Sign(context.Background(), bz1) require.NoError(t, err) hdr1.Signature = sig1 data1 := &types.Data{Metadata: &types.Metadata{ChainID: gen.ChainID, Height: 1, Time: uint64(time.Now().UnixNano())}, Txs: types.Txs{types.Tx("a")}} @@ -61,7 +61,7 @@ func TestDASubmitter_SubmitHeadersAndData_MarksInclusionAndUpdatesLastSubmitted( hdr2 := &types.SignedHeader{Header: types.Header{BaseHeader: types.BaseHeader{ChainID: gen.ChainID, Height: 2, Time: uint64(time.Now().Add(time.Second).UnixNano())}, AppHash: stateRoot, ProposerAddress: addr}, Signer: types.Signer{PubKey: pub, Address: addr}} bz2, err := types.DefaultAggregatorNodeSignatureBytesProvider(&hdr2.Header) require.NoError(t, err) - sig2, err := n.Sign(bz2) + sig2, err := n.Sign(context.Background(), bz2) require.NoError(t, err) hdr2.Signature = sig2 data2 := &types.Data{Metadata: &types.Metadata{ChainID: gen.ChainID, Height: 2, Time: uint64(time.Now().Add(time.Second).UnixNano())}, Txs: types.Txs{types.Tx("b")}} diff --git a/block/internal/submitting/da_submitter_test.go b/block/internal/submitting/da_submitter_test.go index 67967dbe19..defe7e4d73 100644 --- a/block/internal/submitting/da_submitter_test.go +++ b/block/internal/submitting/da_submitter_test.go @@ -175,7 +175,7 @@ func TestDASubmitter_SubmitHeaders_Success(t *testing.T) { for _, header := range []*types.SignedHeader{header1, header2} { bz, err := types.DefaultAggregatorNodeSignatureBytesProvider(&header.Header) require.NoError(t, err) - sig, err := signer.Sign(bz) + sig, err := signer.Sign(context.Background(), bz) require.NoError(t, err) header.Signature = sig } diff --git a/block/internal/syncing/da_retriever_strict_test.go b/block/internal/syncing/da_retriever_strict_test.go index a6d5140d97..4760463577 100644 --- a/block/internal/syncing/da_retriever_strict_test.go +++ b/block/internal/syncing/da_retriever_strict_test.go @@ -1,6 +1,7 @@ package syncing import ( + "context" "testing" "time" @@ -31,7 +32,7 @@ func TestDARetriever_StrictEnvelopeMode_Switch(t *testing.T) { // Sign it bz, err := types.DefaultAggregatorNodeSignatureBytesProvider(&legacyHeader.Header) require.NoError(t, err) - sig, err := signer.Sign(bz) + sig, err := signer.Sign(context.Background(), bz) require.NoError(t, err) legacyHeader.Signature = sig @@ -50,7 +51,7 @@ func TestDARetriever_StrictEnvelopeMode_Switch(t *testing.T) { // Sign content bz2, err := types.DefaultAggregatorNodeSignatureBytesProvider(&envelopeHeader.Header) require.NoError(t, err) - sig2, err := signer.Sign(bz2) + sig2, err := signer.Sign(context.Background(), bz2) require.NoError(t, err) envelopeHeader.Signature = sig2 @@ -61,7 +62,7 @@ func TestDARetriever_StrictEnvelopeMode_Switch(t *testing.T) { contentBytes, err := envelopeHeader.MarshalBinary() require.NoError(t, err) // Sign envelope - envSig, err := signer.Sign(contentBytes) + envSig, err := signer.Sign(context.Background(), contentBytes) require.NoError(t, err) // Marshal to envelope envelopeBlob, err := envelopeHeader.MarshalDAEnvelope(envSig) diff --git a/block/internal/syncing/da_retriever_test.go b/block/internal/syncing/da_retriever_test.go index cbb527488d..a6922a3739 100644 --- a/block/internal/syncing/da_retriever_test.go +++ b/block/internal/syncing/da_retriever_test.go @@ -71,7 +71,7 @@ func makeSignedDataBytesWithTime(t *testing.T, chainID string, height uint64, pr // For DA SignedData, sign the Data payload bytes (matches DA submission logic) payload, _ := d.MarshalBinary() - sig, err := signer.Sign(payload) + sig, err := signer.Sign(context.Background(), payload) require.NoError(t, err) sd := &types.SignedData{Data: *d, Signature: sig, Signer: types.Signer{PubKey: pub, Address: proposer}} bin, err := sd.MarshalBinary() diff --git a/block/internal/syncing/p2p_handler_test.go b/block/internal/syncing/p2p_handler_test.go index a1cc6d993f..879dc089ee 100644 --- a/block/internal/syncing/p2p_handler_test.go +++ b/block/internal/syncing/p2p_handler_test.go @@ -51,7 +51,7 @@ func p2pMakeSignedHeader(t *testing.T, chainID string, height uint64, proposer [ } bz, err := types.DefaultAggregatorNodeSignatureBytesProvider(&hdr.Header) require.NoError(t, err, "failed to get signature bytes for header") - sig, err := signer.Sign(bz) + sig, err := signer.Sign(context.Background(), bz) require.NoError(t, err, "failed to sign header bytes") hdr.Signature = sig return &types.P2PSignedHeader{SignedHeader: hdr} @@ -140,7 +140,7 @@ func TestP2PHandler_ProcessHeight_EmitsEventWhenHeaderAndDataPresent(t *testing. header.DataHash = data.DACommitment() bz, err := types.DefaultAggregatorNodeSignatureBytesProvider(&header.Header) require.NoError(t, err) - sig, err := p.Signer.Sign(bz) + sig, err := p.Signer.Sign(context.Background(), bz) require.NoError(t, err) header.Signature = sig @@ -166,7 +166,7 @@ func TestP2PHandler_ProcessHeight_SkipsWhenDataMissing(t *testing.T) { header.DataHash = data.DACommitment() bz, err := types.DefaultAggregatorNodeSignatureBytesProvider(&header.Header) require.NoError(t, err) - sig, err := p.Signer.Sign(bz) + sig, err := p.Signer.Sign(context.Background(), bz) require.NoError(t, err) header.Signature = sig @@ -236,7 +236,7 @@ func TestP2PHandler_ProcessedHeightSkipsPreviouslyHandledBlocks(t *testing.T) { header.DataHash = data.DACommitment() bz, err := types.DefaultAggregatorNodeSignatureBytesProvider(&header.Header) require.NoError(t, err) - sig, err := p.Signer.Sign(bz) + sig, err := p.Signer.Sign(context.Background(), bz) require.NoError(t, err) header.Signature = sig @@ -259,7 +259,7 @@ func TestP2PHandler_SetProcessedHeightPreventsDuplicates(t *testing.T) { header.DataHash = data.DACommitment() bz, err := types.DefaultAggregatorNodeSignatureBytesProvider(&header.Header) require.NoError(t, err) - sig, err := p.Signer.Sign(bz) + sig, err := p.Signer.Sign(context.Background(), bz) require.NoError(t, err) header.Signature = sig diff --git a/block/internal/syncing/syncer_test.go b/block/internal/syncing/syncer_test.go index 771e71c284..c42024a4b7 100644 --- a/block/internal/syncing/syncer_test.go +++ b/block/internal/syncing/syncer_test.go @@ -78,7 +78,7 @@ func makeSignedHeaderBytes( } bz, err := types.DefaultAggregatorNodeSignatureBytesProvider(&hdr.Header) require.NoError(tb, err) - sig, err := signer.Sign(bz) + sig, err := signer.Sign(context.Background(), bz) require.NoError(tb, err) hdr.Signature = sig bin, err := hdr.MarshalBinary() diff --git a/pkg/cmd/init.go b/pkg/cmd/init.go index 79201a63b2..8a231b8b4d 100644 --- a/pkg/cmd/init.go +++ b/pkg/cmd/init.go @@ -6,11 +6,11 @@ import ( "os" "path/filepath" + rollconf "github.com/evstack/ev-node/pkg/config" rollconf "github.com/evstack/ev-node/pkg/config" "github.com/evstack/ev-node/pkg/hash" "github.com/evstack/ev-node/pkg/p2p/key" - awssigner "github.com/evstack/ev-node/pkg/signer/aws" - "github.com/evstack/ev-node/pkg/signer/file" + "github.com/evstack/ev-node/pkg/signer/factory" ) // CreateSigner sets up the signer configuration and creates necessary files @@ -19,61 +19,28 @@ func CreateSigner(config *rollconf.Config, homePath string, passphrase string) ( return nil, nil } - switch config.Signer.SignerType { - case "file": - if passphrase == "" { - return nil, fmt.Errorf("passphrase is required when using local file signer") - } - + if config.Signer.SignerType == "file" { signerDir := filepath.Join(homePath, "config") - if err := os.MkdirAll(signerDir, 0o750); err != nil { - return nil, fmt.Errorf("failed to create signer directory: %w", err) - } - config.Signer.SignerPath = signerDir + } - signer, err := file.CreateFileSystemSigner(config.Signer.SignerPath, []byte(passphrase)) - if err != nil { - return nil, fmt.Errorf("failed to initialize signer: %w", err) - } - - pubKey, err := signer.GetPublic() - if err != nil { - return nil, fmt.Errorf("failed to get public key: %w", err) - } - - bz, err := pubKey.Raw() - if err != nil { - return nil, fmt.Errorf("failed to get public key raw bytes: %w", err) - } - - proposerAddress := hash.SumTruncated(bz) - return proposerAddress, nil - - case "awskms": - // For KMS, the key is pre-provisioned in AWS. We fetch the public key - // to derive the proposer address. - signer, err := awssigner.NewKmsSigner(context.Background(), config.Signer.KmsRegion, config.Signer.KmsKeyID) - if err != nil { - return nil, fmt.Errorf("failed to initialize AWS KMS signer: %w", err) - } - - pubKey, err := signer.GetPublic() - if err != nil { - return nil, fmt.Errorf("failed to get public key from KMS: %w", err) - } - - bz, err := pubKey.Raw() - if err != nil { - return nil, fmt.Errorf("failed to get public key raw bytes: %w", err) - } + signer, err := factory.NewSigner(context.Background(), config, passphrase) + if err != nil { + return nil, fmt.Errorf("failed to initialize signer via factory: %w", err) + } - proposerAddress := hash.SumTruncated(bz) - return proposerAddress, nil + pubKey, err := signer.GetPublic() + if err != nil { + return nil, fmt.Errorf("failed to get public key: %w", err) + } - default: - return nil, fmt.Errorf("unknown signer type: %s", config.Signer.SignerType) + bz, err := pubKey.Raw() + if err != nil { + return nil, fmt.Errorf("failed to get public key raw bytes: %w", err) } + + proposerAddress := hash.SumTruncated(bz) + return proposerAddress, nil } // LoadOrGenNodeKey creates the node key file if it doesn't exist. diff --git a/pkg/cmd/run_node.go b/pkg/cmd/run_node.go index 52ffbdb546..fde7485278 100644 --- a/pkg/cmd/run_node.go +++ b/pkg/cmd/run_node.go @@ -26,8 +26,7 @@ import ( "github.com/evstack/ev-node/pkg/p2p" "github.com/evstack/ev-node/pkg/p2p/key" "github.com/evstack/ev-node/pkg/signer" - awssigner "github.com/evstack/ev-node/pkg/signer/aws" - "github.com/evstack/ev-node/pkg/signer/file" + "github.com/evstack/ev-node/pkg/signer/factory" "github.com/evstack/ev-node/pkg/telemetry" ) @@ -112,9 +111,8 @@ func StartNode( // eagerly over WebSocket if no DA server is running). var signer signer.Signer if nodeConfig.Node.Aggregator && !nodeConfig.Node.BasedSequencer { - switch nodeConfig.Signer.SignerType { - case "file": - // Get passphrase file path + passphrase := "" + if nodeConfig.Signer.SignerType == "file" { passphraseFile, err := cmd.Flags().GetString(rollconf.FlagSignerPassphraseFile) if err != nil { return fmt.Errorf("failed to get '%s' flag: %w", rollconf.FlagSignerPassphraseFile, err) @@ -124,36 +122,25 @@ func StartNode( return fmt.Errorf("passphrase file must be provided via --evnode.signer.passphrase_file") } - // Read passphrase from file passphraseBytes, err := os.ReadFile(passphraseFile) if err != nil { return fmt.Errorf("failed to read passphrase from file '%s': %w", passphraseFile, err) } - passphrase := strings.TrimSpace(string(passphraseBytes)) + passphrase = strings.TrimSpace(string(passphraseBytes)) if passphrase == "" { return fmt.Errorf("passphrase file '%s' is empty", passphraseFile) } + } - // Resolve signer path; allow absolute, relative to node root, or relative to CWD if resolution fails - signerPath, err := filepath.Abs(strings.TrimSuffix(nodeConfig.Signer.SignerPath, "signer.json")) - if err != nil { - return err - } - - signer, err = file.LoadFileSystemSigner(signerPath, []byte(passphrase)) - if err != nil { - return err - } - case "awskms": - logger.Info().Msg("initializing AWS KMS signer") - var err error - signer, err = awssigner.NewKmsSigner(ctx, nodeConfig.Signer.KmsRegion, nodeConfig.Signer.KmsKeyID) - if err != nil { - return fmt.Errorf("failed to initialize AWS KMS signer: %w", err) - } - default: - return fmt.Errorf("unknown signer type: %s", nodeConfig.Signer.SignerType) + var err error + signer, err = factory.NewSigner(ctx, &nodeConfig, passphrase) + if err != nil { + return fmt.Errorf("failed to initialize signer via factory: %w", err) + } + + if nodeConfig.Signer.SignerType == "awskms" { + logger.Info().Msg("initialized AWS KMS signer via factory") } } diff --git a/pkg/config/config.go b/pkg/config/config.go index 867a51dbdf..f88e28c6d9 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -144,6 +144,14 @@ const ( FlagSignerKmsKeyID = FlagPrefixEvnode + "signer.kms_key_id" // FlagSignerKmsRegion is a flag for specifying the KMS region FlagSignerKmsRegion = FlagPrefixEvnode + "signer.kms_region" + // FlagSignerKmsProfile is a flag for specifying the AWS profile + FlagSignerKmsProfile = FlagPrefixEvnode + "signer.kms_profile" + // FlagSignerKmsTimeout is a flag for specifying the KMS sign timeout + FlagSignerKmsTimeout = FlagPrefixEvnode + "signer.kms_timeout" + // FlagSignerKmsMaxRetries is a flag for specifying the KMS sign max retries + FlagSignerKmsMaxRetries = FlagPrefixEvnode + "signer.kms_max_retries" + // FlagSignerKmsCacheTTL is a flag for specifying the KMS public key cache TTL + FlagSignerKmsCacheTTL = FlagPrefixEvnode + "signer.kms_cache_ttl" // FlagSignerPassphraseFile is a flag for specifying the file containing the signer passphrase FlagSignerPassphraseFile = FlagPrefixEvnode + "signer.passphrase_file" @@ -298,8 +306,12 @@ type P2PConfig struct { type SignerConfig struct { SignerType string `mapstructure:"signer_type" yaml:"signer_type" comment:"Type of remote signer to use (file, grpc, awskms)"` SignerPath string `mapstructure:"signer_path" yaml:"signer_path" comment:"Path to the signer file or address"` - KmsKeyID string `mapstructure:"kms_key_id" yaml:"kms_key_id" comment:"AWS KMS Key ID or ARN for awskms signer"` - KmsRegion string `mapstructure:"kms_region" yaml:"kms_region" comment:"AWS Region for awskms signer"` + KmsKeyID string `mapstructure:"kms_key_id" yaml:"kms_key_id" comment:"AWS KMS Key ID or ARN for awskms signer"` + KmsRegion string `mapstructure:"kms_region" yaml:"kms_region" comment:"AWS Region for awskms signer"` + KmsProfile string `mapstructure:"kms_profile" yaml:"kms_profile" comment:"AWS Profile for awskms signer"` + KmsTimeout DurationWrapper `mapstructure:"kms_timeout" yaml:"kms_timeout" comment:"Timeout for individual AWS KMS Sign requests"` + KmsMaxRetries int `mapstructure:"kms_max_retries" yaml:"kms_max_retries" comment:"Maximum number of retries for transient AWS KMS failures"` + KmsCacheTTL DurationWrapper `mapstructure:"kms_cache_ttl" yaml:"kms_cache_ttl" comment:"Time-to-live for caching the AWS KMS public key (0 means cache forever)"` } // RPCConfig contains all RPC server configuration parameters @@ -398,6 +410,17 @@ func (c RaftConfig) Validate() error { // Validate validates the config and ensures that the root directory exists. // It creates the directory if it does not exist. func (c *Config) Validate() error { + if c.Signer.SignerType == "awskms" { + if c.Signer.KmsKeyID == "" { + return errors.New("evnode.signer.kms_key_id is required when signer_type is awskms") + } + if c.Signer.KmsTimeout.Duration <= 0 { + return errors.New("evnode.signer.kms_timeout must be positive") + } + if c.Signer.KmsMaxRetries < 0 { + return errors.New("evnode.signer.kms_max_retries must be non-negative") + } + } if c.RootDir == "" { return fmt.Errorf("root directory cannot be empty") } @@ -558,6 +581,10 @@ func AddFlags(cmd *cobra.Command) { cmd.Flags().String(FlagSignerPath, def.Signer.SignerPath, "path to the signer file or address") cmd.Flags().String(FlagSignerKmsKeyID, def.Signer.KmsKeyID, "AWS KMS Key ID or ARN for awskms signer") cmd.Flags().String(FlagSignerKmsRegion, def.Signer.KmsRegion, "AWS Region for awskms signer") + cmd.Flags().String(FlagSignerKmsProfile, def.Signer.KmsProfile, "AWS Profile for awskms signer") + cmd.Flags().Duration(FlagSignerKmsTimeout, def.Signer.KmsTimeout.Duration, "Timeout for individual AWS KMS Sign requests") + cmd.Flags().Int(FlagSignerKmsMaxRetries, def.Signer.KmsMaxRetries, "Maximum number of retries for transient AWS KMS failures") + cmd.Flags().Duration(FlagSignerKmsCacheTTL, def.Signer.KmsCacheTTL.Duration, "Time-to-live for caching the AWS KMS public key (0 means cache forever)") cmd.Flags().String(FlagSignerPassphraseFile, "", "path to file containing the signer passphrase (required for file signer and if aggregator is enabled)") cmd.MarkFlagsMutuallyExclusive(FlagLight, FlagAggregator) diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index b78cab8ad9..3d82688553 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -109,6 +109,10 @@ func TestAddFlags(t *testing.T) { assertFlagValue(t, flags, FlagSignerPath, DefaultConfig().Signer.SignerPath) assertFlagValue(t, flags, FlagSignerKmsKeyID, DefaultConfig().Signer.KmsKeyID) assertFlagValue(t, flags, FlagSignerKmsRegion, DefaultConfig().Signer.KmsRegion) + assertFlagValue(t, flags, FlagSignerKmsProfile, DefaultConfig().Signer.KmsProfile) + assertFlagValue(t, flags, FlagSignerKmsTimeout, DefaultConfig().Signer.KmsTimeout.Duration) + assertFlagValue(t, flags, FlagSignerKmsMaxRetries, DefaultConfig().Signer.KmsMaxRetries) + assertFlagValue(t, flags, FlagSignerKmsCacheTTL, DefaultConfig().Signer.KmsCacheTTL.Duration) // RPC flags assertFlagValue(t, flags, FlagRPCAddress, DefaultConfig().RPC.Address) @@ -120,7 +124,7 @@ func TestAddFlags(t *testing.T) { assertFlagValue(t, flags, FlagPruningInterval, DefaultConfig().Pruning.Interval.Duration) // Count the number of flags we're explicitly checking - expectedFlagCount := 69 // Update this number if you add more flag checks above + expectedFlagCount := 73 // Update this number if you add more flag checks above // Get the actual number of flags (both regular and persistent) actualFlagCount := 0 diff --git a/pkg/config/defaults.go b/pkg/config/defaults.go index 4009e5dc50..afde0a67ab 100644 --- a/pkg/config/defaults.go +++ b/pkg/config/defaults.go @@ -95,6 +95,10 @@ func DefaultConfig() Config { SignerPath: "config", KmsKeyID: "", KmsRegion: "", + KmsProfile: "", + KmsTimeout: DurationWrapper{10 * time.Second}, + KmsMaxRetries: 3, + KmsCacheTTL: DurationWrapper{0}, }, RPC: RPCConfig{ Address: "127.0.0.1:7331", diff --git a/pkg/signer/aws/signer.go b/pkg/signer/aws/signer.go index 7342be9f83..22cb9973ca 100644 --- a/pkg/signer/aws/signer.go +++ b/pkg/signer/aws/signer.go @@ -27,20 +27,54 @@ type KMSClient interface { GetPublicKey(ctx context.Context, params *kms.GetPublicKeyInput, optFns ...func(*kms.Options)) (*kms.GetPublicKeyOutput, error) } +// Options configures optional KmsSigner behaviour. +type Options struct { + // Timeout for individual KMS Sign API calls. Default: 10s. + Timeout time.Duration + // MaxRetries for transient KMS failures during Sign. Default: 3. + MaxRetries int + // CacheTTL controls how long the public key is cached before being + // re-fetched from KMS. 0 (default) means cache forever. + CacheTTL time.Duration +} + +func (o *Options) timeout() time.Duration { + if o != nil && o.Timeout > 0 { + return o.Timeout + } + return 10 * time.Second +} + +func (o *Options) maxRetries() int { + if o != nil && o.MaxRetries > 0 { + return o.MaxRetries + } + return 3 +} + +func (o *Options) cacheTTL() time.Duration { + if o != nil { + return o.CacheTTL + } + return 0 +} + // KmsSigner implements the signer.Signer interface using AWS KMS. type KmsSigner struct { - client KMSClient - keyID string - mu sync.RWMutex - publicKey crypto.PubKey - address []byte + client KMSClient + keyID string + opts Options + mu sync.RWMutex + pubKey crypto.PubKey + address []byte + cacheAt time.Time } var _ signer.Signer = (*KmsSigner)(nil) // NewKmsSigner creates a new Signer backed by an AWS KMS Ed25519 key. // It uses the standard AWS credential chain (env vars, ~/.aws/credentials, IAM roles, etc.). -func NewKmsSigner(ctx context.Context, region string, keyID string) (*KmsSigner, error) { +func NewKmsSigner(ctx context.Context, region string, profile string, keyID string, opts *Options) (*KmsSigner, error) { if keyID == "" { return nil, fmt.Errorf("aws kms key ID is required") } @@ -49,6 +83,9 @@ func NewKmsSigner(ctx context.Context, region string, keyID string) (*KmsSigner, if region != "" { cfgOpts = append(cfgOpts, awsconfig.WithRegion(region)) } + if profile != "" { + cfgOpts = append(cfgOpts, awsconfig.WithSharedConfigProfile(profile)) + } cfg, err := awsconfig.LoadDefaultConfig(ctx, cfgOpts...) if err != nil { @@ -56,19 +93,25 @@ func NewKmsSigner(ctx context.Context, region string, keyID string) (*KmsSigner, } client := kms.NewFromConfig(cfg) - return NewKmsSignerFromClient(ctx, client, keyID) + return NewKmsSignerFromClient(ctx, client, keyID, opts) } // NewKmsSignerFromClient creates a KmsSigner from an existing KMS client. // Useful for testing with a mock client. -func NewKmsSignerFromClient(ctx context.Context, client KMSClient, keyID string) (*KmsSigner, error) { +func NewKmsSignerFromClient(ctx context.Context, client KMSClient, keyID string, opts *Options) (*KmsSigner, error) { if keyID == "" { return nil, fmt.Errorf("aws kms key ID is required") } + o := Options{} + if opts != nil { + o = *opts + } + s := &KmsSigner{ client: client, keyID: keyID, + opts: o, } // Fetch and cache the public key eagerly so we fail fast on misconfiguration. @@ -114,40 +157,79 @@ func (s *KmsSigner) fetchPublicKey(ctx context.Context) error { s.mu.Lock() defer s.mu.Unlock() - s.publicKey = cryptoPubKey + s.pubKey = cryptoPubKey s.address = address[:] + s.cacheAt = time.Now() return nil } -// Sign signs a message using the remote KMS key. -func (s *KmsSigner) Sign(message []byte) ([]byte, error) { - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - out, err := s.client.Sign(ctx, &kms.SignInput{ - KeyId: aws.String(s.keyID), - Message: message, - MessageType: types.MessageTypeRaw, - SigningAlgorithm: types.SigningAlgorithmSpecEd25519Sha512, - }) - if err != nil { - return nil, fmt.Errorf("KMS Sign failed: %w", err) +// Sign signs a message using the remote KMS key with configurable timeout +// and retry with exponential backoff. +func (s *KmsSigner) Sign(ctx context.Context, message []byte) ([]byte, error) { + var lastErr error + maxRetries := s.opts.maxRetries() + timeout := s.opts.timeout() + + for attempt := 0; attempt < maxRetries; attempt++ { + if attempt > 0 { + // Exponential backoff: 100ms, 200ms, 400ms, ... + backoff := time.Duration(100< 0 && time.Since(s.cacheAt) > ttl + s.mu.RUnlock() + + if expired { + if err := s.fetchPublicKey(context.Background()); err != nil { + // If refresh fails, return the stale cached key + if pubKey != nil { + return pubKey, nil + } + return nil, fmt.Errorf("failed to refresh public key: %w", err) + } + s.mu.RLock() + pubKey = s.pubKey + s.mu.RUnlock() + } - if s.publicKey == nil { + if pubKey == nil { return nil, fmt.Errorf("public key not loaded") } - return s.publicKey, nil + return pubKey, nil } // GetAddress returns the cached address derived from the public key. diff --git a/pkg/signer/aws/signer_test.go b/pkg/signer/aws/signer_test.go index 5ca4c0133c..fed439b4c1 100644 --- a/pkg/signer/aws/signer_test.go +++ b/pkg/signer/aws/signer_test.go @@ -52,7 +52,7 @@ func TestNewKmsSignerFromClient_Success(t *testing.T) { _, der := generateTestEd25519DER(t) mock := &mockKMSClient{pubKeyDER: der} - s, err := NewKmsSignerFromClient(context.Background(), mock, "arn:aws:kms:us-east-1:123456789012:key/test-key-id") + s, err := NewKmsSignerFromClient(context.Background(), mock, "arn:aws:kms:us-east-1:123456789012:key/test-key-id", nil) require.NoError(t, err) require.NotNil(t, s) @@ -68,7 +68,7 @@ func TestNewKmsSignerFromClient_Success(t *testing.T) { } func TestNewKmsSignerFromClient_EmptyKeyID(t *testing.T) { - _, err := NewKmsSignerFromClient(context.Background(), &mockKMSClient{}, "") + _, err := NewKmsSignerFromClient(context.Background(), &mockKMSClient{}, "", nil) require.Error(t, err) assert.Contains(t, err.Error(), "key ID is required") } @@ -80,7 +80,7 @@ func TestNewKmsSignerFromClient_GetPublicKeyFails(t *testing.T) { }, } - _, err := NewKmsSignerFromClient(context.Background(), mock, "test-key") + _, err := NewKmsSignerFromClient(context.Background(), mock, "test-key", nil) require.Error(t, err) assert.Contains(t, err.Error(), "access denied") } @@ -98,10 +98,10 @@ func TestSign_Success(t *testing.T) { }, } - s, err := NewKmsSignerFromClient(context.Background(), mock, "test-key") + s, err := NewKmsSignerFromClient(context.Background(), mock, "test-key", nil) require.NoError(t, err) - sig, err := s.Sign([]byte("hello world")) + sig, err := s.Sign(context.Background(), []byte("hello world")) require.NoError(t, err) assert.Equal(t, expectedSig, sig) } @@ -116,10 +116,10 @@ func TestSign_KMSFailure(t *testing.T) { }, } - s, err := NewKmsSignerFromClient(context.Background(), mock, "test-key") + s, err := NewKmsSignerFromClient(context.Background(), mock, "test-key", nil) require.NoError(t, err) - _, err = s.Sign([]byte("hello world")) + _, err = s.Sign(context.Background(), []byte("hello world")) require.Error(t, err) assert.Contains(t, err.Error(), "KMS Sign failed") } @@ -128,7 +128,7 @@ func TestGetPublic_Cached(t *testing.T) { pub, der := generateTestEd25519DER(t) mock := &mockKMSClient{pubKeyDER: der} - s, err := NewKmsSignerFromClient(context.Background(), mock, "test-key") + s, err := NewKmsSignerFromClient(context.Background(), mock, "test-key", nil) require.NoError(t, err) cryptoPub, err := s.GetPublic() @@ -143,7 +143,7 @@ func TestGetAddress_Deterministic(t *testing.T) { _, der := generateTestEd25519DER(t) mock := &mockKMSClient{pubKeyDER: der} - s, err := NewKmsSignerFromClient(context.Background(), mock, "test-key") + s, err := NewKmsSignerFromClient(context.Background(), mock, "test-key", nil) require.NoError(t, err) addr1, err := s.GetAddress() diff --git a/pkg/signer/factory/factory.go b/pkg/signer/factory/factory.go new file mode 100644 index 0000000000..0be914db54 --- /dev/null +++ b/pkg/signer/factory/factory.go @@ -0,0 +1,57 @@ +package factory + +import ( + "context" + "fmt" + "os" + "path/filepath" + "strings" + + rollconf "github.com/evstack/ev-node/pkg/config" + "github.com/evstack/ev-node/pkg/signer" + awssigner "github.com/evstack/ev-node/pkg/signer/aws" + "github.com/evstack/ev-node/pkg/signer/file" +) + +// NewSigner creates a new Signer based on the configuration. +func NewSigner(ctx context.Context, config *rollconf.Config, passphrase string) (signer.Signer, error) { + switch config.Signer.SignerType { + case "file": + if passphrase == "" { + return nil, fmt.Errorf("passphrase is required when using local file signer") + } + + // Resolve signer path; allow absolute, relative to node root, or relative to CWD if resolution fails + signerPath, err := filepath.Abs(strings.TrimSuffix(config.Signer.SignerPath, "signer.json")) + if err != nil { + return nil, err + } + + // Ensure directory exists for init command cases + if err := os.MkdirAll(signerPath, 0o750); err != nil { + return nil, fmt.Errorf("failed to create signer directory: %w", err) + } + + // This will either create (if it doesn't exist) or load (if it does). + // In a strictly decoupled factory we'd differentiate Create vs Load, but given the + // underlying CreateFileSystemSigner and LoadFileSystemSigner are typically idempotent + // in behavior if files are provided, let's check for existing files: + signerFile := filepath.Join(signerPath, "signer.json") + if _, err := os.Stat(signerFile); os.IsNotExist(err) { + return file.CreateFileSystemSigner(signerPath, []byte(passphrase)) + } + + return file.LoadFileSystemSigner(signerPath, []byte(passphrase)) + + case "awskms": + opts := &awssigner.Options{ + Timeout: config.Signer.KmsTimeout.Duration, + MaxRetries: config.Signer.KmsMaxRetries, + CacheTTL: config.Signer.KmsCacheTTL.Duration, + } + return awssigner.NewKmsSigner(ctx, config.Signer.KmsRegion, config.Signer.KmsProfile, config.Signer.KmsKeyID, opts) + + default: + return nil, fmt.Errorf("unknown signer type: %s", config.Signer.SignerType) + } +} diff --git a/pkg/signer/file/README.md b/pkg/signer/file/README.md index 9936bca874..e2ecd09302 100644 --- a/pkg/signer/file/README.md +++ b/pkg/signer/file/README.md @@ -31,7 +31,7 @@ if err != nil { // Sign a message message := []byte("Message to sign") -signature, err := signer.Sign(message) +signature, err := signer.Sign(context.Background(), message) if err != nil { // Handle error } diff --git a/pkg/signer/file/doc.go b/pkg/signer/file/doc.go index 6c367aa82a..b150c42df4 100644 --- a/pkg/signer/file/doc.go +++ b/pkg/signer/file/doc.go @@ -14,7 +14,7 @@ The keys are stored in a file in the local filesystem. // Sign a message message := []byte("Message to sign") - signature, err := signer.Sign(message) + signature, err := signer.Sign(context.Background(), message) if err != nil { panic(err) } diff --git a/pkg/signer/file/example_test.go b/pkg/signer/file/example_test.go index 433997d9d8..71d2ced7b7 100644 --- a/pkg/signer/file/example_test.go +++ b/pkg/signer/file/example_test.go @@ -1,6 +1,7 @@ package file import ( + "context" "os" "path/filepath" "testing" @@ -31,7 +32,7 @@ func TestFileSystemSigner(t *testing.T) { // Sign a message message := []byte("Hello, world!") - signature, err := signer.Sign(message) + signature, err := signer.Sign(context.Background(), message) require.NoError(t, err) require.NotNil(t, signature) @@ -78,7 +79,7 @@ func Example() { // Sign a message message := []byte("Message to sign") - signature, err := signer.Sign(message) + signature, err := signer.Sign(context.Background(), message) if err != nil { panic(err) } diff --git a/pkg/signer/file/file_signer_test.go b/pkg/signer/file/file_signer_test.go index ec24e07186..513c4783f8 100644 --- a/pkg/signer/file/file_signer_test.go +++ b/pkg/signer/file/file_signer_test.go @@ -2,6 +2,7 @@ package file import ( "bytes" + "context" "encoding/json" "fmt" "os" @@ -174,7 +175,7 @@ func TestSign(t *testing.T) { // Sign a message message := []byte("Hello, Evolve!") - signature, err := signer.Sign(message) + signature, err := signer.Sign(context.Background(), message) require.NoError(t, err) require.NotNil(t, signature) @@ -208,7 +209,7 @@ func TestSign(t *testing.T) { for _, msg := range messages { message := []byte(msg) - signature, err := signer.Sign(message) + signature, err := signer.Sign(context.Background(), message) require.NoError(t, err) valid, err := pubKey.Verify(message, signature) @@ -248,7 +249,7 @@ func TestKeyPersistence(t *testing.T) { // Sign a test message message := []byte("Test message") - signature, err := signer1.Sign(message) + signature, err := signer1.Sign(context.Background(), message) require.NoError(t, err) // Verify signature works with this key @@ -405,7 +406,7 @@ func TestConcurrentAccess(t *testing.T) { } // Sign message - signature, err := signer.Sign(message) + signature, err := signer.Sign(context.Background(), message) if err != nil { errChan <- err continue @@ -604,7 +605,7 @@ func TestSign_NilPrivateKey(t *testing.T) { } message := []byte("test message") - _, err := signer.Sign(message) + _, err := signer.Sign(context.Background(), message) assert.Error(t, err) assert.Contains(t, err.Error(), "private key not loaded") } diff --git a/pkg/signer/file/local.go b/pkg/signer/file/local.go index 8ab437ae97..4bc602d3be 100644 --- a/pkg/signer/file/local.go +++ b/pkg/signer/file/local.go @@ -1,6 +1,7 @@ package file import ( + "context" "crypto/aes" "crypto/cipher" "crypto/rand" @@ -382,7 +383,7 @@ func (s *FileSystemSigner) loadKeys(passphrase []byte) error { } // Sign signs a message using the private key -func (s *FileSystemSigner) Sign(message []byte) ([]byte, error) { +func (s *FileSystemSigner) Sign(_ context.Context, message []byte) ([]byte, error) { s.mu.RLock() defer s.mu.RUnlock() diff --git a/pkg/signer/noop/signer.go b/pkg/signer/noop/signer.go index ea65c44ecb..b1e239f174 100644 --- a/pkg/signer/noop/signer.go +++ b/pkg/signer/noop/signer.go @@ -1,6 +1,7 @@ package noop import ( + "context" "crypto/sha256" "fmt" @@ -34,7 +35,7 @@ func NewNoopSigner(privKey crypto.PrivKey) (signer.Signer, error) { } // Sign implements the Signer interface by signing the message with the Ed25519 private key. -func (n *NoopSigner) Sign(message []byte) ([]byte, error) { +func (n *NoopSigner) Sign(_ context.Context, message []byte) ([]byte, error) { if n.privKey == nil { return nil, fmt.Errorf("private key not loaded") } diff --git a/pkg/signer/noop/signer_test.go b/pkg/signer/noop/signer_test.go index a3ec506ba5..268f717e61 100644 --- a/pkg/signer/noop/signer_test.go +++ b/pkg/signer/noop/signer_test.go @@ -1,6 +1,7 @@ package noop import ( + "context" "testing" "github.com/libp2p/go-libp2p/core/crypto" @@ -34,7 +35,7 @@ func TestNoopSigner(t *testing.T) { require.NoError(t, err) message := []byte("test message") - signature, err := signer.Sign(message) + signature, err := signer.Sign(context.Background(), message) require.NoError(t, err) require.NotNil(t, signature) @@ -82,7 +83,7 @@ func TestNoopSigner(t *testing.T) { message := []byte("test message") wrongMessage := []byte("wrong message") - signature, err := signer.Sign(message) + signature, err := signer.Sign(context.Background(), message) require.NoError(t, err) pubKey, err := signer.GetPublic() diff --git a/pkg/signer/signer.go b/pkg/signer/signer.go index 09c6b951b4..2c6eb36119 100644 --- a/pkg/signer/signer.go +++ b/pkg/signer/signer.go @@ -1,13 +1,15 @@ package signer import ( + "context" + "github.com/libp2p/go-libp2p/core/crypto" ) // Signer is an interface for signing and verifying messages. type Signer interface { // Sign takes a message as bytes and returns its signature. - Sign(message []byte) ([]byte, error) + Sign(ctx context.Context, message []byte) ([]byte, error) // GetPublic returns the public key paired with this private key. GetPublic() (crypto.PubKey, error) @@ -15,3 +17,4 @@ type Signer interface { // GetAddress returns the address of the signer. GetAddress() ([]byte, error) } + diff --git a/pkg/sync/sync_service_test.go b/pkg/sync/sync_service_test.go index 593b95199a..d817c49370 100644 --- a/pkg/sync/sync_service_test.go +++ b/pkg/sync/sync_service_test.go @@ -372,7 +372,7 @@ func nextHeader(t *testing.T, previousHeader *types.SignedHeader, chainID string } b, err := newSignedHeader.Header.MarshalBinary() require.NoError(t, err) - signature, err := noopSigner.Sign(b) + signature, err := noopSigner.Sign(context.Background(), b) require.NoError(t, err) newSignedHeader.Signature = signature require.NoError(t, newSignedHeader.Validate()) diff --git a/types/utils.go b/types/utils.go index d8c2527521..aa88e96e14 100644 --- a/types/utils.go +++ b/types/utils.go @@ -1,6 +1,7 @@ package types import ( + "context" cryptoRand "crypto/rand" "math/rand" "time" @@ -178,7 +179,7 @@ func GetRandomSignedHeaderCustom(config *HeaderConfig, chainID string) (*SignedH if err != nil { return nil, err } - signature, err := config.Signer.Sign(b) + signature, err := config.Signer.Sign(context.Background(), b) if err != nil { return nil, err } @@ -288,7 +289,7 @@ func GetSignature(header Header, signer signer.Signer) (Signature, error) { if err != nil { return nil, err } - return signer.Sign(b) + return signer.Sign(context.Background(), b) } func getBlockDataWith(nTxs int) *Data { From 25f1aa44ca535c32f9d979ff7acda082e935c781 Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Mon, 16 Mar 2026 18:29:25 +0100 Subject: [PATCH 3/7] Improve kms --- apps/evm/cmd/init.go | 2 +- apps/evm/go.mod | 15 ++++ apps/evm/go.sum | 30 +++++++ apps/grpc/cmd/init.go | 2 +- apps/grpc/go.mod | 15 ++++ apps/grpc/go.sum | 30 +++++++ apps/testapp/cmd/init.go | 2 +- apps/testapp/go.mod | 15 ++++ apps/testapp/go.sum | 30 +++++++ .../da_submitter_integration_test.go | 4 +- .../internal/submitting/da_submitter_test.go | 6 +- block/internal/submitting/submitter_test.go | 4 +- .../syncing/da_retriever_strict_test.go | 7 +- block/internal/syncing/da_retriever_test.go | 2 +- block/internal/syncing/p2p_handler_test.go | 16 ++-- block/internal/syncing/syncer_test.go | 2 +- go.mod | 8 +- pkg/cmd/init.go | 6 +- pkg/cmd/init_test.go | 14 +-- pkg/cmd/run_node.go | 5 +- pkg/config/config.go | 4 +- pkg/config/defaults.go | 16 ++-- pkg/signer/aws/signer.go | 75 +++++++++++----- pkg/signer/aws/signer_test.go | 86 ++++++++++++++++++- pkg/signer/factory/factory.go | 30 ++++--- pkg/signer/file/example_test.go | 2 +- pkg/signer/file/file_signer_test.go | 11 ++- pkg/signer/file/local.go | 5 +- pkg/signer/noop/signer.go | 5 +- pkg/signer/noop/signer_test.go | 5 +- pkg/signer/signer.go | 1 - pkg/store/store_adapter_test.go | 2 +- pkg/sync/sync_service_test.go | 10 +-- types/signed_header_test.go | 7 +- types/utils.go | 20 ++--- types/utils_test.go | 3 +- 36 files changed, 377 insertions(+), 120 deletions(-) diff --git a/apps/evm/cmd/init.go b/apps/evm/cmd/init.go index 3a282a0b24..5c8adbd75c 100644 --- a/apps/evm/cmd/init.go +++ b/apps/evm/cmd/init.go @@ -58,7 +58,7 @@ func InitCmd() *cobra.Command { } } - proposerAddress, err := rollcmd.CreateSigner(&cfg, homePath, passphrase) + proposerAddress, err := rollcmd.CreateSigner(cmd.Context(), &cfg, homePath, passphrase) if err != nil { return err } diff --git a/apps/evm/go.mod b/apps/evm/go.mod index 3bda4fd8a4..ca883eb6c0 100644 --- a/apps/evm/go.mod +++ b/apps/evm/go.mod @@ -25,6 +25,21 @@ require ( github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect github.com/StackExchange/wmi v1.2.1 // indirect github.com/armon/go-metrics v0.4.1 // indirect + github.com/aws/aws-sdk-go-v2 v1.41.4 // indirect + github.com/aws/aws-sdk-go-v2/config v1.32.12 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.19.12 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 // indirect + github.com/aws/aws-sdk-go-v2/service/kms v1.50.3 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 // indirect + github.com/aws/smithy-go v1.24.2 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.20.0 // indirect diff --git a/apps/evm/go.sum b/apps/evm/go.sum index 679352a745..d875f7bcb9 100644 --- a/apps/evm/go.sum +++ b/apps/evm/go.sum @@ -271,6 +271,36 @@ github.com/apache/arrow/go/v14 v14.0.2/go.mod h1:u3fgh3EdgN/YQ8cVQRguVW3R+seMybF github.com/apache/thrift v0.17.0/go.mod h1:OLxhMRJxomX+1I/KUw03qoV3mMz16BwaKI+d4fPBx7Q= github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k= +github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= +github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0= +github.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g= +github.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8= +github.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 h1:CNXO7mvgThFGqOFgbNAP2nol2qAWBOGfqR/7tQlvLmc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 h1:tN6W/hg+pkM+tf9XDkWUbDEjGLb+raoBMFsTodcoYKw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 h1:2HvVAIq+YqgGotK6EkMf+KIEqTISmTYh5zLpYyeTo1Y= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20/go.mod h1:V4X406Y666khGa8ghKmphma/7C0DAtEQYhkq9z4vpbk= +github.com/aws/aws-sdk-go-v2/service/kms v1.50.3 h1:s/zDSG/a/Su9aX+v0Ld9cimUCdkr5FWPmBV8owaEbZY= +github.com/aws/aws-sdk-go-v2/service/kms v1.50.3/go.mod h1:/iSgiUor15ZuxFGQSTf3lA2FmKxFsQoc2tADOarQBSw= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 h1:0GFOLzEbOyZABS3PhYfBIx2rNBACYcKty+XGkTgw1ow= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.8/go.mod h1:LXypKvk85AROkKhOG6/YEcHFPoX+prKTowKnVdcaIxE= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 h1:kiIDLZ005EcKomYYITtfsjn7dtOwHDOFy7IbPXKek2o= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.13/go.mod h1:2h/xGEowcW/g38g06g3KpRWDlT+OTfxxI0o1KqayAB8= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB8KfgAEuG0dc08Bkda7NU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk= +github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= +github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= diff --git a/apps/grpc/cmd/init.go b/apps/grpc/cmd/init.go index 2800003e20..f48114be8c 100644 --- a/apps/grpc/cmd/init.go +++ b/apps/grpc/cmd/init.go @@ -60,7 +60,7 @@ This will create the necessary configuration structure in the specified root dir } } - proposerAddress, err := rollcmd.CreateSigner(&cfg, homePath, passphrase) + proposerAddress, err := rollcmd.CreateSigner(cmd.Context(), &cfg, homePath, passphrase) if err != nil { return err } diff --git a/apps/grpc/go.mod b/apps/grpc/go.mod index 98907d4f52..f89fdc6110 100644 --- a/apps/grpc/go.mod +++ b/apps/grpc/go.mod @@ -20,6 +20,21 @@ require ( connectrpc.com/connect v1.19.1 // indirect connectrpc.com/grpcreflect v1.3.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect + github.com/aws/aws-sdk-go-v2 v1.41.4 // indirect + github.com/aws/aws-sdk-go-v2/config v1.32.12 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.19.12 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 // indirect + github.com/aws/aws-sdk-go-v2/service/kms v1.50.3 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 // indirect + github.com/aws/smithy-go v1.24.2 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/boltdb/bolt v1.3.1 // indirect diff --git a/apps/grpc/go.sum b/apps/grpc/go.sum index 626f4a9bc8..e9e6ce6e7f 100644 --- a/apps/grpc/go.sum +++ b/apps/grpc/go.sum @@ -261,6 +261,36 @@ github.com/apache/arrow/go/v14 v14.0.2/go.mod h1:u3fgh3EdgN/YQ8cVQRguVW3R+seMybF github.com/apache/thrift v0.17.0/go.mod h1:OLxhMRJxomX+1I/KUw03qoV3mMz16BwaKI+d4fPBx7Q= github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k= +github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= +github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0= +github.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g= +github.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8= +github.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 h1:CNXO7mvgThFGqOFgbNAP2nol2qAWBOGfqR/7tQlvLmc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 h1:tN6W/hg+pkM+tf9XDkWUbDEjGLb+raoBMFsTodcoYKw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 h1:2HvVAIq+YqgGotK6EkMf+KIEqTISmTYh5zLpYyeTo1Y= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20/go.mod h1:V4X406Y666khGa8ghKmphma/7C0DAtEQYhkq9z4vpbk= +github.com/aws/aws-sdk-go-v2/service/kms v1.50.3 h1:s/zDSG/a/Su9aX+v0Ld9cimUCdkr5FWPmBV8owaEbZY= +github.com/aws/aws-sdk-go-v2/service/kms v1.50.3/go.mod h1:/iSgiUor15ZuxFGQSTf3lA2FmKxFsQoc2tADOarQBSw= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 h1:0GFOLzEbOyZABS3PhYfBIx2rNBACYcKty+XGkTgw1ow= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.8/go.mod h1:LXypKvk85AROkKhOG6/YEcHFPoX+prKTowKnVdcaIxE= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 h1:kiIDLZ005EcKomYYITtfsjn7dtOwHDOFy7IbPXKek2o= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.13/go.mod h1:2h/xGEowcW/g38g06g3KpRWDlT+OTfxxI0o1KqayAB8= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB8KfgAEuG0dc08Bkda7NU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk= +github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= +github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= diff --git a/apps/testapp/cmd/init.go b/apps/testapp/cmd/init.go index 8a046675d1..a6fd34ea73 100644 --- a/apps/testapp/cmd/init.go +++ b/apps/testapp/cmd/init.go @@ -58,7 +58,7 @@ func InitCmd() *cobra.Command { } } - proposerAddress, err := rollcmd.CreateSigner(&cfg, homePath, passphrase) + proposerAddress, err := rollcmd.CreateSigner(cmd.Context(), &cfg, homePath, passphrase) if err != nil { return err } diff --git a/apps/testapp/go.mod b/apps/testapp/go.mod index 289f97a580..43311766bf 100644 --- a/apps/testapp/go.mod +++ b/apps/testapp/go.mod @@ -17,6 +17,21 @@ require ( connectrpc.com/connect v1.19.1 // indirect connectrpc.com/grpcreflect v1.3.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect + github.com/aws/aws-sdk-go-v2 v1.41.4 // indirect + github.com/aws/aws-sdk-go-v2/config v1.32.12 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.19.12 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 // indirect + github.com/aws/aws-sdk-go-v2/service/kms v1.50.3 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 // indirect + github.com/aws/smithy-go v1.24.2 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/boltdb/bolt v1.3.1 // indirect diff --git a/apps/testapp/go.sum b/apps/testapp/go.sum index 626f4a9bc8..e9e6ce6e7f 100644 --- a/apps/testapp/go.sum +++ b/apps/testapp/go.sum @@ -261,6 +261,36 @@ github.com/apache/arrow/go/v14 v14.0.2/go.mod h1:u3fgh3EdgN/YQ8cVQRguVW3R+seMybF github.com/apache/thrift v0.17.0/go.mod h1:OLxhMRJxomX+1I/KUw03qoV3mMz16BwaKI+d4fPBx7Q= github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k= +github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= +github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0= +github.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g= +github.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8= +github.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 h1:CNXO7mvgThFGqOFgbNAP2nol2qAWBOGfqR/7tQlvLmc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 h1:tN6W/hg+pkM+tf9XDkWUbDEjGLb+raoBMFsTodcoYKw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 h1:2HvVAIq+YqgGotK6EkMf+KIEqTISmTYh5zLpYyeTo1Y= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20/go.mod h1:V4X406Y666khGa8ghKmphma/7C0DAtEQYhkq9z4vpbk= +github.com/aws/aws-sdk-go-v2/service/kms v1.50.3 h1:s/zDSG/a/Su9aX+v0Ld9cimUCdkr5FWPmBV8owaEbZY= +github.com/aws/aws-sdk-go-v2/service/kms v1.50.3/go.mod h1:/iSgiUor15ZuxFGQSTf3lA2FmKxFsQoc2tADOarQBSw= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 h1:0GFOLzEbOyZABS3PhYfBIx2rNBACYcKty+XGkTgw1ow= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.8/go.mod h1:LXypKvk85AROkKhOG6/YEcHFPoX+prKTowKnVdcaIxE= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 h1:kiIDLZ005EcKomYYITtfsjn7dtOwHDOFy7IbPXKek2o= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.13/go.mod h1:2h/xGEowcW/g38g06g3KpRWDlT+OTfxxI0o1KqayAB8= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB8KfgAEuG0dc08Bkda7NU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk= +github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= +github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= diff --git a/block/internal/submitting/da_submitter_integration_test.go b/block/internal/submitting/da_submitter_integration_test.go index fafe4f044c..b2c4efcd20 100644 --- a/block/internal/submitting/da_submitter_integration_test.go +++ b/block/internal/submitting/da_submitter_integration_test.go @@ -53,7 +53,7 @@ func TestDASubmitter_SubmitHeadersAndData_MarksInclusionAndUpdatesLastSubmitted( hdr1 := &types.SignedHeader{Header: types.Header{BaseHeader: types.BaseHeader{ChainID: gen.ChainID, Height: 1, Time: uint64(time.Now().UnixNano())}, AppHash: stateRoot, ProposerAddress: addr}, Signer: types.Signer{PubKey: pub, Address: addr}} bz1, err := types.DefaultAggregatorNodeSignatureBytesProvider(&hdr1.Header) require.NoError(t, err) - sig1, err := n.Sign(context.Background(), bz1) + sig1, err := n.Sign(t.Context(), bz1) require.NoError(t, err) hdr1.Signature = sig1 data1 := &types.Data{Metadata: &types.Metadata{ChainID: gen.ChainID, Height: 1, Time: uint64(time.Now().UnixNano())}, Txs: types.Txs{types.Tx("a")}} @@ -61,7 +61,7 @@ func TestDASubmitter_SubmitHeadersAndData_MarksInclusionAndUpdatesLastSubmitted( hdr2 := &types.SignedHeader{Header: types.Header{BaseHeader: types.BaseHeader{ChainID: gen.ChainID, Height: 2, Time: uint64(time.Now().Add(time.Second).UnixNano())}, AppHash: stateRoot, ProposerAddress: addr}, Signer: types.Signer{PubKey: pub, Address: addr}} bz2, err := types.DefaultAggregatorNodeSignatureBytesProvider(&hdr2.Header) require.NoError(t, err) - sig2, err := n.Sign(context.Background(), bz2) + sig2, err := n.Sign(t.Context(), bz2) require.NoError(t, err) hdr2.Signature = sig2 data2 := &types.Data{Metadata: &types.Metadata{ChainID: gen.ChainID, Height: 2, Time: uint64(time.Now().Add(time.Second).UnixNano())}, Txs: types.Txs{types.Tx("b")}} diff --git a/block/internal/submitting/da_submitter_test.go b/block/internal/submitting/da_submitter_test.go index defe7e4d73..d25786018b 100644 --- a/block/internal/submitting/da_submitter_test.go +++ b/block/internal/submitting/da_submitter_test.go @@ -175,7 +175,7 @@ func TestDASubmitter_SubmitHeaders_Success(t *testing.T) { for _, header := range []*types.SignedHeader{header1, header2} { bz, err := types.DefaultAggregatorNodeSignatureBytesProvider(&header.Header) require.NoError(t, err) - sig, err := signer.Sign(context.Background(), bz) + sig, err := signer.Sign(t.Context(), bz) require.NoError(t, err) header.Signature = sig } @@ -503,7 +503,7 @@ func TestDASubmitter_SignData(t *testing.T) { } // Create signed data - resultData, resultDataBz, err := submitter.signData(dataList, dataListBz, signer, gen) + resultData, resultDataBz, err := submitter.signData(t.Context(), dataList, dataListBz, signer, gen) require.NoError(t, err) // Should have 2 items (empty data skipped) @@ -542,7 +542,7 @@ func TestDASubmitter_SignData_NilSigner(t *testing.T) { } // Create signed data with nil signer - should fail - _, _, err := submitter.signData(dataList, dataListBz, nil, gen) + _, _, err := submitter.signData(t.Context(), dataList, dataListBz, nil, gen) require.Error(t, err) assert.Contains(t, err.Error(), "signer is nil") } diff --git a/block/internal/submitting/submitter_test.go b/block/internal/submitting/submitter_test.go index d19e9d3eb4..946fba0ab6 100644 --- a/block/internal/submitting/submitter_test.go +++ b/block/internal/submitting/submitter_test.go @@ -442,7 +442,9 @@ func (f *fakeDASubmitter) SubmitData(ctx context.Context, _ []*types.SignedData, // fakeSigner implements signer.Signer with deterministic behavior for tests. type fakeSigner struct{} -func (f *fakeSigner) Sign(msg []byte) ([]byte, error) { return append([]byte(nil), msg...), nil } +func (f *fakeSigner) Sign(_ context.Context, msg []byte) ([]byte, error) { + return append([]byte(nil), msg...), nil +} func (f *fakeSigner) GetPublic() (crypto.PubKey, error) { return nil, nil } func (f *fakeSigner) GetAddress() ([]byte, error) { return []byte("addr"), nil } diff --git a/block/internal/syncing/da_retriever_strict_test.go b/block/internal/syncing/da_retriever_strict_test.go index 4760463577..981a40614e 100644 --- a/block/internal/syncing/da_retriever_strict_test.go +++ b/block/internal/syncing/da_retriever_strict_test.go @@ -1,7 +1,6 @@ package syncing import ( - "context" "testing" "time" @@ -32,7 +31,7 @@ func TestDARetriever_StrictEnvelopeMode_Switch(t *testing.T) { // Sign it bz, err := types.DefaultAggregatorNodeSignatureBytesProvider(&legacyHeader.Header) require.NoError(t, err) - sig, err := signer.Sign(context.Background(), bz) + sig, err := signer.Sign(t.Context(), bz) require.NoError(t, err) legacyHeader.Signature = sig @@ -51,7 +50,7 @@ func TestDARetriever_StrictEnvelopeMode_Switch(t *testing.T) { // Sign content bz2, err := types.DefaultAggregatorNodeSignatureBytesProvider(&envelopeHeader.Header) require.NoError(t, err) - sig2, err := signer.Sign(context.Background(), bz2) + sig2, err := signer.Sign(t.Context(), bz2) require.NoError(t, err) envelopeHeader.Signature = sig2 @@ -62,7 +61,7 @@ func TestDARetriever_StrictEnvelopeMode_Switch(t *testing.T) { contentBytes, err := envelopeHeader.MarshalBinary() require.NoError(t, err) // Sign envelope - envSig, err := signer.Sign(context.Background(), contentBytes) + envSig, err := signer.Sign(t.Context(), contentBytes) require.NoError(t, err) // Marshal to envelope envelopeBlob, err := envelopeHeader.MarshalDAEnvelope(envSig) diff --git a/block/internal/syncing/da_retriever_test.go b/block/internal/syncing/da_retriever_test.go index a6922a3739..1daafe40ec 100644 --- a/block/internal/syncing/da_retriever_test.go +++ b/block/internal/syncing/da_retriever_test.go @@ -71,7 +71,7 @@ func makeSignedDataBytesWithTime(t *testing.T, chainID string, height uint64, pr // For DA SignedData, sign the Data payload bytes (matches DA submission logic) payload, _ := d.MarshalBinary() - sig, err := signer.Sign(context.Background(), payload) + sig, err := signer.Sign(t.Context(), payload) require.NoError(t, err) sd := &types.SignedData{Data: *d, Signature: sig, Signer: types.Signer{PubKey: pub, Address: proposer}} bin, err := sd.MarshalBinary() diff --git a/block/internal/syncing/p2p_handler_test.go b/block/internal/syncing/p2p_handler_test.go index 879dc089ee..8bffc31ede 100644 --- a/block/internal/syncing/p2p_handler_test.go +++ b/block/internal/syncing/p2p_handler_test.go @@ -51,7 +51,7 @@ func p2pMakeSignedHeader(t *testing.T, chainID string, height uint64, proposer [ } bz, err := types.DefaultAggregatorNodeSignatureBytesProvider(&hdr.Header) require.NoError(t, err, "failed to get signature bytes for header") - sig, err := signer.Sign(context.Background(), bz) + sig, err := signer.Sign(t.Context(), bz) require.NoError(t, err, "failed to sign header bytes") hdr.Signature = sig return &types.P2PSignedHeader{SignedHeader: hdr} @@ -140,7 +140,7 @@ func TestP2PHandler_ProcessHeight_EmitsEventWhenHeaderAndDataPresent(t *testing. header.DataHash = data.DACommitment() bz, err := types.DefaultAggregatorNodeSignatureBytesProvider(&header.Header) require.NoError(t, err) - sig, err := p.Signer.Sign(context.Background(), bz) + sig, err := p.Signer.Sign(t.Context(), bz) require.NoError(t, err) header.Signature = sig @@ -159,14 +159,14 @@ func TestP2PHandler_ProcessHeight_EmitsEventWhenHeaderAndDataPresent(t *testing. func TestP2PHandler_ProcessHeight_SkipsWhenDataMissing(t *testing.T) { p := setupP2P(t) - ctx := context.Background() + ctx := t.Context() header := p2pMakeSignedHeader(t, p.Genesis.ChainID, 7, p.ProposerAddr, p.ProposerPub, p.Signer) data := &types.P2PData{Data: makeData(p.Genesis.ChainID, 7, 1)} header.DataHash = data.DACommitment() bz, err := types.DefaultAggregatorNodeSignatureBytesProvider(&header.Header) require.NoError(t, err) - sig, err := p.Signer.Sign(context.Background(), bz) + sig, err := p.Signer.Sign(ctx, bz) require.NoError(t, err) header.Signature = sig @@ -217,7 +217,7 @@ func TestP2PHandler_ProcessHeight_SkipsOnProposerMismatch(t *testing.T) { func TestP2PHandler_ProcessedHeightSkipsPreviouslyHandledBlocks(t *testing.T) { p := setupP2P(t) - ctx := context.Background() + ctx := t.Context() // Mark up to height 5 as processed. p.Handler.SetProcessedHeight(5) @@ -236,7 +236,7 @@ func TestP2PHandler_ProcessedHeightSkipsPreviouslyHandledBlocks(t *testing.T) { header.DataHash = data.DACommitment() bz, err := types.DefaultAggregatorNodeSignatureBytesProvider(&header.Header) require.NoError(t, err) - sig, err := p.Signer.Sign(context.Background(), bz) + sig, err := p.Signer.Sign(ctx, bz) require.NoError(t, err) header.Signature = sig @@ -252,14 +252,14 @@ func TestP2PHandler_ProcessedHeightSkipsPreviouslyHandledBlocks(t *testing.T) { func TestP2PHandler_SetProcessedHeightPreventsDuplicates(t *testing.T) { p := setupP2P(t) - ctx := context.Background() + ctx := t.Context() header := p2pMakeSignedHeader(t, p.Genesis.ChainID, 8, p.ProposerAddr, p.ProposerPub, p.Signer) data := &types.P2PData{Data: makeData(p.Genesis.ChainID, 8, 0)} header.DataHash = data.DACommitment() bz, err := types.DefaultAggregatorNodeSignatureBytesProvider(&header.Header) require.NoError(t, err) - sig, err := p.Signer.Sign(context.Background(), bz) + sig, err := p.Signer.Sign(ctx, bz) require.NoError(t, err) header.Signature = sig diff --git a/block/internal/syncing/syncer_test.go b/block/internal/syncing/syncer_test.go index c42024a4b7..c35efda25b 100644 --- a/block/internal/syncing/syncer_test.go +++ b/block/internal/syncing/syncer_test.go @@ -78,7 +78,7 @@ func makeSignedHeaderBytes( } bz, err := types.DefaultAggregatorNodeSignatureBytesProvider(&hdr.Header) require.NoError(tb, err) - sig, err := signer.Sign(context.Background(), bz) + sig, err := signer.Sign(tb.Context(), bz) require.NoError(tb, err) hdr.Signature = sig bin, err := hdr.MarshalBinary() diff --git a/go.mod b/go.mod index bc25b70e03..bf2431bee7 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,10 @@ retract v0.12.0 // Published by accident require ( connectrpc.com/connect v1.19.1 connectrpc.com/grpcreflect v1.3.0 + github.com/aws/aws-sdk-go-v2 v1.41.4 + github.com/aws/aws-sdk-go-v2/config v1.32.12 + github.com/aws/aws-sdk-go-v2/service/kms v1.50.3 + github.com/aws/smithy-go v1.24.2 github.com/celestiaorg/go-header v0.8.1 github.com/celestiaorg/go-square/merkle v0.0.0-20240627094109-7d01436067a3 github.com/celestiaorg/go-square/v3 v3.0.2 @@ -45,8 +49,6 @@ require ( require ( github.com/armon/go-metrics v0.4.1 // indirect - github.com/aws/aws-sdk-go-v2 v1.41.4 // indirect - github.com/aws/aws-sdk-go-v2/config v1.32.12 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.19.12 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 // indirect @@ -54,12 +56,10 @@ require ( github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 // indirect - github.com/aws/aws-sdk-go-v2/service/kms v1.50.3 // indirect github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 // indirect - github.com/aws/smithy-go v1.24.2 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/boltdb/bolt v1.3.1 // indirect diff --git a/pkg/cmd/init.go b/pkg/cmd/init.go index 8a231b8b4d..479684af99 100644 --- a/pkg/cmd/init.go +++ b/pkg/cmd/init.go @@ -3,10 +3,8 @@ package cmd import ( "context" "fmt" - "os" "path/filepath" - rollconf "github.com/evstack/ev-node/pkg/config" rollconf "github.com/evstack/ev-node/pkg/config" "github.com/evstack/ev-node/pkg/hash" "github.com/evstack/ev-node/pkg/p2p/key" @@ -14,7 +12,7 @@ import ( ) // CreateSigner sets up the signer configuration and creates necessary files -func CreateSigner(config *rollconf.Config, homePath string, passphrase string) ([]byte, error) { +func CreateSigner(ctx context.Context, config *rollconf.Config, homePath string, passphrase string) ([]byte, error) { if !config.Node.Aggregator { return nil, nil } @@ -24,7 +22,7 @@ func CreateSigner(config *rollconf.Config, homePath string, passphrase string) ( config.Signer.SignerPath = signerDir } - signer, err := factory.NewSigner(context.Background(), config, passphrase) + signer, err := factory.NewSignerForInit(ctx, config, passphrase) if err != nil { return nil, fmt.Errorf("failed to initialize signer via factory: %w", err) } diff --git a/pkg/cmd/init_test.go b/pkg/cmd/init_test.go index 7bef0c9a41..c3ee9a7c30 100644 --- a/pkg/cmd/init_test.go +++ b/pkg/cmd/init_test.go @@ -24,7 +24,7 @@ func TestCreateSigner(t *testing.T) { Signer: rollconf.SignerConfig{SignerType: "file"}, Node: rollconf.NodeConfig{Aggregator: true}, } - _, err := cmd.CreateSigner(cfg, tmpDir, "") + _, err := cmd.CreateSigner(t.Context(), cfg, tmpDir, "") require.Error(err) assert.Contains(err.Error(), "passphrase is required") }) @@ -36,7 +36,7 @@ func TestCreateSigner(t *testing.T) { Signer: rollconf.SignerConfig{SignerType: "file"}, Node: rollconf.NodeConfig{Aggregator: true}, } - addr, err := cmd.CreateSigner(cfg, tmpDir, "testpass") + addr, err := cmd.CreateSigner(t.Context(), cfg, tmpDir, "testpass") require.NoError(err) assert.NotNil(addr) assert.NotEmpty(addr) @@ -46,16 +46,16 @@ func TestCreateSigner(t *testing.T) { assert.NoError(err, "signer file should exist") }) - // Case 3: Non-File signer, Aggregator -> Error (Remote signer not implemented) + // Case 3: Non-File signer, Aggregator -> Error (unknown signer type) t.Run("RemoteSigner_Aggregator", func(t *testing.T) { tmpDir := t.TempDir() cfg := &rollconf.Config{ Signer: rollconf.SignerConfig{SignerType: "remote"}, Node: rollconf.NodeConfig{Aggregator: true}, } - _, err := cmd.CreateSigner(cfg, tmpDir, "") + _, err := cmd.CreateSigner(t.Context(), cfg, tmpDir, "") require.Error(err) - assert.Contains(err.Error(), "remote signer not implemented") + assert.Contains(err.Error(), "unknown signer type") }) // Case 4: Not Aggregator -> No-op (returns nil, nil) @@ -65,7 +65,7 @@ func TestCreateSigner(t *testing.T) { Signer: rollconf.SignerConfig{SignerType: "file"}, // Signer type doesn't matter here Node: rollconf.NodeConfig{Aggregator: false}, } - addr, err := cmd.CreateSigner(cfg, tmpDir, "testpass") + addr, err := cmd.CreateSigner(t.Context(), cfg, tmpDir, "testpass") require.NoError(err) assert.Nil(addr) }) @@ -83,7 +83,7 @@ func TestCreateSigner(t *testing.T) { Signer: rollconf.SignerConfig{SignerType: "file"}, Node: rollconf.NodeConfig{Aggregator: true}, } - _, err = cmd.CreateSigner(cfg, tmpDir, "testpass") + _, err = cmd.CreateSigner(t.Context(), cfg, tmpDir, "testpass") require.Error(err) assert.Contains(err.Error(), "failed to create signer directory") }) diff --git a/pkg/cmd/run_node.go b/pkg/cmd/run_node.go index fde7485278..f2f49748e4 100644 --- a/pkg/cmd/run_node.go +++ b/pkg/cmd/run_node.go @@ -6,7 +6,6 @@ import ( "fmt" "os" "os/signal" - "path/filepath" "runtime" "strings" "syscall" @@ -136,9 +135,9 @@ func StartNode( var err error signer, err = factory.NewSigner(ctx, &nodeConfig, passphrase) if err != nil { - return fmt.Errorf("failed to initialize signer via factory: %w", err) + return fmt.Errorf("initialize signer via factory: %w", err) } - + if nodeConfig.Signer.SignerType == "awskms" { logger.Info().Msg("initialized AWS KMS signer via factory") } diff --git a/pkg/config/config.go b/pkg/config/config.go index f88e28c6d9..d16f0edbe0 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -304,8 +304,8 @@ type P2PConfig struct { // SignerConfig contains all signer configuration parameters type SignerConfig struct { - SignerType string `mapstructure:"signer_type" yaml:"signer_type" comment:"Type of remote signer to use (file, grpc, awskms)"` - SignerPath string `mapstructure:"signer_path" yaml:"signer_path" comment:"Path to the signer file or address"` + SignerType string `mapstructure:"signer_type" yaml:"signer_type" comment:"Type of remote signer to use (file, awskms)"` + SignerPath string `mapstructure:"signer_path" yaml:"signer_path" comment:"Path to the signer file or address"` KmsKeyID string `mapstructure:"kms_key_id" yaml:"kms_key_id" comment:"AWS KMS Key ID or ARN for awskms signer"` KmsRegion string `mapstructure:"kms_region" yaml:"kms_region" comment:"AWS Region for awskms signer"` KmsProfile string `mapstructure:"kms_profile" yaml:"kms_profile" comment:"AWS Profile for awskms signer"` diff --git a/pkg/config/defaults.go b/pkg/config/defaults.go index afde0a67ab..d65aff6520 100644 --- a/pkg/config/defaults.go +++ b/pkg/config/defaults.go @@ -91,14 +91,14 @@ func DefaultConfig() Config { Trace: false, }, Signer: SignerConfig{ - SignerType: "file", - SignerPath: "config", - KmsKeyID: "", - KmsRegion: "", - KmsProfile: "", - KmsTimeout: DurationWrapper{10 * time.Second}, - KmsMaxRetries: 3, - KmsCacheTTL: DurationWrapper{0}, + SignerType: "file", + SignerPath: "config", + KmsKeyID: "", + KmsRegion: "", + KmsProfile: "", + KmsTimeout: DurationWrapper{10 * time.Second}, + KmsMaxRetries: 3, + KmsCacheTTL: DurationWrapper{0}, }, RPC: RPCConfig{ Address: "127.0.0.1:7331", diff --git a/pkg/signer/aws/signer.go b/pkg/signer/aws/signer.go index 22cb9973ca..7622208d85 100644 --- a/pkg/signer/aws/signer.go +++ b/pkg/signer/aws/signer.go @@ -7,7 +7,9 @@ import ( "crypto/ed25519" "crypto/sha256" "crypto/x509" + "errors" "fmt" + "net" "sync" "time" @@ -15,6 +17,7 @@ import ( awsconfig "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/kms" "github.com/aws/aws-sdk-go-v2/service/kms/types" + "github.com/aws/smithy-go" "github.com/libp2p/go-libp2p/core/crypto" "github.com/evstack/ev-node/pkg/signer" @@ -38,26 +41,11 @@ type Options struct { CacheTTL time.Duration } -func (o *Options) timeout() time.Duration { - if o != nil && o.Timeout > 0 { - return o.Timeout - } - return 10 * time.Second -} +func (o *Options) timeout() time.Duration { return o.Timeout } -func (o *Options) maxRetries() int { - if o != nil && o.MaxRetries > 0 { - return o.MaxRetries - } - return 3 -} +func (o *Options) maxRetries() int { return o.MaxRetries } -func (o *Options) cacheTTL() time.Duration { - if o != nil { - return o.CacheTTL - } - return 0 -} +func (o *Options) cacheTTL() time.Duration { return o.CacheTTL } // KmsSigner implements the signer.Signer interface using AWS KMS. type KmsSigner struct { @@ -102,10 +90,19 @@ func NewKmsSignerFromClient(ctx context.Context, client KMSClient, keyID string, if keyID == "" { return nil, fmt.Errorf("aws kms key ID is required") } + if client == nil { + return nil, fmt.Errorf("aws kms client is required") + } - o := Options{} + o := Options{Timeout: 5 * time.Second, MaxRetries: 3, CacheTTL: 0} if opts != nil { - o = *opts + if opts.Timeout > 0 { + o.Timeout = opts.Timeout + } + if opts.MaxRetries >= 0 { + o.MaxRetries = opts.MaxRetries + } + o.CacheTTL = opts.CacheTTL } s := &KmsSigner{ @@ -170,8 +167,9 @@ func (s *KmsSigner) Sign(ctx context.Context, message []byte) ([]byte, error) { var lastErr error maxRetries := s.opts.maxRetries() timeout := s.opts.timeout() + maxAttempts := maxRetries + 1 - for attempt := 0; attempt < maxRetries; attempt++ { + for attempt := 0; attempt < maxAttempts; attempt++ { if attempt > 0 { // Exponential backoff: 100ms, 200ms, 400ms, ... backoff := time.Duration(100< Date: Thu, 19 Mar 2026 14:09:53 +0100 Subject: [PATCH 4/7] Better doc, config and tests --- docs/.vitepress/config.ts | 4 + docs/guides/operations/aws-kms-signer.md | 80 +++++++++++++ execution/evm/test/go.sum | 30 +++++ pkg/cmd/init.go | 4 +- pkg/cmd/run_node.go | 9 +- pkg/config/config.go | 4 - pkg/config/config_test.go | 3 +- pkg/config/defaults.go | 1 - pkg/signer/aws/README.md | 31 +++++ pkg/signer/aws/signer.go | 44 +------ pkg/signer/aws/signer_test.go | 52 ++------- pkg/signer/{factory => }/factory.go | 10 +- pkg/signer/factory_test.go | 94 +++++++++++++++ pkg/signer/file/file_signer_test.go | 16 +-- pkg/signer/file/local.go | 6 +- test/e2e/evm_aws_kms_e2e_test.go | 142 +++++++++++++++++++++++ test/e2e/go.mod | 15 +++ test/e2e/go.sum | 30 +++++ 18 files changed, 461 insertions(+), 114 deletions(-) create mode 100644 docs/guides/operations/aws-kms-signer.md create mode 100644 pkg/signer/aws/README.md rename pkg/signer/{factory => }/factory.go (88%) create mode 100644 pkg/signer/factory_test.go create mode 100644 test/e2e/evm_aws_kms_e2e_test.go diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 9b1def7546..ae97a39a8c 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -317,6 +317,10 @@ function sidebarHome() { text: "Metrics", link: "/guides/metrics", }, + { + text: "Use AWS KMS signer", + link: "/guides/operations/aws-kms-signer", + }, { text: "Use IBC token (TIA) as gas token in your chain", link: "/guides/use-tia-for-gas", diff --git a/docs/guides/operations/aws-kms-signer.md b/docs/guides/operations/aws-kms-signer.md new file mode 100644 index 0000000000..c5f5804da8 --- /dev/null +++ b/docs/guides/operations/aws-kms-signer.md @@ -0,0 +1,80 @@ +# Use AWS KMS Signer + +Use this guide to run `ev-node` with an AWS KMS-backed signer (`signer_type: awskms`) instead of a local key file. + +## Prerequisites + +- An AWS KMS **asymmetric** key with: + - `KeyUsage: SIGN_VERIFY` + - `KeySpec: ECC_NIST_EDWARDS25519` +- IAM permissions for initial key creation/management (example policy): + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "AllowKeyCreation", + "Effect": "Allow", + "Action": [ + "kms:CreateKey", + "kms:TagResource", + "kms:EnableKey", + "kms:PutKeyPolicy", + "kms:GetPublicKey", + "kms:Sign", + "kms:ListKeys", + "kms:ListAliases" + ], + "Resource": "*" + } + ] +} +``` + +- Runtime IAM permissions for `ev-node` (minimum): + - `kms:GetPublicKey` + - `kms:Sign` +- AWS credentials available to the node process (IAM role, env vars, or shared profile). + +## 1. Create an ED25519 KMS key (example) + +```bash +aws kms create-key \ + --description "ev-node signer" \ + --key-usage SIGN_VERIFY \ + --key-spec ECC_NIST_EDWARDS25519 +``` + +Copy the returned key ARN (or key ID). You can also create an alias and use that. + +## 2. Configure `evnode.yaml` + +```yaml +signer: + signer_type: "awskms" + kms_key_id: "arn:aws:kms:us-east-1:123456789012:key/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + kms_region: "us-east-1" # optional but recommended + kms_profile: "prod" # optional; omit when using IAM role/env creds + kms_timeout: "10s" # must be > 0 + kms_max_retries: 3 # must be >= 0 +``` + +## 3. Start as an aggregator + +```bash +evnode start --evnode.node.aggregator +``` + +You should see a startup log line: + +`initialized AWS KMS signer via factory` + +## Troubleshooting + +- `evnode.signer.kms_key_id is required when signer_type is awskms`: + Set `signer.kms_key_id`. +- `unsupported key type from KMS: expected ed25519`: + Recreate the key as `ECC_NIST_EDWARDS25519`. +- `KMS Sign failed ...`: + Check IAM permissions, key policy, region/profile, and network access to AWS KMS. diff --git a/execution/evm/test/go.sum b/execution/evm/test/go.sum index dd6b5a3a81..ecf59d7d28 100644 --- a/execution/evm/test/go.sum +++ b/execution/evm/test/go.sum @@ -54,6 +54,36 @@ github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kd github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/avast/retry-go/v4 v4.6.1 h1:VkOLRubHdisGrHnTu89g08aQEWEgRU7LVEop3GbIcMk= github.com/avast/retry-go/v4 v4.6.1/go.mod h1:V6oF8njAwxJ5gRo1Q7Cxab24xs5NCWZBeaHHBklR8mA= +github.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k= +github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= +github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0= +github.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g= +github.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8= +github.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 h1:CNXO7mvgThFGqOFgbNAP2nol2qAWBOGfqR/7tQlvLmc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 h1:tN6W/hg+pkM+tf9XDkWUbDEjGLb+raoBMFsTodcoYKw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 h1:2HvVAIq+YqgGotK6EkMf+KIEqTISmTYh5zLpYyeTo1Y= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20/go.mod h1:V4X406Y666khGa8ghKmphma/7C0DAtEQYhkq9z4vpbk= +github.com/aws/aws-sdk-go-v2/service/kms v1.50.3 h1:s/zDSG/a/Su9aX+v0Ld9cimUCdkr5FWPmBV8owaEbZY= +github.com/aws/aws-sdk-go-v2/service/kms v1.50.3/go.mod h1:/iSgiUor15ZuxFGQSTf3lA2FmKxFsQoc2tADOarQBSw= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 h1:0GFOLzEbOyZABS3PhYfBIx2rNBACYcKty+XGkTgw1ow= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.8/go.mod h1:LXypKvk85AROkKhOG6/YEcHFPoX+prKTowKnVdcaIxE= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 h1:kiIDLZ005EcKomYYITtfsjn7dtOwHDOFy7IbPXKek2o= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.13/go.mod h1:2h/xGEowcW/g38g06g3KpRWDlT+OTfxxI0o1KqayAB8= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB8KfgAEuG0dc08Bkda7NU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk= +github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= +github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= diff --git a/pkg/cmd/init.go b/pkg/cmd/init.go index 479684af99..6ea5558fb3 100644 --- a/pkg/cmd/init.go +++ b/pkg/cmd/init.go @@ -8,7 +8,7 @@ import ( rollconf "github.com/evstack/ev-node/pkg/config" "github.com/evstack/ev-node/pkg/hash" "github.com/evstack/ev-node/pkg/p2p/key" - "github.com/evstack/ev-node/pkg/signer/factory" + "github.com/evstack/ev-node/pkg/signer" ) // CreateSigner sets up the signer configuration and creates necessary files @@ -22,7 +22,7 @@ func CreateSigner(ctx context.Context, config *rollconf.Config, homePath string, config.Signer.SignerPath = signerDir } - signer, err := factory.NewSignerForInit(ctx, config, passphrase) + signer, err := signer.NewSignerForInit(ctx, config, passphrase) if err != nil { return nil, fmt.Errorf("failed to initialize signer via factory: %w", err) } diff --git a/pkg/cmd/run_node.go b/pkg/cmd/run_node.go index f2f49748e4..f2c84280de 100644 --- a/pkg/cmd/run_node.go +++ b/pkg/cmd/run_node.go @@ -24,8 +24,7 @@ import ( genesispkg "github.com/evstack/ev-node/pkg/genesis" "github.com/evstack/ev-node/pkg/p2p" "github.com/evstack/ev-node/pkg/p2p/key" - "github.com/evstack/ev-node/pkg/signer" - "github.com/evstack/ev-node/pkg/signer/factory" + pkgsigner "github.com/evstack/ev-node/pkg/signer" "github.com/evstack/ev-node/pkg/telemetry" ) @@ -106,9 +105,9 @@ func StartNode( }() } - // Validate and load signer first (before attempting DA connection, which may fail + // Validate and load pkgsigner first (before attempting DA connection, which may fail // eagerly over WebSocket if no DA server is running). - var signer signer.Signer + var signer pkgsigner.Signer if nodeConfig.Node.Aggregator && !nodeConfig.Node.BasedSequencer { passphrase := "" if nodeConfig.Signer.SignerType == "file" { @@ -133,7 +132,7 @@ func StartNode( } var err error - signer, err = factory.NewSigner(ctx, &nodeConfig, passphrase) + signer, err = pkgsigner.NewSigner(ctx, &nodeConfig, passphrase) if err != nil { return fmt.Errorf("initialize signer via factory: %w", err) } diff --git a/pkg/config/config.go b/pkg/config/config.go index d16f0edbe0..5b40a9e1c3 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -150,8 +150,6 @@ const ( FlagSignerKmsTimeout = FlagPrefixEvnode + "signer.kms_timeout" // FlagSignerKmsMaxRetries is a flag for specifying the KMS sign max retries FlagSignerKmsMaxRetries = FlagPrefixEvnode + "signer.kms_max_retries" - // FlagSignerKmsCacheTTL is a flag for specifying the KMS public key cache TTL - FlagSignerKmsCacheTTL = FlagPrefixEvnode + "signer.kms_cache_ttl" // FlagSignerPassphraseFile is a flag for specifying the file containing the signer passphrase FlagSignerPassphraseFile = FlagPrefixEvnode + "signer.passphrase_file" @@ -311,7 +309,6 @@ type SignerConfig struct { KmsProfile string `mapstructure:"kms_profile" yaml:"kms_profile" comment:"AWS Profile for awskms signer"` KmsTimeout DurationWrapper `mapstructure:"kms_timeout" yaml:"kms_timeout" comment:"Timeout for individual AWS KMS Sign requests"` KmsMaxRetries int `mapstructure:"kms_max_retries" yaml:"kms_max_retries" comment:"Maximum number of retries for transient AWS KMS failures"` - KmsCacheTTL DurationWrapper `mapstructure:"kms_cache_ttl" yaml:"kms_cache_ttl" comment:"Time-to-live for caching the AWS KMS public key (0 means cache forever)"` } // RPCConfig contains all RPC server configuration parameters @@ -584,7 +581,6 @@ func AddFlags(cmd *cobra.Command) { cmd.Flags().String(FlagSignerKmsProfile, def.Signer.KmsProfile, "AWS Profile for awskms signer") cmd.Flags().Duration(FlagSignerKmsTimeout, def.Signer.KmsTimeout.Duration, "Timeout for individual AWS KMS Sign requests") cmd.Flags().Int(FlagSignerKmsMaxRetries, def.Signer.KmsMaxRetries, "Maximum number of retries for transient AWS KMS failures") - cmd.Flags().Duration(FlagSignerKmsCacheTTL, def.Signer.KmsCacheTTL.Duration, "Time-to-live for caching the AWS KMS public key (0 means cache forever)") cmd.Flags().String(FlagSignerPassphraseFile, "", "path to file containing the signer passphrase (required for file signer and if aggregator is enabled)") cmd.MarkFlagsMutuallyExclusive(FlagLight, FlagAggregator) diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 3d82688553..a191ecf6af 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -112,7 +112,6 @@ func TestAddFlags(t *testing.T) { assertFlagValue(t, flags, FlagSignerKmsProfile, DefaultConfig().Signer.KmsProfile) assertFlagValue(t, flags, FlagSignerKmsTimeout, DefaultConfig().Signer.KmsTimeout.Duration) assertFlagValue(t, flags, FlagSignerKmsMaxRetries, DefaultConfig().Signer.KmsMaxRetries) - assertFlagValue(t, flags, FlagSignerKmsCacheTTL, DefaultConfig().Signer.KmsCacheTTL.Duration) // RPC flags assertFlagValue(t, flags, FlagRPCAddress, DefaultConfig().RPC.Address) @@ -124,7 +123,7 @@ func TestAddFlags(t *testing.T) { assertFlagValue(t, flags, FlagPruningInterval, DefaultConfig().Pruning.Interval.Duration) // Count the number of flags we're explicitly checking - expectedFlagCount := 73 // Update this number if you add more flag checks above + expectedFlagCount := 72 // Update this number if you add more flag checks above // Get the actual number of flags (both regular and persistent) actualFlagCount := 0 diff --git a/pkg/config/defaults.go b/pkg/config/defaults.go index d65aff6520..30903c3e65 100644 --- a/pkg/config/defaults.go +++ b/pkg/config/defaults.go @@ -98,7 +98,6 @@ func DefaultConfig() Config { KmsProfile: "", KmsTimeout: DurationWrapper{10 * time.Second}, KmsMaxRetries: 3, - KmsCacheTTL: DurationWrapper{0}, }, RPC: RPCConfig{ Address: "127.0.0.1:7331", diff --git a/pkg/signer/aws/README.md b/pkg/signer/aws/README.md new file mode 100644 index 0000000000..d5a8234d22 --- /dev/null +++ b/pkg/signer/aws/README.md @@ -0,0 +1,31 @@ +# AWS KMS Signer + +This package implements `signer.Signer` using AWS KMS. + +It uses KMS for `Sign` operations and caches the public key/address in memory after initialization. + +## Requirements + +- AWS credentials must be available via the standard AWS SDK credential chain. +- The configured KMS key must be an asymmetric **Ed25519** key. + +## Configuration + +Set `evnode.signer.signer_type` to `awskms` and provide at least `kms_key_id`. + +Example: + +```yaml +signer: + signer_type: awskms + kms_key_id: arn:aws:kms:eu-central-1:123456789012:key/00000000-0000-0000-0000-000000000000 + kms_region: eu-central-1 + kms_profile: default + kms_timeout: 1s + kms_max_retries: 3 +``` + +## Notes + +- `kms_timeout` is the timeout per KMS Sign request. +- `kms_max_retries` controls retries for transient KMS/API/network failures. diff --git a/pkg/signer/aws/signer.go b/pkg/signer/aws/signer.go index 7622208d85..f1b6f1bec5 100644 --- a/pkg/signer/aws/signer.go +++ b/pkg/signer/aws/signer.go @@ -19,8 +19,6 @@ import ( "github.com/aws/aws-sdk-go-v2/service/kms/types" "github.com/aws/smithy-go" "github.com/libp2p/go-libp2p/core/crypto" - - "github.com/evstack/ev-node/pkg/signer" ) // KMSClient is the subset of the AWS KMS client API that KmsSigner needs. @@ -36,17 +34,12 @@ type Options struct { Timeout time.Duration // MaxRetries for transient KMS failures during Sign. Default: 3. MaxRetries int - // CacheTTL controls how long the public key is cached before being - // re-fetched from KMS. 0 (default) means cache forever. - CacheTTL time.Duration } func (o *Options) timeout() time.Duration { return o.Timeout } func (o *Options) maxRetries() int { return o.MaxRetries } -func (o *Options) cacheTTL() time.Duration { return o.CacheTTL } - // KmsSigner implements the signer.Signer interface using AWS KMS. type KmsSigner struct { client KMSClient @@ -55,11 +48,8 @@ type KmsSigner struct { mu sync.RWMutex pubKey crypto.PubKey address []byte - cacheAt time.Time } -var _ signer.Signer = (*KmsSigner)(nil) - // NewKmsSigner creates a new Signer backed by an AWS KMS Ed25519 key. // It uses the standard AWS credential chain (env vars, ~/.aws/credentials, IAM roles, etc.). func NewKmsSigner(ctx context.Context, region string, profile string, keyID string, opts *Options) (*KmsSigner, error) { @@ -67,7 +57,7 @@ func NewKmsSigner(ctx context.Context, region string, profile string, keyID stri return nil, fmt.Errorf("aws kms key ID is required") } - cfgOpts := []func(*awsconfig.LoadOptions) error{} + var cfgOpts []func(*awsconfig.LoadOptions) error if region != "" { cfgOpts = append(cfgOpts, awsconfig.WithRegion(region)) } @@ -81,12 +71,12 @@ func NewKmsSigner(ctx context.Context, region string, profile string, keyID stri } client := kms.NewFromConfig(cfg) - return NewKmsSignerFromClient(ctx, client, keyID, opts) + return kmsSignerFromClient(ctx, client, keyID, opts) } -// NewKmsSignerFromClient creates a KmsSigner from an existing KMS client. +// kmsSignerFromClient creates a KmsSigner from an existing KMS client. // Useful for testing with a mock client. -func NewKmsSignerFromClient(ctx context.Context, client KMSClient, keyID string, opts *Options) (*KmsSigner, error) { +func kmsSignerFromClient(ctx context.Context, client KMSClient, keyID string, opts *Options) (*KmsSigner, error) { if keyID == "" { return nil, fmt.Errorf("aws kms key ID is required") } @@ -94,7 +84,7 @@ func NewKmsSignerFromClient(ctx context.Context, client KMSClient, keyID string, return nil, fmt.Errorf("aws kms client is required") } - o := Options{Timeout: 5 * time.Second, MaxRetries: 3, CacheTTL: 0} + o := Options{Timeout: 1 * time.Second, MaxRetries: 3} if opts != nil { if opts.Timeout > 0 { o.Timeout = opts.Timeout @@ -102,7 +92,6 @@ func NewKmsSignerFromClient(ctx context.Context, client KMSClient, keyID string, if opts.MaxRetries >= 0 { o.MaxRetries = opts.MaxRetries } - o.CacheTTL = opts.CacheTTL } s := &KmsSigner{ @@ -156,7 +145,6 @@ func (s *KmsSigner) fetchPublicKey(ctx context.Context) error { s.pubKey = cryptoPubKey s.address = address[:] - s.cacheAt = time.Now() return nil } @@ -203,32 +191,12 @@ func (s *KmsSigner) Sign(ctx context.Context, message []byte) ([]byte, error) { return nil, fmt.Errorf("KMS Sign failed after %d attempts: %w", maxAttempts, lastErr) } -// GetPublic returns the cached public key, optionally refreshing if cache TTL -// has expired. +// GetPublic returns the cached public key. func (s *KmsSigner) GetPublic() (crypto.PubKey, error) { - ttl := s.opts.cacheTTL() - s.mu.RLock() pubKey := s.pubKey - expired := ttl > 0 && time.Since(s.cacheAt) > ttl s.mu.RUnlock() - if expired { - refreshCtx, cancel := context.WithTimeout(context.Background(), s.opts.timeout()) - err := s.fetchPublicKey(refreshCtx) - cancel() - if err != nil { - // If refresh fails, return the stale cached key - if pubKey != nil { - return pubKey, nil - } - return nil, fmt.Errorf("failed to refresh public key: %w", err) - } - s.mu.RLock() - pubKey = s.pubKey - s.mu.RUnlock() - } - if pubKey == nil { return nil, fmt.Errorf("public key not loaded") } diff --git a/pkg/signer/aws/signer_test.go b/pkg/signer/aws/signer_test.go index 19787694af..4a3e79673a 100644 --- a/pkg/signer/aws/signer_test.go +++ b/pkg/signer/aws/signer_test.go @@ -7,7 +7,6 @@ import ( "fmt" "sync/atomic" "testing" - "time" "github.com/aws/aws-sdk-go-v2/service/kms" "github.com/aws/aws-sdk-go-v2/service/kms/types" @@ -55,7 +54,7 @@ func TestNewKmsSignerFromClient_Success(t *testing.T) { _, der := generateTestEd25519DER(t) mock := &mockKMSClient{pubKeyDER: der} - s, err := NewKmsSignerFromClient(context.Background(), mock, "arn:aws:kms:us-east-1:123456789012:key/test-key-id", nil) + s, err := kmsSignerFromClient(context.Background(), mock, "arn:aws:kms:us-east-1:123456789012:key/test-key-id", nil) require.NoError(t, err) require.NotNil(t, s) @@ -71,13 +70,13 @@ func TestNewKmsSignerFromClient_Success(t *testing.T) { } func TestNewKmsSignerFromClient_EmptyKeyID(t *testing.T) { - _, err := NewKmsSignerFromClient(context.Background(), &mockKMSClient{}, "", nil) + _, err := kmsSignerFromClient(context.Background(), &mockKMSClient{}, "", nil) require.Error(t, err) assert.Contains(t, err.Error(), "key ID is required") } func TestNewKmsSignerFromClient_NilClient(t *testing.T) { - _, err := NewKmsSignerFromClient(context.Background(), nil, "test-key", nil) + _, err := kmsSignerFromClient(context.Background(), nil, "test-key", nil) require.Error(t, err) assert.Contains(t, err.Error(), "client is required") } @@ -89,7 +88,7 @@ func TestNewKmsSignerFromClient_GetPublicKeyFails(t *testing.T) { }, } - _, err := NewKmsSignerFromClient(context.Background(), mock, "test-key", nil) + _, err := kmsSignerFromClient(context.Background(), mock, "test-key", nil) require.Error(t, err) assert.Contains(t, err.Error(), "access denied") } @@ -107,7 +106,7 @@ func TestSign_Success(t *testing.T) { }, } - s, err := NewKmsSignerFromClient(context.Background(), mock, "test-key", nil) + s, err := kmsSignerFromClient(context.Background(), mock, "test-key", nil) require.NoError(t, err) sig, err := s.Sign(context.Background(), []byte("hello world")) @@ -127,7 +126,7 @@ func TestSign_KMSFailure(t *testing.T) { }, } - s, err := NewKmsSignerFromClient(context.Background(), mock, "test-key", nil) + s, err := kmsSignerFromClient(context.Background(), mock, "test-key", nil) require.NoError(t, err) _, err = s.Sign(context.Background(), []byte("hello world")) @@ -148,7 +147,7 @@ func TestSign_MaxRetriesZero_DisablesRetries(t *testing.T) { }, } - s, err := NewKmsSignerFromClient(context.Background(), mock, "test-key", &Options{MaxRetries: 0}) + s, err := kmsSignerFromClient(context.Background(), mock, "test-key", &Options{MaxRetries: 0}) require.NoError(t, err) _, err = s.Sign(context.Background(), []byte("hello world")) @@ -168,7 +167,7 @@ func TestSign_NonRetryableError_NoRetries(t *testing.T) { }, } - s, err := NewKmsSignerFromClient(context.Background(), mock, "test-key", &Options{MaxRetries: 3}) + s, err := kmsSignerFromClient(context.Background(), mock, "test-key", &Options{MaxRetries: 3}) require.NoError(t, err) _, err = s.Sign(context.Background(), []byte("hello world")) @@ -181,7 +180,7 @@ func TestGetPublic_Cached(t *testing.T) { pub, der := generateTestEd25519DER(t) mock := &mockKMSClient{pubKeyDER: der} - s, err := NewKmsSignerFromClient(context.Background(), mock, "test-key", nil) + s, err := kmsSignerFromClient(context.Background(), mock, "test-key", nil) require.NoError(t, err) cryptoPub, err := s.GetPublic() @@ -196,7 +195,7 @@ func TestGetAddress_Deterministic(t *testing.T) { _, der := generateTestEd25519DER(t) mock := &mockKMSClient{pubKeyDER: der} - s, err := NewKmsSignerFromClient(context.Background(), mock, "test-key", nil) + s, err := kmsSignerFromClient(context.Background(), mock, "test-key", nil) require.NoError(t, err) addr1, err := s.GetAddress() @@ -207,34 +206,3 @@ func TestGetAddress_Deterministic(t *testing.T) { assert.Equal(t, addr1, addr2, "address should be deterministic") } - -func TestGetPublic_RefreshTimeoutReturnsStale(t *testing.T) { - _, der := generateTestEd25519DER(t) - - var getPubCalls int32 - mock := &mockKMSClient{ - pubKeyDER: der, - getPubFn: func(ctx context.Context, _ *kms.GetPublicKeyInput) (*kms.GetPublicKeyOutput, error) { - call := atomic.AddInt32(&getPubCalls, 1) - if call == 1 { - return &kms.GetPublicKeyOutput{PublicKey: der}, nil - } - <-ctx.Done() - return nil, ctx.Err() - }, - } - - s, err := NewKmsSignerFromClient(context.Background(), mock, "test-key", &Options{CacheTTL: time.Nanosecond, Timeout: 20 * time.Millisecond}) - require.NoError(t, err) - - time.Sleep(2 * time.Millisecond) - - start := time.Now() - pub, err := s.GetPublic() - duration := time.Since(start) - - require.NoError(t, err) - require.NotNil(t, pub) - assert.Less(t, duration, 200*time.Millisecond, "refresh should respect timeout and return stale key") - assert.GreaterOrEqual(t, atomic.LoadInt32(&getPubCalls), int32(2), "should attempt refresh call") -} diff --git a/pkg/signer/factory/factory.go b/pkg/signer/factory.go similarity index 88% rename from pkg/signer/factory/factory.go rename to pkg/signer/factory.go index 9fab3a033f..3c7b7f52d5 100644 --- a/pkg/signer/factory/factory.go +++ b/pkg/signer/factory.go @@ -1,4 +1,4 @@ -package factory +package signer import ( "context" @@ -8,23 +8,22 @@ import ( "strings" rollconf "github.com/evstack/ev-node/pkg/config" - "github.com/evstack/ev-node/pkg/signer" awssigner "github.com/evstack/ev-node/pkg/signer/aws" "github.com/evstack/ev-node/pkg/signer/file" ) // NewSigner creates a new Signer based on the configuration. -func NewSigner(ctx context.Context, config *rollconf.Config, passphrase string) (signer.Signer, error) { +func NewSigner(ctx context.Context, config *rollconf.Config, passphrase string) (Signer, error) { return newSigner(ctx, config, passphrase, false) } // NewSignerForInit creates a new Signer for init-time flows. // For file signer, it creates a new key if signer.json is missing. -func NewSignerForInit(ctx context.Context, config *rollconf.Config, passphrase string) (signer.Signer, error) { +func NewSignerForInit(ctx context.Context, config *rollconf.Config, passphrase string) (Signer, error) { return newSigner(ctx, config, passphrase, true) } -func newSigner(ctx context.Context, config *rollconf.Config, passphrase string, allowCreate bool) (signer.Signer, error) { +func newSigner(ctx context.Context, config *rollconf.Config, passphrase string, allowCreate bool) (Signer, error) { switch config.Signer.SignerType { case "file": if passphrase == "" { @@ -53,7 +52,6 @@ func newSigner(ctx context.Context, config *rollconf.Config, passphrase string, opts := &awssigner.Options{ Timeout: config.Signer.KmsTimeout.Duration, MaxRetries: config.Signer.KmsMaxRetries, - CacheTTL: config.Signer.KmsCacheTTL.Duration, } return awssigner.NewKmsSigner(ctx, config.Signer.KmsRegion, config.Signer.KmsProfile, config.Signer.KmsKeyID, opts) diff --git a/pkg/signer/factory_test.go b/pkg/signer/factory_test.go new file mode 100644 index 0000000000..7580fc15cf --- /dev/null +++ b/pkg/signer/factory_test.go @@ -0,0 +1,94 @@ +package signer + +import ( + "os" + "path/filepath" + "testing" + + rollconf "github.com/evstack/ev-node/pkg/config" + "github.com/stretchr/testify/require" +) + +func TestNewSigner_ErrorPaths(t *testing.T) { + t.Parallel() + + specs := map[string]struct { + mutateCfg func(cfg *rollconf.Config) + pass string + wantErr string + }{ + "unknown-type": { + mutateCfg: func(cfg *rollconf.Config) { + cfg.Signer.SignerType = "unknown" + }, + pass: "test-passphrase", + wantErr: "unknown signer type: unknown", + }, + "file-empty-passphrase": { + mutateCfg: func(cfg *rollconf.Config) { + cfg.Signer.SignerType = "file" + cfg.Signer.SignerPath = t.TempDir() + }, + pass: "", + wantErr: "passphrase is required when using local file signer", + }, + "awskms-empty-key-id": { + mutateCfg: func(cfg *rollconf.Config) { + cfg.Signer.SignerType = "awskms" + cfg.Signer.KmsKeyID = "" + }, + pass: "unused-passphrase", + wantErr: "aws kms key ID is required", + }, + } + + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + cfg := rollconf.DefaultConfig() + spec.mutateCfg(&cfg) + + s, err := NewSigner(t.Context(), &cfg, spec.pass) + require.Error(t, err) + require.Nil(t, s) + require.ErrorContains(t, err, spec.wantErr) + }) + } +} + +func TestNewSignerForInit_FileFlow(t *testing.T) { + specs := map[string]struct { + setupExisting bool + }{ + "create-missing-file": { + setupExisting: false, + }, + "load-existing-file": { + setupExisting: true, + }, + } + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + cfg := rollconf.DefaultConfig() + cfg.Signer.SignerType = "file" + cfg.Signer.SignerPath = t.TempDir() + const passphrase = "test-passphrase" + + if spec.setupExisting { + seedSigner, err := NewSignerForInit(t.Context(), &cfg, passphrase) + require.NoError(t, err) + require.NotNil(t, seedSigner) + } + + initSigner, err := NewSignerForInit(t.Context(), &cfg, passphrase) + require.NoError(t, err) + require.NotNil(t, initSigner) + + _, err = os.Stat(filepath.Join(cfg.Signer.SignerPath, "signer.json")) + require.NoError(t, err) + + runtimeSigner, err := NewSigner(t.Context(), &cfg, passphrase) + require.NoError(t, err) + require.NotNil(t, runtimeSigner) + }) + } +} diff --git a/pkg/signer/file/file_signer_test.go b/pkg/signer/file/file_signer_test.go index fbb50d5b19..8dc796e369 100644 --- a/pkg/signer/file/file_signer_test.go +++ b/pkg/signer/file/file_signer_test.go @@ -237,7 +237,7 @@ func TestKeyPersistence(t *testing.T) { signer1, err := CreateFileSystemSigner(keyPath, passphrase) require.NoError(t, err) - privateKey1 := signer1.(*FileSystemSigner).privateKey + privateKey1 := signer1.privateKey require.NotNil(t, privateKey1) // Get public key for later comparison @@ -262,7 +262,7 @@ func TestKeyPersistence(t *testing.T) { signer2, err := LoadFileSystemSigner(keyPath, newPassphrase) require.NoError(t, err) - privateKey3 := signer2.(*FileSystemSigner).privateKey + privateKey3 := signer2.privateKey require.NotNil(t, privateKey3) require.Equal(t, privateKey1, privateKey3) @@ -462,8 +462,7 @@ func TestImportExportPrivateKey(t *testing.T) { require.NoError(t, err) require.NotNil(t, signer) - fsSigner := signer.(*FileSystemSigner) - originalPrivKeyBytes, err := fsSigner.privateKey.Raw() + originalPrivKeyBytes, err := signer.privateKey.Raw() require.NoError(t, err) // 2. Export the private key @@ -487,8 +486,7 @@ func TestImportExportPrivateKey(t *testing.T) { loadedSigner, err := LoadFileSystemSigner(importKeyPath, importPassphrase) require.NoError(t, err) - loadedFsSigner := loadedSigner.(*FileSystemSigner) - loadedPrivKeyBytes, err := loadedFsSigner.privateKey.Raw() + loadedPrivKeyBytes, err := loadedSigner.privateKey.Raw() require.NoError(t, err) // 6. Verify the loaded key from the imported file matches the original @@ -658,11 +656,9 @@ func TestSaveAndLoadKeys_ErrorCases(t *testing.T) { require.NoError(t, err) // Try to save to an invalid location - fs := signer.(*FileSystemSigner) - fs.keyFile = filepath.Join(readOnlyDir, "invalid.json") - + signer.keyFile = filepath.Join(readOnlyDir, "invalid.json") newPassphrase := []byte("new-passphrase") - err = fs.saveKeys(newPassphrase) + err = signer.saveKeys(newPassphrase) assert.Error(t, err) assert.Contains(t, err.Error(), "failed to write key file") }) diff --git a/pkg/signer/file/local.go b/pkg/signer/file/local.go index 8232a01386..6792c3f07b 100644 --- a/pkg/signer/file/local.go +++ b/pkg/signer/file/local.go @@ -15,8 +15,6 @@ import ( "github.com/libp2p/go-libp2p/core/crypto" "golang.org/x/crypto/argon2" - - "github.com/evstack/ev-node/pkg/signer" ) // FileSystemSigner implements a signer that securely stores keys on disk @@ -37,7 +35,7 @@ type keyData struct { } // CreateFileSystemSigner creates a new key pair and saves it encrypted to disk. -func CreateFileSystemSigner(keyPath string, passphrase []byte) (signer.Signer, error) { +func CreateFileSystemSigner(keyPath string, passphrase []byte) (*FileSystemSigner, error) { defer zeroBytes(passphrase) // Wipe passphrase from memory after use filePath := filepath.Join(keyPath, "signer.json") @@ -79,7 +77,7 @@ func CreateFileSystemSigner(keyPath string, passphrase []byte) (signer.Signer, e } // LoadFileSystemSigner loads existing keys from an encrypted file on disk. -func LoadFileSystemSigner(keyPath string, passphrase []byte) (signer.Signer, error) { +func LoadFileSystemSigner(keyPath string, passphrase []byte) (*FileSystemSigner, error) { defer zeroBytes(passphrase) // Wipe passphrase from memory after use filePath := filepath.Join(keyPath, "signer.json") diff --git a/test/e2e/evm_aws_kms_e2e_test.go b/test/e2e/evm_aws_kms_e2e_test.go new file mode 100644 index 0000000000..94e6eb963d --- /dev/null +++ b/test/e2e/evm_aws_kms_e2e_test.go @@ -0,0 +1,142 @@ +//go:build evm + +package e2e + +import ( + "context" + "flag" + "os" + "path/filepath" + "strings" + "testing" + "time" + + tastoradocker "github.com/celestiaorg/tastora/framework/docker" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/stretchr/testify/require" + + "github.com/evstack/ev-node/execution/evm" +) + +// TestEvmSequencerWithAWSKMSSignerE2E validates an EVM sequencer using AWS KMS +// as the block signer. The test is skipped unless KMS env vars are provided. +// export EVNODE_E2E_AWS_KMS_KEY_ID= +// export EVNODE_E2E_AWS_KMS_REGION= +// go test -v -tags e2e,evm -run TestEvmSequencerWithAWSKMSSignerE2E -count=1 --evm-binary=$(pwd)/../../build/testapp +func TestEvmSequencerWithAWSKMSSignerE2E(t *testing.T) { + if testing.Short() { + t.Skip("skip e2e in short mode") + } + flag.Parse() + + kmsKeyID := os.Getenv("EVNODE_E2E_AWS_KMS_KEY_ID") + if kmsKeyID == "" { + t.Skip("set EVNODE_E2E_AWS_KMS_KEY_ID to run AWS KMS EVM e2e test") + } + + kmsRegion := firstNonEmptyEVMKMS( + os.Getenv("EVNODE_E2E_AWS_KMS_REGION"), + os.Getenv("AWS_REGION"), + os.Getenv("AWS_DEFAULT_REGION"), + ) + kmsProfile := os.Getenv("EVNODE_E2E_AWS_KMS_PROFILE") + + workDir := t.TempDir() + sequencerHome := filepath.Join(workDir, "evm-kms-agg") + sut := NewSystemUnderTest(t) + evmBinary := requireEVMBinary(t, sut) + + dockerClient, networkID := tastoradocker.Setup(t) + evmEnv := SetupCommonEVMEnv(t, sut, dockerClient, networkID) + + jwtSecretFile := createJWTSecretFile(t, sequencerHome, evmEnv.SequencerJWT) + + initArgs := []string{ + "init", + "--home", sequencerHome, + "--evnode.node.aggregator=true", + "--evnode.signer.signer_type=awskms", + "--evnode.signer.kms_key_id=" + kmsKeyID, + "--evnode.signer.kms_timeout=10s", + "--evnode.signer.kms_max_retries=3", + } + if kmsRegion != "" { + initArgs = append(initArgs, "--evnode.signer.kms_region="+kmsRegion) + } + if kmsProfile != "" { + initArgs = append(initArgs, "--evnode.signer.kms_profile="+kmsProfile) + } + + output, err := sut.RunCmd(evmBinary, initArgs...) + require.NoError(t, err, "failed to init evm sequencer with awskms signer: %s", output) + + startArgs := []string{ + "start", + "--evnode.log.format", "json", + "--home", sequencerHome, + "--evnode.node.aggregator=true", + "--evnode.node.block_time", DefaultBlockTime, + "--evnode.da.block_time", DefaultDABlockTime, + "--evnode.da.address", evmEnv.Endpoints.GetDAAddress(), + "--evnode.da.namespace", DefaultDANamespace, + "--evnode.da.batching_strategy", "immediate", + "--evnode.rpc.address", evmEnv.Endpoints.GetRollkitRPCListen(), + "--evnode.p2p.listen_address", evmEnv.Endpoints.GetRollkitP2PAddress(), + "--evnode.signer.signer_type=awskms", + "--evnode.signer.kms_key_id=" + kmsKeyID, + "--evnode.signer.kms_timeout=10s", + "--evnode.signer.kms_max_retries=3", + "--evm.jwt-secret-file", jwtSecretFile, + "--evm.genesis-hash", evmEnv.GenesisHash, + "--evm.engine-url", evmEnv.Endpoints.GetSequencerEngineURL(), + "--evm.eth-url", evmEnv.Endpoints.GetSequencerEthURL(), + } + if kmsRegion != "" { + startArgs = append(startArgs, "--evnode.signer.kms_region="+kmsRegion) + } + if kmsProfile != "" { + startArgs = append(startArgs, "--evnode.signer.kms_profile="+kmsProfile) + } + + sut.ExecCmd(evmBinary, startArgs...) + sut.AwaitNodeUp(t, evmEnv.Endpoints.GetRollkitRPCAddress(), NodeStartupTimeout) + + client, err := ethclient.Dial(evmEnv.Endpoints.GetSequencerEthURL()) + require.NoError(t, err, "should connect to evm endpoint") + defer client.Close() + + var nonce uint64 + tx := evm.GetRandomTransaction(t, TestPrivateKey, TestToAddress, DefaultChainID, DefaultGasLimit, &nonce) + require.NoError(t, client.SendTransaction(context.Background(), tx), "failed to submit tx") + + require.Eventually(t, func() bool { + return evm.CheckTxIncluded(client, tx.Hash()) + }, 20*time.Second, 500*time.Millisecond, "tx should be included in a block") + + t.Logf("KMS-backed EVM tx included: %s", tx.Hash().Hex()) +} + +func firstNonEmptyEVMKMS(values ...string) string { + for _, v := range values { + if v != "" { + return v + } + } + return "" +} + +func requireEVMBinary(t testing.TB, sut *SystemUnderTest) string { + t.Helper() + + helpOutput, err := sut.RunCmd(evmSingleBinaryPath, "start", "--help") + require.NoError(t, err, "failed to run start --help on -evm-binary=%q", evmSingleBinaryPath) + + if !strings.Contains(helpOutput, "--evm.jwt-secret-file") { + t.Skipf( + "-evm-binary=%q does not look like the EVM binary (missing --evm.jwt-secret-file). Pass the correct binary path via -evm-binary", + evmSingleBinaryPath, + ) + } + + return evmSingleBinaryPath +} diff --git a/test/e2e/go.mod b/test/e2e/go.mod index 74278b565e..ddba0c9889 100644 --- a/test/e2e/go.mod +++ b/test/e2e/go.mod @@ -50,6 +50,21 @@ require ( github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/avast/retry-go/v4 v4.6.1 // indirect + github.com/aws/aws-sdk-go-v2 v1.41.4 // indirect + github.com/aws/aws-sdk-go-v2/config v1.32.12 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.19.12 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 // indirect + github.com/aws/aws-sdk-go-v2/service/kms v1.50.3 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 // indirect + github.com/aws/smithy-go v1.24.2 // indirect github.com/bcp-innovations/hyperlane-cosmos v1.0.1 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect diff --git a/test/e2e/go.sum b/test/e2e/go.sum index 4802a58344..189caf5356 100644 --- a/test/e2e/go.sum +++ b/test/e2e/go.sum @@ -104,6 +104,36 @@ github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k= +github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= +github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0= +github.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g= +github.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8= +github.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 h1:CNXO7mvgThFGqOFgbNAP2nol2qAWBOGfqR/7tQlvLmc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 h1:tN6W/hg+pkM+tf9XDkWUbDEjGLb+raoBMFsTodcoYKw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 h1:2HvVAIq+YqgGotK6EkMf+KIEqTISmTYh5zLpYyeTo1Y= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20/go.mod h1:V4X406Y666khGa8ghKmphma/7C0DAtEQYhkq9z4vpbk= +github.com/aws/aws-sdk-go-v2/service/kms v1.50.3 h1:s/zDSG/a/Su9aX+v0Ld9cimUCdkr5FWPmBV8owaEbZY= +github.com/aws/aws-sdk-go-v2/service/kms v1.50.3/go.mod h1:/iSgiUor15ZuxFGQSTf3lA2FmKxFsQoc2tADOarQBSw= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 h1:0GFOLzEbOyZABS3PhYfBIx2rNBACYcKty+XGkTgw1ow= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.8/go.mod h1:LXypKvk85AROkKhOG6/YEcHFPoX+prKTowKnVdcaIxE= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 h1:kiIDLZ005EcKomYYITtfsjn7dtOwHDOFy7IbPXKek2o= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.13/go.mod h1:2h/xGEowcW/g38g06g3KpRWDlT+OTfxxI0o1KqayAB8= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB8KfgAEuG0dc08Bkda7NU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk= +github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= +github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/bcp-innovations/hyperlane-cosmos v1.0.1 h1:gT8OqyJ866Q6AHOlIXKxSdLjd0p8crKG9XXERIWoh4c= github.com/bcp-innovations/hyperlane-cosmos v1.0.1/go.mod h1:3yfa0io5Ii6GmhHWsWl2LEAOEHsqWuMgw2R02+LPogw= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= From 8810f41107b914892eba448a2d6fad96dd34db35 Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Thu, 19 Mar 2026 14:22:52 +0100 Subject: [PATCH 5/7] Linting only --- pkg/signer/factory_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/signer/factory_test.go b/pkg/signer/factory_test.go index 7580fc15cf..f8b02ca252 100644 --- a/pkg/signer/factory_test.go +++ b/pkg/signer/factory_test.go @@ -5,12 +5,12 @@ import ( "path/filepath" "testing" - rollconf "github.com/evstack/ev-node/pkg/config" "github.com/stretchr/testify/require" + + rollconf "github.com/evstack/ev-node/pkg/config" ) func TestNewSigner_ErrorPaths(t *testing.T) { - t.Parallel() specs := map[string]struct { mutateCfg func(cfg *rollconf.Config) @@ -37,7 +37,7 @@ func TestNewSigner_ErrorPaths(t *testing.T) { cfg.Signer.SignerType = "awskms" cfg.Signer.KmsKeyID = "" }, - pass: "unused-passphrase", + pass: "test-passphrase", wantErr: "aws kms key ID is required", }, } From 5363cdb0e92d84eeb366e4df46bac03379860081 Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Thu, 19 Mar 2026 14:26:22 +0100 Subject: [PATCH 6/7] Changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e507bdabf..c0bae2f160 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,8 +15,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Strict raft state. [#3167](https://github.com/evstack/ev-node/pull/3167) - Retry fetching the timestamp on error in da-client [#3166](https://github.com/evstack/ev-node/pull/3166) -### Changes +### Added +- Add AWS KMS signer backend [#3171](https://github.com/evstack/ev-node/pull/3171) - Subscribe to forced inclusion namespace events [#3146](https://github.com/evstack/ev-node/pull/3146) ## v1.0.0 From a4f7bf670c743bf40d3201e6debc92176304fbc6 Mon Sep 17 00:00:00 2001 From: Alexander Peters Date: Fri, 20 Mar 2026 15:42:10 +0100 Subject: [PATCH 7/7] Add remote signer GCO KMS (#3182) * Add remote signer GCO KMS * Review feedback * Minor updates --- apps/evm/go.mod | 22 +- apps/evm/go.sum | 42 ++- apps/grpc/go.mod | 22 +- apps/grpc/go.sum | 42 ++- apps/testapp/go.mod | 22 +- apps/testapp/go.sum | 42 ++- docs/guides/operations/aws-kms-signer.md | 21 +- docs/guides/operations/gcp-kms-signer.md | 91 +++++ execution/evm/test/go.mod | 8 +- execution/evm/test/go.sum | 47 ++- go.mod | 24 +- go.sum | 53 ++- pkg/cmd/run_node.go | 11 +- pkg/config/config.go | 114 ++++-- pkg/config/config_test.go | 103 +++++- pkg/config/defaults.go | 25 +- pkg/signer/aws/README.md | 22 +- pkg/signer/factory.go | 29 +- pkg/signer/factory_test.go | 24 +- pkg/signer/gcp/README.md | 35 ++ pkg/signer/gcp/signer.go | 306 ++++++++++++++++ pkg/signer/gcp/signer_test.go | 338 ++++++++++++++++++ ...ws_kms_e2e_test.go => evm_kms_e2e_test.go} | 132 ++++--- test/e2e/go.mod | 21 +- test/e2e/go.sum | 78 ++-- 25 files changed, 1485 insertions(+), 189 deletions(-) create mode 100644 docs/guides/operations/gcp-kms-signer.md create mode 100644 pkg/signer/gcp/README.md create mode 100644 pkg/signer/gcp/signer.go create mode 100644 pkg/signer/gcp/signer_test.go rename test/e2e/{evm_aws_kms_e2e_test.go => evm_kms_e2e_test.go} (63%) diff --git a/apps/evm/go.mod b/apps/evm/go.mod index 8939c51bcf..6e78a41295 100644 --- a/apps/evm/go.mod +++ b/apps/evm/go.mod @@ -19,6 +19,13 @@ require ( ) require ( + cloud.google.com/go v0.123.0 // indirect + cloud.google.com/go/auth v0.18.2 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect + cloud.google.com/go/compute/metadata v0.9.0 // indirect + cloud.google.com/go/iam v1.5.3 // indirect + cloud.google.com/go/kms v1.26.0 // indirect + cloud.google.com/go/longrunning v0.8.0 // indirect connectrpc.com/connect v1.19.1 // indirect connectrpc.com/grpcreflect v1.3.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect @@ -64,6 +71,7 @@ require ( github.com/emicklei/dot v1.6.2 // indirect github.com/ethereum/c-kzg-4844/v2 v2.1.6 // indirect github.com/fatih/color v1.18.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/ferranbt/fastssz v0.1.4 // indirect github.com/filecoin-project/go-clock v0.1.0 // indirect github.com/filecoin-project/go-jsonrpc v0.10.1 // indirect @@ -83,7 +91,10 @@ require ( github.com/google/flatbuffers v25.1.24+incompatible // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/gopacket v1.1.19 // indirect + github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect + github.com/googleapis/gax-go/v2 v2.18.0 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect @@ -192,6 +203,8 @@ require ( github.com/wlynxg/anet v0.0.5 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect go.opentelemetry.io/otel v1.42.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.42.0 // indirect @@ -210,16 +223,19 @@ require ( golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect golang.org/x/mod v0.33.0 // indirect golang.org/x/net v0.52.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect golang.org/x/sync v0.20.0 // indirect golang.org/x/sys v0.42.0 // indirect golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 // indirect golang.org/x/text v0.35.0 // indirect - golang.org/x/time v0.12.0 // indirect + golang.org/x/time v0.15.0 // indirect golang.org/x/tools v0.42.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gonum.org/v1/gonum v0.17.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect + google.golang.org/api v0.272.0 // indirect + google.golang.org/genproto v0.0.0-20260217215200-42d3e9bedb6d // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260217215200-42d3e9bedb6d // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c // indirect google.golang.org/grpc v1.79.2 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/apps/evm/go.sum b/apps/evm/go.sum index 327a84a0e3..ab3d1ca8c1 100644 --- a/apps/evm/go.sum +++ b/apps/evm/go.sum @@ -40,6 +40,8 @@ cloud.google.com/go v0.110.7/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5x cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk= cloud.google.com/go v0.111.0/go.mod h1:0mibmpKP1TyOOFYQY5izo0LnT+ecvOQ0Sg3OdmMiNRU= cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= +cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE= +cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= cloud.google.com/go/accessapproval v1.7.5/go.mod h1:g88i1ok5dvQ9XJsxpUInWWvUBrIZhyPDPbk4T01OoJ0= cloud.google.com/go/accesscontextmanager v1.8.5/go.mod h1:TInEhcZ7V9jptGNqN3EzZ5XMhT6ijWxTGjzyETwmL0Q= cloud.google.com/go/aiplatform v1.60.0/go.mod h1:eTlGuHOahHprZw3Hio5VKmtThIOak5/qy6pzdsqcQnM= @@ -52,6 +54,10 @@ cloud.google.com/go/area120 v0.8.5/go.mod h1:BcoFCbDLZjsfe4EkCnEq1LKvHSK0Ew/zk5U cloud.google.com/go/artifactregistry v1.14.7/go.mod h1:0AUKhzWQzfmeTvT4SjfI4zjot72EMfrkvL9g9aRjnnM= cloud.google.com/go/asset v1.17.2/go.mod h1:SVbzde67ehddSoKf5uebOD1sYw8Ab/jD/9EIeWg99q4= cloud.google.com/go/assuredworkloads v1.11.5/go.mod h1:FKJ3g3ZvkL2D7qtqIGnDufFkHxwIpNM9vtmhvt+6wqk= +cloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM= +cloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/automl v1.13.5/go.mod h1:MDw3vLem3yh+SvmSgeYUmUKqyls6NzSumDm9OJ3xJ1Y= cloud.google.com/go/baremetalsolution v1.2.4/go.mod h1:BHCmxgpevw9IEryE99HbYEfxXkAEA3hkMJbYYsHtIuY= cloud.google.com/go/batch v1.8.0/go.mod h1:k8V7f6VE2Suc0zUM4WtoibNrA6D3dqBpB+++e3vSGYc= @@ -95,6 +101,8 @@ cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZ cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= cloud.google.com/go/contactcenterinsights v1.13.0/go.mod h1:ieq5d5EtHsu8vhe2y3amtZ+BE+AQwX5qAy7cpo0POsI= cloud.google.com/go/container v1.31.0/go.mod h1:7yABn5s3Iv3lmw7oMmyGbeV6tQj86njcTijkkGuvdZA= cloud.google.com/go/containeranalysis v0.11.4/go.mod h1:cVZT7rXYBS9NG1rhQbWL9pWbXCKHWJPYraE8/FTSYPE= @@ -138,12 +146,16 @@ cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCta cloud.google.com/go/iam v1.1.3/go.mod h1:3khUlaBXfPKKe7huYgEpDn6FtgRyMEqbkvBxrQyY5SE= cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= +cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc= +cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU= cloud.google.com/go/iap v1.9.4/go.mod h1:vO4mSq0xNf/Pu6E5paORLASBwEmphXEjgCFg7aeNu1w= cloud.google.com/go/ids v1.4.5/go.mod h1:p0ZnyzjMWxww6d2DvMGnFwCsSxDJM666Iir1bK1UuBo= cloud.google.com/go/iot v1.7.5/go.mod h1:nq3/sqTz3HGaWJi1xNiX7F41ThOzpud67vwk0YsSsqs= cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= cloud.google.com/go/kms v1.15.5/go.mod h1:cU2H5jnp6G2TDpUGZyqTCoy1n16fbubHZjmVXSMtwDI= cloud.google.com/go/kms v1.15.7/go.mod h1:ub54lbsa6tDkUwnu4W7Yt1aAIFLnspgh0kPGToDukeI= +cloud.google.com/go/kms v1.26.0 h1:cK9mN2cf+9V63D3H1f6koxTatWy39aTI/hCjz1I+adU= +cloud.google.com/go/kms v1.26.0/go.mod h1:pHKOdFJm63hxBsiPkYtowZPltu9dW0MWvBa6IA4HM58= cloud.google.com/go/language v1.12.3/go.mod h1:evFX9wECX6mksEva8RbRnr/4wi/vKGYnAJrTRXU8+f8= cloud.google.com/go/lifesciences v0.9.5/go.mod h1:OdBm0n7C0Osh5yZB7j9BXyrMnTRGBJIZonUMxo5CzPw= cloud.google.com/go/logging v1.9.0/go.mod h1:1Io0vnZv4onoUnsVUQY3HZ3Igb1nBchky0A0y7BBBhE= @@ -155,6 +167,8 @@ cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHS cloud.google.com/go/longrunning v0.5.2/go.mod h1:nqo6DQbNV2pXhGDbDMoN2bWz68MjZUzqv2YttZiveCs= cloud.google.com/go/longrunning v0.5.4/go.mod h1:zqNVncI0BOP8ST6XQD1+VcvuShMmq7+xFSzOL++V0dI= cloud.google.com/go/longrunning v0.5.5/go.mod h1:WV2LAxD8/rg5Z1cNW6FJ/ZpX4E4VnDnoTk0yawPBB7s= +cloud.google.com/go/longrunning v0.8.0 h1:LiKK77J3bx5gDLi4SMViHixjD2ohlkwBi+mKA7EhfW8= +cloud.google.com/go/longrunning v0.8.0/go.mod h1:UmErU2Onzi+fKDg2gR7dusz11Pe26aknR4kHmJJqIfk= cloud.google.com/go/managedidentities v1.6.5/go.mod h1:fkFI2PwwyRQbjLxlm5bQ8SjtObFMW3ChBGNqaMcgZjI= cloud.google.com/go/maps v1.6.4/go.mod h1:rhjqRy8NWmDJ53saCfsXQ0LKwBHfi6OSh5wkq6BaMhI= cloud.google.com/go/mediatranslation v0.8.5/go.mod h1:y7kTHYIPCIfgyLbKncgqouXJtLsU+26hZhHEEy80fSs= @@ -360,6 +374,8 @@ github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20230428030218-4003588d1b74/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4= @@ -424,6 +440,9 @@ github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJ github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI= github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g= +github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= +github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g= +github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= @@ -431,6 +450,8 @@ github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6Ni github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= github.com/envoyproxy/protoc-gen-validate v1.0.1/go.mod h1:0vj8bNkYbSTNS2PIyH87KZaeN4x9zpL9Qt8fQC7d+vs= github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= +github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4= +github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA= github.com/ethereum/c-kzg-4844/v2 v2.1.6 h1:xQymkKCT5E2Jiaoqf3v4wsNgjZLY0lRSkZn27fRjSls= github.com/ethereum/c-kzg-4844/v2 v2.1.6/go.mod h1:8HMkUZ5JRv4hpw/XUrYWSQNAUzhHMg2UDb/U+5m+XNw= github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk= @@ -444,6 +465,7 @@ github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYF github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY= github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg= @@ -615,6 +637,8 @@ github.com/google/s2a-go v0.1.0/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JV github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -629,6 +653,8 @@ github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5 github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/enterprise-certificate-proxy v0.2.4/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/googleapis/enterprise-certificate-proxy v0.3.14 h1:yh8ncqsbUY4shRD5dA6RlzjJaT4hi3kII+zYw8wmLb8= +github.com/googleapis/enterprise-certificate-proxy v0.3.14/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -644,6 +670,8 @@ github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38 github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw= github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= +github.com/googleapis/gax-go/v2 v2.18.0 h1:jxP5Uuo3bxm3M6gGtV94P4lliVetoCB4Wk2x8QA86LI= +github.com/googleapis/gax-go/v2 v2.18.0/go.mod h1:uSzZN4a356eRG985CzJ3WfbFSpqkLTjsnhWGJR6EwrE= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -942,6 +970,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= 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= @@ -1109,8 +1139,12 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0/go.mod h1:r9vWsPS/3AQItv3OSlEJ/E4mbrhUbbw18meOjArPtKQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= @@ -1361,6 +1395,8 @@ golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM= golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= 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= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1532,8 +1568,8 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.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/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1691,6 +1727,8 @@ google.golang.org/api v0.155.0/go.mod h1:GI5qK5f40kCpHfPn6+YzGAByIKWv8ujFnmoWm7I google.golang.org/api v0.157.0/go.mod h1:+z4v4ufbZ1WEpld6yMGHyggs+PmAHiaLNj5ytP3N01g= google.golang.org/api v0.160.0/go.mod h1:0mu0TpK33qnydLvWqbImq2b1eQ5FHRSDCBzAxX9ZHyw= google.golang.org/api v0.162.0/go.mod h1:6SulDkfoBIg4NFmCuZ39XeeAgSHCPecfSUuDyYlAHs0= +google.golang.org/api v0.272.0 h1:eLUQZGnAS3OHn31URRf9sAmRk3w2JjMx37d2k8AjJmA= +google.golang.org/api v0.272.0/go.mod h1:wKjowi5LNJc5qarNvDCvNQBn3rVK8nSy6jg2SwRwzIA= 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/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= diff --git a/apps/grpc/go.mod b/apps/grpc/go.mod index 139ae32f1e..064c84fa34 100644 --- a/apps/grpc/go.mod +++ b/apps/grpc/go.mod @@ -17,6 +17,13 @@ require ( ) require ( + cloud.google.com/go v0.123.0 // indirect + cloud.google.com/go/auth v0.18.2 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect + cloud.google.com/go/compute/metadata v0.9.0 // indirect + cloud.google.com/go/iam v1.5.3 // indirect + cloud.google.com/go/kms v1.26.0 // indirect + cloud.google.com/go/longrunning v0.8.0 // indirect connectrpc.com/connect v1.19.1 // indirect connectrpc.com/grpcreflect v1.3.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect @@ -53,6 +60,7 @@ require ( github.com/dunglas/httpsfv v1.1.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/fatih/color v1.18.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/filecoin-project/go-clock v0.1.0 // indirect github.com/filecoin-project/go-jsonrpc v0.10.1 // indirect github.com/flynn/noise v1.1.0 // indirect @@ -66,7 +74,10 @@ require ( github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/google/flatbuffers v25.1.24+incompatible // indirect github.com/google/gopacket v1.1.19 // indirect + github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect + github.com/googleapis/gax-go/v2 v2.18.0 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect github.com/hashicorp/go-hclog v1.6.2 // indirect @@ -169,6 +180,8 @@ require ( github.com/wlynxg/anet v0.0.5 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect go.opentelemetry.io/otel v1.42.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.42.0 // indirect @@ -187,16 +200,19 @@ require ( golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect golang.org/x/mod v0.33.0 // indirect golang.org/x/net v0.52.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect golang.org/x/sync v0.20.0 // indirect golang.org/x/sys v0.42.0 // indirect golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 // indirect golang.org/x/text v0.35.0 // indirect - golang.org/x/time v0.12.0 // indirect + golang.org/x/time v0.15.0 // indirect golang.org/x/tools v0.42.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gonum.org/v1/gonum v0.17.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect + google.golang.org/api v0.272.0 // indirect + google.golang.org/genproto v0.0.0-20260217215200-42d3e9bedb6d // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260217215200-42d3e9bedb6d // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c // indirect google.golang.org/grpc v1.79.2 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/apps/grpc/go.sum b/apps/grpc/go.sum index c8a627d115..5599dd3216 100644 --- a/apps/grpc/go.sum +++ b/apps/grpc/go.sum @@ -40,6 +40,8 @@ cloud.google.com/go v0.110.7/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5x cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk= cloud.google.com/go v0.111.0/go.mod h1:0mibmpKP1TyOOFYQY5izo0LnT+ecvOQ0Sg3OdmMiNRU= cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= +cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE= +cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= cloud.google.com/go/accessapproval v1.7.5/go.mod h1:g88i1ok5dvQ9XJsxpUInWWvUBrIZhyPDPbk4T01OoJ0= cloud.google.com/go/accesscontextmanager v1.8.5/go.mod h1:TInEhcZ7V9jptGNqN3EzZ5XMhT6ijWxTGjzyETwmL0Q= cloud.google.com/go/aiplatform v1.60.0/go.mod h1:eTlGuHOahHprZw3Hio5VKmtThIOak5/qy6pzdsqcQnM= @@ -52,6 +54,10 @@ cloud.google.com/go/area120 v0.8.5/go.mod h1:BcoFCbDLZjsfe4EkCnEq1LKvHSK0Ew/zk5U cloud.google.com/go/artifactregistry v1.14.7/go.mod h1:0AUKhzWQzfmeTvT4SjfI4zjot72EMfrkvL9g9aRjnnM= cloud.google.com/go/asset v1.17.2/go.mod h1:SVbzde67ehddSoKf5uebOD1sYw8Ab/jD/9EIeWg99q4= cloud.google.com/go/assuredworkloads v1.11.5/go.mod h1:FKJ3g3ZvkL2D7qtqIGnDufFkHxwIpNM9vtmhvt+6wqk= +cloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM= +cloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/automl v1.13.5/go.mod h1:MDw3vLem3yh+SvmSgeYUmUKqyls6NzSumDm9OJ3xJ1Y= cloud.google.com/go/baremetalsolution v1.2.4/go.mod h1:BHCmxgpevw9IEryE99HbYEfxXkAEA3hkMJbYYsHtIuY= cloud.google.com/go/batch v1.8.0/go.mod h1:k8V7f6VE2Suc0zUM4WtoibNrA6D3dqBpB+++e3vSGYc= @@ -95,6 +101,8 @@ cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZ cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= cloud.google.com/go/contactcenterinsights v1.13.0/go.mod h1:ieq5d5EtHsu8vhe2y3amtZ+BE+AQwX5qAy7cpo0POsI= cloud.google.com/go/container v1.31.0/go.mod h1:7yABn5s3Iv3lmw7oMmyGbeV6tQj86njcTijkkGuvdZA= cloud.google.com/go/containeranalysis v0.11.4/go.mod h1:cVZT7rXYBS9NG1rhQbWL9pWbXCKHWJPYraE8/FTSYPE= @@ -138,12 +146,16 @@ cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCta cloud.google.com/go/iam v1.1.3/go.mod h1:3khUlaBXfPKKe7huYgEpDn6FtgRyMEqbkvBxrQyY5SE= cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= +cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc= +cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU= cloud.google.com/go/iap v1.9.4/go.mod h1:vO4mSq0xNf/Pu6E5paORLASBwEmphXEjgCFg7aeNu1w= cloud.google.com/go/ids v1.4.5/go.mod h1:p0ZnyzjMWxww6d2DvMGnFwCsSxDJM666Iir1bK1UuBo= cloud.google.com/go/iot v1.7.5/go.mod h1:nq3/sqTz3HGaWJi1xNiX7F41ThOzpud67vwk0YsSsqs= cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= cloud.google.com/go/kms v1.15.5/go.mod h1:cU2H5jnp6G2TDpUGZyqTCoy1n16fbubHZjmVXSMtwDI= cloud.google.com/go/kms v1.15.7/go.mod h1:ub54lbsa6tDkUwnu4W7Yt1aAIFLnspgh0kPGToDukeI= +cloud.google.com/go/kms v1.26.0 h1:cK9mN2cf+9V63D3H1f6koxTatWy39aTI/hCjz1I+adU= +cloud.google.com/go/kms v1.26.0/go.mod h1:pHKOdFJm63hxBsiPkYtowZPltu9dW0MWvBa6IA4HM58= cloud.google.com/go/language v1.12.3/go.mod h1:evFX9wECX6mksEva8RbRnr/4wi/vKGYnAJrTRXU8+f8= cloud.google.com/go/lifesciences v0.9.5/go.mod h1:OdBm0n7C0Osh5yZB7j9BXyrMnTRGBJIZonUMxo5CzPw= cloud.google.com/go/logging v1.9.0/go.mod h1:1Io0vnZv4onoUnsVUQY3HZ3Igb1nBchky0A0y7BBBhE= @@ -155,6 +167,8 @@ cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHS cloud.google.com/go/longrunning v0.5.2/go.mod h1:nqo6DQbNV2pXhGDbDMoN2bWz68MjZUzqv2YttZiveCs= cloud.google.com/go/longrunning v0.5.4/go.mod h1:zqNVncI0BOP8ST6XQD1+VcvuShMmq7+xFSzOL++V0dI= cloud.google.com/go/longrunning v0.5.5/go.mod h1:WV2LAxD8/rg5Z1cNW6FJ/ZpX4E4VnDnoTk0yawPBB7s= +cloud.google.com/go/longrunning v0.8.0 h1:LiKK77J3bx5gDLi4SMViHixjD2ohlkwBi+mKA7EhfW8= +cloud.google.com/go/longrunning v0.8.0/go.mod h1:UmErU2Onzi+fKDg2gR7dusz11Pe26aknR4kHmJJqIfk= cloud.google.com/go/managedidentities v1.6.5/go.mod h1:fkFI2PwwyRQbjLxlm5bQ8SjtObFMW3ChBGNqaMcgZjI= cloud.google.com/go/maps v1.6.4/go.mod h1:rhjqRy8NWmDJ53saCfsXQ0LKwBHfi6OSh5wkq6BaMhI= cloud.google.com/go/mediatranslation v0.8.5/go.mod h1:y7kTHYIPCIfgyLbKncgqouXJtLsU+26hZhHEEy80fSs= @@ -348,6 +362,8 @@ github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20230428030218-4003588d1b74/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= @@ -390,6 +406,9 @@ github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJ github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI= github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g= +github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= +github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g= +github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= @@ -397,6 +416,8 @@ github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6Ni github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= github.com/envoyproxy/protoc-gen-validate v1.0.1/go.mod h1:0vj8bNkYbSTNS2PIyH87KZaeN4x9zpL9Qt8fQC7d+vs= github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= +github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4= +github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA= github.com/evstack/ev-node/core v1.0.0 h1:s0Tx0uWHme7SJn/ZNEtee4qNM8UO6PIxXnHhPbbKTz8= github.com/evstack/ev-node/core v1.0.0/go.mod h1:n2w/LhYQTPsi48m6lMj16YiIqsaQw6gxwjyJvR+B3sY= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= @@ -404,6 +425,7 @@ github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYF github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/filecoin-project/go-clock v0.1.0 h1:SFbYIM75M8NnFm1yMHhN9Ahy3W5bEZV9gd6MPfXbKVU= github.com/filecoin-project/go-clock v0.1.0/go.mod h1:4uB/O4PvOjlx1VCMdZ9MyDZXRm//gkj1ELEbxfI1AZs= @@ -560,6 +582,8 @@ github.com/google/s2a-go v0.1.0/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JV github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -574,6 +598,8 @@ github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5 github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/enterprise-certificate-proxy v0.2.4/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/googleapis/enterprise-certificate-proxy v0.3.14 h1:yh8ncqsbUY4shRD5dA6RlzjJaT4hi3kII+zYw8wmLb8= +github.com/googleapis/enterprise-certificate-proxy v0.3.14/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -589,6 +615,8 @@ github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38 github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw= github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= +github.com/googleapis/gax-go/v2 v2.18.0 h1:jxP5Uuo3bxm3M6gGtV94P4lliVetoCB4Wk2x8QA86LI= +github.com/googleapis/gax-go/v2 v2.18.0/go.mod h1:uSzZN4a356eRG985CzJ3WfbFSpqkLTjsnhWGJR6EwrE= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -866,6 +894,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= 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= @@ -1013,8 +1043,12 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0/go.mod h1:r9vWsPS/3AQItv3OSlEJ/E4mbrhUbbw18meOjArPtKQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= @@ -1265,6 +1299,8 @@ golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM= golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= 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= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1435,8 +1471,8 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.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/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1594,6 +1630,8 @@ google.golang.org/api v0.155.0/go.mod h1:GI5qK5f40kCpHfPn6+YzGAByIKWv8ujFnmoWm7I google.golang.org/api v0.157.0/go.mod h1:+z4v4ufbZ1WEpld6yMGHyggs+PmAHiaLNj5ytP3N01g= google.golang.org/api v0.160.0/go.mod h1:0mu0TpK33qnydLvWqbImq2b1eQ5FHRSDCBzAxX9ZHyw= google.golang.org/api v0.162.0/go.mod h1:6SulDkfoBIg4NFmCuZ39XeeAgSHCPecfSUuDyYlAHs0= +google.golang.org/api v0.272.0 h1:eLUQZGnAS3OHn31URRf9sAmRk3w2JjMx37d2k8AjJmA= +google.golang.org/api v0.272.0/go.mod h1:wKjowi5LNJc5qarNvDCvNQBn3rVK8nSy6jg2SwRwzIA= 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/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= diff --git a/apps/testapp/go.mod b/apps/testapp/go.mod index 7b093951d5..17b171bcf7 100644 --- a/apps/testapp/go.mod +++ b/apps/testapp/go.mod @@ -14,6 +14,13 @@ require ( ) require ( + cloud.google.com/go v0.123.0 // indirect + cloud.google.com/go/auth v0.18.2 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect + cloud.google.com/go/compute/metadata v0.9.0 // indirect + cloud.google.com/go/iam v1.5.3 // indirect + cloud.google.com/go/kms v1.26.0 // indirect + cloud.google.com/go/longrunning v0.8.0 // indirect connectrpc.com/connect v1.19.1 // indirect connectrpc.com/grpcreflect v1.3.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect @@ -50,6 +57,7 @@ require ( github.com/dunglas/httpsfv v1.1.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/fatih/color v1.18.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/filecoin-project/go-clock v0.1.0 // indirect github.com/filecoin-project/go-jsonrpc v0.10.1 // indirect github.com/flynn/noise v1.1.0 // indirect @@ -63,7 +71,10 @@ require ( github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/google/flatbuffers v25.1.24+incompatible // indirect github.com/google/gopacket v1.1.19 // indirect + github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect + github.com/googleapis/gax-go/v2 v2.18.0 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect github.com/hashicorp/go-hclog v1.6.2 // indirect @@ -165,6 +176,8 @@ require ( github.com/wlynxg/anet v0.0.5 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect go.opentelemetry.io/otel v1.42.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.42.0 // indirect @@ -183,16 +196,19 @@ require ( golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect golang.org/x/mod v0.33.0 // indirect golang.org/x/net v0.52.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect golang.org/x/sync v0.20.0 // indirect golang.org/x/sys v0.42.0 // indirect golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 // indirect golang.org/x/text v0.35.0 // indirect - golang.org/x/time v0.12.0 // indirect + golang.org/x/time v0.15.0 // indirect golang.org/x/tools v0.42.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gonum.org/v1/gonum v0.17.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect + google.golang.org/api v0.272.0 // indirect + google.golang.org/genproto v0.0.0-20260217215200-42d3e9bedb6d // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260217215200-42d3e9bedb6d // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c // indirect google.golang.org/grpc v1.79.2 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/apps/testapp/go.sum b/apps/testapp/go.sum index c8a627d115..5599dd3216 100644 --- a/apps/testapp/go.sum +++ b/apps/testapp/go.sum @@ -40,6 +40,8 @@ cloud.google.com/go v0.110.7/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5x cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk= cloud.google.com/go v0.111.0/go.mod h1:0mibmpKP1TyOOFYQY5izo0LnT+ecvOQ0Sg3OdmMiNRU= cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= +cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE= +cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= cloud.google.com/go/accessapproval v1.7.5/go.mod h1:g88i1ok5dvQ9XJsxpUInWWvUBrIZhyPDPbk4T01OoJ0= cloud.google.com/go/accesscontextmanager v1.8.5/go.mod h1:TInEhcZ7V9jptGNqN3EzZ5XMhT6ijWxTGjzyETwmL0Q= cloud.google.com/go/aiplatform v1.60.0/go.mod h1:eTlGuHOahHprZw3Hio5VKmtThIOak5/qy6pzdsqcQnM= @@ -52,6 +54,10 @@ cloud.google.com/go/area120 v0.8.5/go.mod h1:BcoFCbDLZjsfe4EkCnEq1LKvHSK0Ew/zk5U cloud.google.com/go/artifactregistry v1.14.7/go.mod h1:0AUKhzWQzfmeTvT4SjfI4zjot72EMfrkvL9g9aRjnnM= cloud.google.com/go/asset v1.17.2/go.mod h1:SVbzde67ehddSoKf5uebOD1sYw8Ab/jD/9EIeWg99q4= cloud.google.com/go/assuredworkloads v1.11.5/go.mod h1:FKJ3g3ZvkL2D7qtqIGnDufFkHxwIpNM9vtmhvt+6wqk= +cloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM= +cloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/automl v1.13.5/go.mod h1:MDw3vLem3yh+SvmSgeYUmUKqyls6NzSumDm9OJ3xJ1Y= cloud.google.com/go/baremetalsolution v1.2.4/go.mod h1:BHCmxgpevw9IEryE99HbYEfxXkAEA3hkMJbYYsHtIuY= cloud.google.com/go/batch v1.8.0/go.mod h1:k8V7f6VE2Suc0zUM4WtoibNrA6D3dqBpB+++e3vSGYc= @@ -95,6 +101,8 @@ cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZ cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= cloud.google.com/go/contactcenterinsights v1.13.0/go.mod h1:ieq5d5EtHsu8vhe2y3amtZ+BE+AQwX5qAy7cpo0POsI= cloud.google.com/go/container v1.31.0/go.mod h1:7yABn5s3Iv3lmw7oMmyGbeV6tQj86njcTijkkGuvdZA= cloud.google.com/go/containeranalysis v0.11.4/go.mod h1:cVZT7rXYBS9NG1rhQbWL9pWbXCKHWJPYraE8/FTSYPE= @@ -138,12 +146,16 @@ cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCta cloud.google.com/go/iam v1.1.3/go.mod h1:3khUlaBXfPKKe7huYgEpDn6FtgRyMEqbkvBxrQyY5SE= cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= +cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc= +cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU= cloud.google.com/go/iap v1.9.4/go.mod h1:vO4mSq0xNf/Pu6E5paORLASBwEmphXEjgCFg7aeNu1w= cloud.google.com/go/ids v1.4.5/go.mod h1:p0ZnyzjMWxww6d2DvMGnFwCsSxDJM666Iir1bK1UuBo= cloud.google.com/go/iot v1.7.5/go.mod h1:nq3/sqTz3HGaWJi1xNiX7F41ThOzpud67vwk0YsSsqs= cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= cloud.google.com/go/kms v1.15.5/go.mod h1:cU2H5jnp6G2TDpUGZyqTCoy1n16fbubHZjmVXSMtwDI= cloud.google.com/go/kms v1.15.7/go.mod h1:ub54lbsa6tDkUwnu4W7Yt1aAIFLnspgh0kPGToDukeI= +cloud.google.com/go/kms v1.26.0 h1:cK9mN2cf+9V63D3H1f6koxTatWy39aTI/hCjz1I+adU= +cloud.google.com/go/kms v1.26.0/go.mod h1:pHKOdFJm63hxBsiPkYtowZPltu9dW0MWvBa6IA4HM58= cloud.google.com/go/language v1.12.3/go.mod h1:evFX9wECX6mksEva8RbRnr/4wi/vKGYnAJrTRXU8+f8= cloud.google.com/go/lifesciences v0.9.5/go.mod h1:OdBm0n7C0Osh5yZB7j9BXyrMnTRGBJIZonUMxo5CzPw= cloud.google.com/go/logging v1.9.0/go.mod h1:1Io0vnZv4onoUnsVUQY3HZ3Igb1nBchky0A0y7BBBhE= @@ -155,6 +167,8 @@ cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHS cloud.google.com/go/longrunning v0.5.2/go.mod h1:nqo6DQbNV2pXhGDbDMoN2bWz68MjZUzqv2YttZiveCs= cloud.google.com/go/longrunning v0.5.4/go.mod h1:zqNVncI0BOP8ST6XQD1+VcvuShMmq7+xFSzOL++V0dI= cloud.google.com/go/longrunning v0.5.5/go.mod h1:WV2LAxD8/rg5Z1cNW6FJ/ZpX4E4VnDnoTk0yawPBB7s= +cloud.google.com/go/longrunning v0.8.0 h1:LiKK77J3bx5gDLi4SMViHixjD2ohlkwBi+mKA7EhfW8= +cloud.google.com/go/longrunning v0.8.0/go.mod h1:UmErU2Onzi+fKDg2gR7dusz11Pe26aknR4kHmJJqIfk= cloud.google.com/go/managedidentities v1.6.5/go.mod h1:fkFI2PwwyRQbjLxlm5bQ8SjtObFMW3ChBGNqaMcgZjI= cloud.google.com/go/maps v1.6.4/go.mod h1:rhjqRy8NWmDJ53saCfsXQ0LKwBHfi6OSh5wkq6BaMhI= cloud.google.com/go/mediatranslation v0.8.5/go.mod h1:y7kTHYIPCIfgyLbKncgqouXJtLsU+26hZhHEEy80fSs= @@ -348,6 +362,8 @@ github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20230428030218-4003588d1b74/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= @@ -390,6 +406,9 @@ github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJ github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI= github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g= +github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= +github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g= +github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= @@ -397,6 +416,8 @@ github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6Ni github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= github.com/envoyproxy/protoc-gen-validate v1.0.1/go.mod h1:0vj8bNkYbSTNS2PIyH87KZaeN4x9zpL9Qt8fQC7d+vs= github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= +github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4= +github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA= github.com/evstack/ev-node/core v1.0.0 h1:s0Tx0uWHme7SJn/ZNEtee4qNM8UO6PIxXnHhPbbKTz8= github.com/evstack/ev-node/core v1.0.0/go.mod h1:n2w/LhYQTPsi48m6lMj16YiIqsaQw6gxwjyJvR+B3sY= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= @@ -404,6 +425,7 @@ github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYF github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/filecoin-project/go-clock v0.1.0 h1:SFbYIM75M8NnFm1yMHhN9Ahy3W5bEZV9gd6MPfXbKVU= github.com/filecoin-project/go-clock v0.1.0/go.mod h1:4uB/O4PvOjlx1VCMdZ9MyDZXRm//gkj1ELEbxfI1AZs= @@ -560,6 +582,8 @@ github.com/google/s2a-go v0.1.0/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JV github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -574,6 +598,8 @@ github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5 github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/enterprise-certificate-proxy v0.2.4/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/googleapis/enterprise-certificate-proxy v0.3.14 h1:yh8ncqsbUY4shRD5dA6RlzjJaT4hi3kII+zYw8wmLb8= +github.com/googleapis/enterprise-certificate-proxy v0.3.14/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -589,6 +615,8 @@ github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38 github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw= github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= +github.com/googleapis/gax-go/v2 v2.18.0 h1:jxP5Uuo3bxm3M6gGtV94P4lliVetoCB4Wk2x8QA86LI= +github.com/googleapis/gax-go/v2 v2.18.0/go.mod h1:uSzZN4a356eRG985CzJ3WfbFSpqkLTjsnhWGJR6EwrE= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -866,6 +894,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= 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= @@ -1013,8 +1043,12 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0/go.mod h1:r9vWsPS/3AQItv3OSlEJ/E4mbrhUbbw18meOjArPtKQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= @@ -1265,6 +1299,8 @@ golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM= golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= 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= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1435,8 +1471,8 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.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/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1594,6 +1630,8 @@ google.golang.org/api v0.155.0/go.mod h1:GI5qK5f40kCpHfPn6+YzGAByIKWv8ujFnmoWm7I google.golang.org/api v0.157.0/go.mod h1:+z4v4ufbZ1WEpld6yMGHyggs+PmAHiaLNj5ytP3N01g= google.golang.org/api v0.160.0/go.mod h1:0mu0TpK33qnydLvWqbImq2b1eQ5FHRSDCBzAxX9ZHyw= google.golang.org/api v0.162.0/go.mod h1:6SulDkfoBIg4NFmCuZ39XeeAgSHCPecfSUuDyYlAHs0= +google.golang.org/api v0.272.0 h1:eLUQZGnAS3OHn31URRf9sAmRk3w2JjMx37d2k8AjJmA= +google.golang.org/api v0.272.0/go.mod h1:wKjowi5LNJc5qarNvDCvNQBn3rVK8nSy6jg2SwRwzIA= 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/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= diff --git a/docs/guides/operations/aws-kms-signer.md b/docs/guides/operations/aws-kms-signer.md index c5f5804da8..7ebc162448 100644 --- a/docs/guides/operations/aws-kms-signer.md +++ b/docs/guides/operations/aws-kms-signer.md @@ -1,6 +1,6 @@ # Use AWS KMS Signer -Use this guide to run `ev-node` with an AWS KMS-backed signer (`signer_type: awskms`) instead of a local key file. +Use this guide to run `ev-node` with an AWS KMS-backed signer (`signer_type: kms`, `kms.provider: aws`) instead of a local key file. ## Prerequisites @@ -52,12 +52,15 @@ Copy the returned key ARN (or key ID). You can also create an alias and use that ```yaml signer: - signer_type: "awskms" - kms_key_id: "arn:aws:kms:us-east-1:123456789012:key/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" - kms_region: "us-east-1" # optional but recommended - kms_profile: "prod" # optional; omit when using IAM role/env creds - kms_timeout: "10s" # must be > 0 - kms_max_retries: 3 # must be >= 0 + signer_type: "kms" + kms: + provider: "aws" + aws: + key_id: "arn:aws:kms:us-east-1:123456789012:key/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + region: "us-east-1" # optional but recommended + profile: "prod" # optional; omit when using IAM role/env creds + timeout: "10s" # must be > 0 + max_retries: 3 # must be >= 0 ``` ## 3. Start as an aggregator @@ -72,8 +75,8 @@ You should see a startup log line: ## Troubleshooting -- `evnode.signer.kms_key_id is required when signer_type is awskms`: - Set `signer.kms_key_id`. +- `evnode.signer.kms.aws.key_id is required when signer.signer_type is kms and signer.kms.provider is aws`: + Set `signer.kms.aws.key_id`. - `unsupported key type from KMS: expected ed25519`: Recreate the key as `ECC_NIST_EDWARDS25519`. - `KMS Sign failed ...`: diff --git a/docs/guides/operations/gcp-kms-signer.md b/docs/guides/operations/gcp-kms-signer.md new file mode 100644 index 0000000000..676a86fbc3 --- /dev/null +++ b/docs/guides/operations/gcp-kms-signer.md @@ -0,0 +1,91 @@ +# Use GCP KMS Signer + +Use this guide to run `ev-node` with a Google Cloud KMS-backed signer (`signer_type: kms`, `kms.provider: gcp`) instead +of a local key file. + +## Prerequisites + +- A Google Cloud KMS asymmetric key version with: + - `purpose: ASYMMETRIC_SIGN` + - `algorithm: EC_SIGN_ED25519` +- IAM permissions for runtime: + - `cloudkms.cryptoKeyVersions.useToSign` + - `cloudkms.cryptoKeyVersions.viewPublicKey` +- Google credentials available to the node process: + - Application Default Credentials (recommended), or + - service-account JSON file. + +## 1. Create an ED25519 KMS key (example) + +```bash +gcloud kms keyrings create ev-node --location=global + +gcloud kms keys create signer \ + --location=global \ + --keyring=ev-node \ + --purpose=asymmetric-signing \ + --default-algorithm=ec-sign-ed25519 +``` + +Get key version resource name: + +```bash +gcloud kms keys versions list \ + --location=global \ + --keyring=ev-node \ + --key=signer +``` + +Use a full key version name like: +`projects//locations/global/keyRings/ev-node/cryptoKeys/signer/cryptoKeyVersions/1` + +Create and export credentials for a service account: + +```sh +# create +gcloud iam service-accounts create "kms-go-client" \ + --display-name "KMS Go Client Account" + +# set permissions +gcloud projects add-iam-policy-binding \ + --member "serviceAccount:kms-go-client@.iam.gserviceaccount.com" \ + --role "roles/cloudkms.publicKeyViewer" \ + --role "roles/cloudkms.signerVerifier" + +# export credentials file +gcloud iam service-accounts keys create /path/to/service-account.json \ + --iam-account "kms-go-client@.iam.gserviceaccount.com" +``` + +## 2. Configure `evnode.yaml` + +```yaml +signer: + signer_type: "kms" + kms: + provider: "gcp" + gcp: + key_name: "projects/my-project/locations/global/keyRings/ev-node/cryptoKeys/signer/cryptoKeyVersions/1" + credentials_file: "/path/to/service-account.json" # optional; ADC is used when omitted + timeout: "1s" # must be > 0 + max_retries: 3 # must be >= 0 +``` + +## 3. Start as an aggregator + +```bash +evnode start --evnode.node.aggregator +``` + +You should see a startup log line: + +`initialized GCP KMS signer via factory` + +## Troubleshooting + +- `evnode.signer.kms.gcp.key_name is required when signer.signer_type is kms and signer.kms.provider is gcp`: + Set `signer.kms.gcp.key_name`. +- `unsupported key type from KMS: expected ed25519`: + Recreate key with `EC_SIGN_ED25519`. +- `KMS Sign failed ...`: + Check IAM permissions, credentials, and network access to Cloud KMS. diff --git a/execution/evm/test/go.mod b/execution/evm/test/go.mod index 86c7d88e78..f6adb773f2 100644 --- a/execution/evm/test/go.mod +++ b/execution/evm/test/go.mod @@ -166,7 +166,7 @@ require ( go.etcd.io/bbolt v1.4.0-alpha.0.0.20240404170359-43604f3112c5 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect go.opentelemetry.io/otel v1.42.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.42.0 // indirect @@ -184,9 +184,9 @@ require ( golang.org/x/sys v0.42.0 // indirect golang.org/x/term v0.41.0 // indirect golang.org/x/text v0.35.0 // indirect - google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect + google.golang.org/genproto v0.0.0-20260217215200-42d3e9bedb6d // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260217215200-42d3e9bedb6d // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c // indirect google.golang.org/grpc v1.79.2 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/execution/evm/test/go.sum b/execution/evm/test/go.sum index ecf59d7d28..fb2aa3f4fb 100644 --- a/execution/evm/test/go.sum +++ b/execution/evm/test/go.sum @@ -1,5 +1,20 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE= +cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= +cloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM= +cloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= +cloud.google.com/go/compute v1.54.0 h1:4CKmnpO+40z44bKG5bdcKxQ7ocNpRtOc9SCLLUzze1w= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= +cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc= +cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU= +cloud.google.com/go/kms v1.26.0 h1:cK9mN2cf+9V63D3H1f6koxTatWy39aTI/hCjz1I+adU= +cloud.google.com/go/kms v1.26.0/go.mod h1:pHKOdFJm63hxBsiPkYtowZPltu9dW0MWvBa6IA4HM58= +cloud.google.com/go/longrunning v0.8.0 h1:LiKK77J3bx5gDLi4SMViHixjD2ohlkwBi+mKA7EhfW8= +cloud.google.com/go/longrunning v0.8.0/go.mod h1:UmErU2Onzi+fKDg2gR7dusz11Pe26aknR4kHmJJqIfk= cosmossdk.io/api v0.7.6 h1:PC20PcXy1xYKH2KU4RMurVoFjjKkCgYRbVAD4PdqUuY= cosmossdk.io/api v0.7.6/go.mod h1:IcxpYS5fMemZGqyYtErK7OqvdM0C8kdW3dq8Q/XIG38= cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s= @@ -343,9 +358,15 @@ github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo github.com/google/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us= github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.14 h1:yh8ncqsbUY4shRD5dA6RlzjJaT4hi3kII+zYw8wmLb8= +github.com/googleapis/enterprise-certificate-proxy v0.3.14/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg= +github.com/googleapis/gax-go/v2 v2.18.0 h1:jxP5Uuo3bxm3M6gGtV94P4lliVetoCB4Wk2x8QA86LI= +github.com/googleapis/gax-go/v2 v2.18.0/go.mod h1:uSzZN4a356eRG985CzJ3WfbFSpqkLTjsnhWGJR6EwrE= github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= @@ -774,8 +795,10 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= 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/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho= go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 h1:THuZiwpQZuHPul65w4WcwEnkX2QIuMT+UFoOrygtoJw= @@ -849,6 +872,8 @@ golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= 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= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -907,8 +932,8 @@ 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.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= -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/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -927,6 +952,8 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/api v0.272.0 h1:eLUQZGnAS3OHn31URRf9sAmRk3w2JjMx37d2k8AjJmA= +google.golang.org/api v0.272.0/go.mod h1:wKjowi5LNJc5qarNvDCvNQBn3rVK8nSy6jg2SwRwzIA= 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-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -934,12 +961,12 @@ google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk= -google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc= -google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0= -google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto v0.0.0-20260217215200-42d3e9bedb6d h1:vsOm753cOAMkt76efriTCDKjpCbK18XGHMJHo0JUKhc= +google.golang.org/genproto v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:0oz9d7g9QLSdv9/lgbIjowW1JoxMbxmBVNe8i6tORJI= +google.golang.org/genproto/googleapis/api v0.0.0-20260217215200-42d3e9bedb6d h1:EocjzKLywydp5uZ5tJ79iP6Q0UjDnyiHkGRWxuPBP8s= +google.golang.org/genproto/googleapis/api v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:48U2I+QQUYhsFrg2SY6r+nJzeOtjey7j//WBESw+qyQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c h1:xgCzyF2LFIO/0X2UAoVRiXKU5Xg6VjToG4i2/ecSswk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= 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= diff --git a/go.mod b/go.mod index 52411e6005..5351f003ac 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.25.6 retract v0.12.0 // Published by accident require ( + cloud.google.com/go/kms v1.26.0 connectrpc.com/connect v1.19.1 connectrpc.com/grpcreflect v1.3.0 github.com/aws/aws-sdk-go-v2 v1.41.4 @@ -43,11 +44,19 @@ require ( golang.org/x/crypto v0.49.0 golang.org/x/net v0.52.0 golang.org/x/sync v0.20.0 + google.golang.org/api v0.272.0 + google.golang.org/grpc v1.79.2 google.golang.org/protobuf v1.36.11 gotest.tools/v3 v3.5.2 ) require ( + cloud.google.com/go v0.123.0 // indirect + cloud.google.com/go/auth v0.18.2 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect + cloud.google.com/go/compute/metadata v0.9.0 // indirect + cloud.google.com/go/iam v1.5.3 // indirect + cloud.google.com/go/longrunning v0.8.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.19.12 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 // indirect @@ -74,6 +83,7 @@ require ( github.com/dunglas/httpsfv v1.1.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/fatih/color v1.18.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/filecoin-project/go-clock v0.1.0 // indirect github.com/flynn/noise v1.1.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect @@ -84,7 +94,10 @@ require ( github.com/google/flatbuffers v25.1.24+incompatible // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/gopacket v1.1.19 // indirect + github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect + github.com/googleapis/gax-go/v2 v2.18.0 // indirect github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect @@ -177,6 +190,8 @@ require ( github.com/wlynxg/anet v0.0.5 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 // indirect go.opentelemetry.io/otel/metric v1.42.0 // indirect go.opentelemetry.io/proto/otlp v1.10.0 // indirect @@ -189,16 +204,17 @@ require ( go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect golang.org/x/mod v0.33.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect golang.org/x/sys v0.42.0 // indirect golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 // indirect golang.org/x/text v0.35.0 // indirect - golang.org/x/time v0.12.0 // indirect + golang.org/x/time v0.15.0 // indirect golang.org/x/tools v0.42.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gonum.org/v1/gonum v0.17.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect - google.golang.org/grpc v1.79.2 // indirect + google.golang.org/genproto v0.0.0-20260217215200-42d3e9bedb6d // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260217215200-42d3e9bedb6d // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.4.1 // indirect ) diff --git a/go.sum b/go.sum index da455f95f7..1bad15db32 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,19 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE= +cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= +cloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM= +cloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= +cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc= +cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU= +cloud.google.com/go/kms v1.26.0 h1:cK9mN2cf+9V63D3H1f6koxTatWy39aTI/hCjz1I+adU= +cloud.google.com/go/kms v1.26.0/go.mod h1:pHKOdFJm63hxBsiPkYtowZPltu9dW0MWvBa6IA4HM58= +cloud.google.com/go/longrunning v0.8.0 h1:LiKK77J3bx5gDLi4SMViHixjD2ohlkwBi+mKA7EhfW8= +cloud.google.com/go/longrunning v0.8.0/go.mod h1:UmErU2Onzi+fKDg2gR7dusz11Pe26aknR4kHmJJqIfk= 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= @@ -77,6 +91,8 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= @@ -106,12 +122,19 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= +github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g= +github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4= +github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA= github.com/evstack/ev-node/core v1.0.0 h1:s0Tx0uWHme7SJn/ZNEtee4qNM8UO6PIxXnHhPbbKTz8= github.com/evstack/ev-node/core v1.0.0/go.mod h1:n2w/LhYQTPsi48m6lMj16YiIqsaQw6gxwjyJvR+B3sY= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/filecoin-project/go-clock v0.1.0 h1:SFbYIM75M8NnFm1yMHhN9Ahy3W5bEZV9gd6MPfXbKVU= github.com/filecoin-project/go-clock v0.1.0/go.mod h1:4uB/O4PvOjlx1VCMdZ9MyDZXRm//gkj1ELEbxfI1AZs= github.com/filecoin-project/go-jsonrpc v0.10.1 h1:iEhgrjO0+rawwOZWRNgexLrWGLA+IEUyWiRRL134Ob8= @@ -184,9 +207,15 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.14 h1:yh8ncqsbUY4shRD5dA6RlzjJaT4hi3kII+zYw8wmLb8= +github.com/googleapis/enterprise-certificate-proxy v0.3.14/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg= +github.com/googleapis/gax-go/v2 v2.18.0 h1:jxP5Uuo3bxm3M6gGtV94P4lliVetoCB4Wk2x8QA86LI= +github.com/googleapis/gax-go/v2 v2.18.0/go.mod h1:uSzZN4a356eRG985CzJ3WfbFSpqkLTjsnhWGJR6EwrE= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f h1:KMlcu9X58lhTA/KrfX8Bi1LQSO4pzoVjTiL3h4Jk+Zk= github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -425,6 +454,8 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= 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= @@ -538,6 +569,10 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= 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/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho= go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 h1:THuZiwpQZuHPul65w4WcwEnkX2QIuMT+UFoOrygtoJw= @@ -631,6 +666,8 @@ golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= 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= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -705,8 +742,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= -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/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -732,16 +769,20 @@ golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhS golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/api v0.272.0 h1:eLUQZGnAS3OHn31URRf9sAmRk3w2JjMx37d2k8AjJmA= +google.golang.org/api v0.272.0/go.mod h1:wKjowi5LNJc5qarNvDCvNQBn3rVK8nSy6jg2SwRwzIA= 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/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 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/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0= -google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto v0.0.0-20260217215200-42d3e9bedb6d h1:vsOm753cOAMkt76efriTCDKjpCbK18XGHMJHo0JUKhc= +google.golang.org/genproto v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:0oz9d7g9QLSdv9/lgbIjowW1JoxMbxmBVNe8i6tORJI= +google.golang.org/genproto/googleapis/api v0.0.0-20260217215200-42d3e9bedb6d h1:EocjzKLywydp5uZ5tJ79iP6Q0UjDnyiHkGRWxuPBP8s= +google.golang.org/genproto/googleapis/api v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:48U2I+QQUYhsFrg2SY6r+nJzeOtjey7j//WBESw+qyQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c h1:xgCzyF2LFIO/0X2UAoVRiXKU5Xg6VjToG4i2/ecSswk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= 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= diff --git a/pkg/cmd/run_node.go b/pkg/cmd/run_node.go index f2c84280de..b0a36c889a 100644 --- a/pkg/cmd/run_node.go +++ b/pkg/cmd/run_node.go @@ -137,8 +137,15 @@ func StartNode( return fmt.Errorf("initialize signer via factory: %w", err) } - if nodeConfig.Signer.SignerType == "awskms" { - logger.Info().Msg("initialized AWS KMS signer via factory") + if nodeConfig.Signer.SignerType == "kms" { + switch nodeConfig.Signer.KMS.Provider { + case "aws": + logger.Info().Msg("initialized AWS KMS signer via factory") + case "gcp": + logger.Info().Msg("initialized GCP KMS signer via factory") + default: + logger.Info().Str("provider", nodeConfig.Signer.KMS.Provider).Msg("initialized KMS signer via factory") + } } } diff --git a/pkg/config/config.go b/pkg/config/config.go index 5b40a9e1c3..da90489001 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -140,16 +140,26 @@ const ( FlagSignerType = FlagPrefixEvnode + "signer.signer_type" // FlagSignerPath is a flag for specifying the signer path FlagSignerPath = FlagPrefixEvnode + "signer.signer_path" - // FlagSignerKmsKeyID is a flag for specifying the KMS key ID - FlagSignerKmsKeyID = FlagPrefixEvnode + "signer.kms_key_id" - // FlagSignerKmsRegion is a flag for specifying the KMS region - FlagSignerKmsRegion = FlagPrefixEvnode + "signer.kms_region" - // FlagSignerKmsProfile is a flag for specifying the AWS profile - FlagSignerKmsProfile = FlagPrefixEvnode + "signer.kms_profile" - // FlagSignerKmsTimeout is a flag for specifying the KMS sign timeout - FlagSignerKmsTimeout = FlagPrefixEvnode + "signer.kms_timeout" - // FlagSignerKmsMaxRetries is a flag for specifying the KMS sign max retries - FlagSignerKmsMaxRetries = FlagPrefixEvnode + "signer.kms_max_retries" + // FlagSignerKmsProvider is a flag for specifying the signer KMS provider + FlagSignerKmsProvider = FlagPrefixEvnode + "signer.kms.provider" + // FlagSignerKmsAwsKeyID is a flag for specifying the AWS KMS key ID + FlagSignerKmsAwsKeyID = FlagPrefixEvnode + "signer.kms.aws.key_id" + // FlagSignerKmsAwsRegion is a flag for specifying the AWS KMS region + FlagSignerKmsAwsRegion = FlagPrefixEvnode + "signer.kms.aws.region" + // FlagSignerKmsAwsProfile is a flag for specifying the AWS profile + FlagSignerKmsAwsProfile = FlagPrefixEvnode + "signer.kms.aws.profile" + // FlagSignerKmsAwsTimeout is a flag for specifying the AWS KMS sign timeout + FlagSignerKmsAwsTimeout = FlagPrefixEvnode + "signer.kms.aws.timeout" + // FlagSignerKmsAwsMaxRetries is a flag for specifying the AWS KMS sign max retries + FlagSignerKmsAwsMaxRetries = FlagPrefixEvnode + "signer.kms.aws.max_retries" + // FlagSignerKmsGcpKeyName is a flag for specifying the GCP KMS key version resource name + FlagSignerKmsGcpKeyName = FlagPrefixEvnode + "signer.kms.gcp.key_name" + // FlagSignerKmsGcpCredentialsFile is a flag for specifying GCP credentials JSON file + FlagSignerKmsGcpCredentialsFile = FlagPrefixEvnode + "signer.kms.gcp.credentials_file" + // FlagSignerKmsGcpTimeout is a flag for specifying the GCP KMS sign timeout + FlagSignerKmsGcpTimeout = FlagPrefixEvnode + "signer.kms.gcp.timeout" + // FlagSignerKmsGcpMaxRetries is a flag for specifying the GCP KMS sign max retries + FlagSignerKmsGcpMaxRetries = FlagPrefixEvnode + "signer.kms.gcp.max_retries" // FlagSignerPassphraseFile is a flag for specifying the file containing the signer passphrase FlagSignerPassphraseFile = FlagPrefixEvnode + "signer.passphrase_file" @@ -302,13 +312,33 @@ type P2PConfig struct { // SignerConfig contains all signer configuration parameters type SignerConfig struct { - SignerType string `mapstructure:"signer_type" yaml:"signer_type" comment:"Type of remote signer to use (file, awskms)"` - SignerPath string `mapstructure:"signer_path" yaml:"signer_path" comment:"Path to the signer file or address"` - KmsKeyID string `mapstructure:"kms_key_id" yaml:"kms_key_id" comment:"AWS KMS Key ID or ARN for awskms signer"` - KmsRegion string `mapstructure:"kms_region" yaml:"kms_region" comment:"AWS Region for awskms signer"` - KmsProfile string `mapstructure:"kms_profile" yaml:"kms_profile" comment:"AWS Profile for awskms signer"` - KmsTimeout DurationWrapper `mapstructure:"kms_timeout" yaml:"kms_timeout" comment:"Timeout for individual AWS KMS Sign requests"` - KmsMaxRetries int `mapstructure:"kms_max_retries" yaml:"kms_max_retries" comment:"Maximum number of retries for transient AWS KMS failures"` + SignerType string `mapstructure:"signer_type" yaml:"signer_type" comment:"Type of remote signer to use (file, grpc, kms)"` + SignerPath string `mapstructure:"signer_path" yaml:"signer_path" comment:"Path to the signer file or address"` + KMS SignerKMSConfig `mapstructure:"kms" yaml:"kms"` +} + +// SignerKMSConfig contains cloud-KMS signer configuration. +type SignerKMSConfig struct { + Provider string `mapstructure:"provider" yaml:"provider" comment:"KMS provider for signer type 'kms' (aws, gcp)"` + AWS SignerAWSKMSConfig `mapstructure:"aws" yaml:"aws"` + GCP SignerGCPKMSConfig `mapstructure:"gcp" yaml:"gcp"` +} + +// SignerAWSKMSConfig contains AWS KMS signer configuration. +type SignerAWSKMSConfig struct { + KeyID string `mapstructure:"key_id" yaml:"key_id" comment:"AWS KMS Key ID or ARN"` + Region string `mapstructure:"region" yaml:"region" comment:"AWS region"` + Profile string `mapstructure:"profile" yaml:"profile" comment:"AWS profile"` + Timeout DurationWrapper `mapstructure:"timeout" yaml:"timeout" comment:"Timeout for individual AWS KMS Sign requests"` + MaxRetries int `mapstructure:"max_retries" yaml:"max_retries" comment:"Maximum number of retries for transient AWS KMS failures"` +} + +// SignerGCPKMSConfig contains GCP KMS signer configuration. +type SignerGCPKMSConfig struct { + KeyName string `mapstructure:"key_name" yaml:"key_name" comment:"GCP KMS CryptoKeyVersion resource name"` + CredentialsFile string `mapstructure:"credentials_file" yaml:"credentials_file" comment:"Path to Google credentials JSON (optional; defaults to ADC)"` + Timeout DurationWrapper `mapstructure:"timeout" yaml:"timeout" comment:"Timeout for individual GCP KMS Sign requests"` + MaxRetries int `mapstructure:"max_retries" yaml:"max_retries" comment:"Maximum number of retries for transient GCP KMS failures"` } // RPCConfig contains all RPC server configuration parameters @@ -407,15 +437,30 @@ func (c RaftConfig) Validate() error { // Validate validates the config and ensures that the root directory exists. // It creates the directory if it does not exist. func (c *Config) Validate() error { - if c.Signer.SignerType == "awskms" { - if c.Signer.KmsKeyID == "" { - return errors.New("evnode.signer.kms_key_id is required when signer_type is awskms") - } - if c.Signer.KmsTimeout.Duration <= 0 { - return errors.New("evnode.signer.kms_timeout must be positive") - } - if c.Signer.KmsMaxRetries < 0 { - return errors.New("evnode.signer.kms_max_retries must be non-negative") + if c.Signer.SignerType == "kms" { + switch c.Signer.KMS.Provider { + case "aws": + if c.Signer.KMS.AWS.KeyID == "" { + return errors.New("evnode.signer.kms.aws.key_id is required when signer.signer_type is kms and signer.kms.provider is aws") + } + if c.Signer.KMS.AWS.Timeout.Duration <= 0 { + return errors.New("evnode.signer.kms.aws.timeout must be positive") + } + if c.Signer.KMS.AWS.MaxRetries < 0 { + return errors.New("evnode.signer.kms.aws.max_retries must be non-negative") + } + case "gcp": + if c.Signer.KMS.GCP.KeyName == "" { + return errors.New("evnode.signer.kms.gcp.key_name is required when signer.signer_type is kms and signer.kms.provider is gcp") + } + if c.Signer.KMS.GCP.Timeout.Duration <= 0 { + return errors.New("evnode.signer.kms.gcp.timeout must be positive") + } + if c.Signer.KMS.GCP.MaxRetries < 0 { + return errors.New("evnode.signer.kms.gcp.max_retries must be non-negative") + } + default: + return errors.New("evnode.signer.kms.provider must be one of: aws, gcp when signer.signer_type is kms") } } if c.RootDir == "" { @@ -574,13 +619,18 @@ func AddFlags(cmd *cobra.Command) { cmd.Flags().Float64(FlagTracingSampleRate, instrDef.TracingSampleRate, "trace sampling rate (0.0-1.0)") // Signer configuration flags - cmd.Flags().String(FlagSignerType, def.Signer.SignerType, "type of signer to use (file, grpc, awskms)") + cmd.Flags().String(FlagSignerType, def.Signer.SignerType, "type of signer to use (file, grpc, kms)") cmd.Flags().String(FlagSignerPath, def.Signer.SignerPath, "path to the signer file or address") - cmd.Flags().String(FlagSignerKmsKeyID, def.Signer.KmsKeyID, "AWS KMS Key ID or ARN for awskms signer") - cmd.Flags().String(FlagSignerKmsRegion, def.Signer.KmsRegion, "AWS Region for awskms signer") - cmd.Flags().String(FlagSignerKmsProfile, def.Signer.KmsProfile, "AWS Profile for awskms signer") - cmd.Flags().Duration(FlagSignerKmsTimeout, def.Signer.KmsTimeout.Duration, "Timeout for individual AWS KMS Sign requests") - cmd.Flags().Int(FlagSignerKmsMaxRetries, def.Signer.KmsMaxRetries, "Maximum number of retries for transient AWS KMS failures") + cmd.Flags().String(FlagSignerKmsProvider, def.Signer.KMS.Provider, "KMS provider for signer type kms (aws, gcp)") + cmd.Flags().String(FlagSignerKmsAwsKeyID, def.Signer.KMS.AWS.KeyID, "AWS KMS Key ID or ARN for signer.kms.provider=aws") + cmd.Flags().String(FlagSignerKmsAwsRegion, def.Signer.KMS.AWS.Region, "AWS region for signer.kms.provider=aws") + cmd.Flags().String(FlagSignerKmsAwsProfile, def.Signer.KMS.AWS.Profile, "AWS profile for signer.kms.provider=aws") + cmd.Flags().Duration(FlagSignerKmsAwsTimeout, def.Signer.KMS.AWS.Timeout.Duration, "Timeout for individual AWS KMS Sign requests") + cmd.Flags().Int(FlagSignerKmsAwsMaxRetries, def.Signer.KMS.AWS.MaxRetries, "Maximum number of retries for transient AWS KMS failures") + cmd.Flags().String(FlagSignerKmsGcpKeyName, def.Signer.KMS.GCP.KeyName, "GCP KMS CryptoKeyVersion resource name for signer.kms.provider=gcp") + cmd.Flags().String(FlagSignerKmsGcpCredentialsFile, def.Signer.KMS.GCP.CredentialsFile, "Path to Google credentials JSON for signer.kms.provider=gcp (optional)") + cmd.Flags().Duration(FlagSignerKmsGcpTimeout, def.Signer.KMS.GCP.Timeout.Duration, "Timeout for individual GCP KMS Sign requests") + cmd.Flags().Int(FlagSignerKmsGcpMaxRetries, def.Signer.KMS.GCP.MaxRetries, "Maximum number of retries for transient GCP KMS failures") cmd.Flags().String(FlagSignerPassphraseFile, "", "path to file containing the signer passphrase (required for file signer and if aggregator is enabled)") cmd.MarkFlagsMutuallyExclusive(FlagLight, FlagAggregator) diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index a191ecf6af..2ae2b0ef66 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -107,11 +107,16 @@ func TestAddFlags(t *testing.T) { assertFlagValue(t, flags, FlagSignerPassphraseFile, "") assertFlagValue(t, flags, FlagSignerType, "file") assertFlagValue(t, flags, FlagSignerPath, DefaultConfig().Signer.SignerPath) - assertFlagValue(t, flags, FlagSignerKmsKeyID, DefaultConfig().Signer.KmsKeyID) - assertFlagValue(t, flags, FlagSignerKmsRegion, DefaultConfig().Signer.KmsRegion) - assertFlagValue(t, flags, FlagSignerKmsProfile, DefaultConfig().Signer.KmsProfile) - assertFlagValue(t, flags, FlagSignerKmsTimeout, DefaultConfig().Signer.KmsTimeout.Duration) - assertFlagValue(t, flags, FlagSignerKmsMaxRetries, DefaultConfig().Signer.KmsMaxRetries) + assertFlagValue(t, flags, FlagSignerKmsProvider, DefaultConfig().Signer.KMS.Provider) + assertFlagValue(t, flags, FlagSignerKmsAwsKeyID, DefaultConfig().Signer.KMS.AWS.KeyID) + assertFlagValue(t, flags, FlagSignerKmsAwsRegion, DefaultConfig().Signer.KMS.AWS.Region) + assertFlagValue(t, flags, FlagSignerKmsAwsProfile, DefaultConfig().Signer.KMS.AWS.Profile) + assertFlagValue(t, flags, FlagSignerKmsAwsTimeout, DefaultConfig().Signer.KMS.AWS.Timeout.Duration) + assertFlagValue(t, flags, FlagSignerKmsAwsMaxRetries, DefaultConfig().Signer.KMS.AWS.MaxRetries) + assertFlagValue(t, flags, FlagSignerKmsGcpKeyName, DefaultConfig().Signer.KMS.GCP.KeyName) + assertFlagValue(t, flags, FlagSignerKmsGcpCredentialsFile, DefaultConfig().Signer.KMS.GCP.CredentialsFile) + assertFlagValue(t, flags, FlagSignerKmsGcpTimeout, DefaultConfig().Signer.KMS.GCP.Timeout.Duration) + assertFlagValue(t, flags, FlagSignerKmsGcpMaxRetries, DefaultConfig().Signer.KMS.GCP.MaxRetries) // RPC flags assertFlagValue(t, flags, FlagRPCAddress, DefaultConfig().RPC.Address) @@ -123,7 +128,7 @@ func TestAddFlags(t *testing.T) { assertFlagValue(t, flags, FlagPruningInterval, DefaultConfig().Pruning.Interval.Duration) // Count the number of flags we're explicitly checking - expectedFlagCount := 72 // Update this number if you add more flag checks above + expectedFlagCount := 77 // Update this number if you add more flag checks above // Get the actual number of flags (both regular and persistent) actualFlagCount := 0 @@ -524,3 +529,89 @@ func TestBasedSequencerValidation(t *testing.T) { }) } } + +func TestSignerValidation(t *testing.T) { + tests := []struct { + name string + modify func(cfg *Config) + expectError string + }{ + { + name: "kms aws requires key id", + modify: func(cfg *Config) { + cfg.Signer.SignerType = "kms" + cfg.Signer.KMS.Provider = "aws" + cfg.Signer.KMS.AWS.KeyID = "" + }, + expectError: "evnode.signer.kms.aws.key_id is required when signer.signer_type is kms and signer.kms.provider is aws", + }, + { + name: "kms aws requires positive timeout", + modify: func(cfg *Config) { + cfg.Signer.SignerType = "kms" + cfg.Signer.KMS.Provider = "aws" + cfg.Signer.KMS.AWS.KeyID = "arn:aws:kms:eu-central-1:123456789012:key/test" + cfg.Signer.KMS.AWS.Timeout = DurationWrapper{0} + }, + expectError: "evnode.signer.kms.aws.timeout must be positive", + }, + { + name: "kms aws retries must be non-negative", + modify: func(cfg *Config) { + cfg.Signer.SignerType = "kms" + cfg.Signer.KMS.Provider = "aws" + cfg.Signer.KMS.AWS.KeyID = "arn:aws:kms:eu-central-1:123456789012:key/test" + cfg.Signer.KMS.AWS.MaxRetries = -1 + }, + expectError: "evnode.signer.kms.aws.max_retries must be non-negative", + }, + { + name: "kms gcp requires key name", + modify: func(cfg *Config) { + cfg.Signer.SignerType = "kms" + cfg.Signer.KMS.Provider = "gcp" + cfg.Signer.KMS.GCP.KeyName = "" + }, + expectError: "evnode.signer.kms.gcp.key_name is required when signer.signer_type is kms and signer.kms.provider is gcp", + }, + { + name: "kms gcp requires positive timeout", + modify: func(cfg *Config) { + cfg.Signer.SignerType = "kms" + cfg.Signer.KMS.Provider = "gcp" + cfg.Signer.KMS.GCP.KeyName = "projects/p/locations/global/keyRings/r/cryptoKeys/k/cryptoKeyVersions/1" + cfg.Signer.KMS.GCP.Timeout = DurationWrapper{0} + }, + expectError: "evnode.signer.kms.gcp.timeout must be positive", + }, + { + name: "kms gcp retries must be non-negative", + modify: func(cfg *Config) { + cfg.Signer.SignerType = "kms" + cfg.Signer.KMS.Provider = "gcp" + cfg.Signer.KMS.GCP.KeyName = "projects/p/locations/global/keyRings/r/cryptoKeys/k/cryptoKeyVersions/1" + cfg.Signer.KMS.GCP.MaxRetries = -1 + }, + expectError: "evnode.signer.kms.gcp.max_retries must be non-negative", + }, + { + name: "kms requires provider", + modify: func(cfg *Config) { + cfg.Signer.SignerType = "kms" + cfg.Signer.KMS.Provider = "" + }, + expectError: "evnode.signer.kms.provider must be one of: aws, gcp when signer.signer_type is kms", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := DefaultConfig() + cfg.RootDir = t.TempDir() + tt.modify(&cfg) + err := cfg.Validate() + require.Error(t, err) + assert.Contains(t, err.Error(), tt.expectError) + }) + } +} diff --git a/pkg/config/defaults.go b/pkg/config/defaults.go index 30903c3e65..91fe68e3fc 100644 --- a/pkg/config/defaults.go +++ b/pkg/config/defaults.go @@ -91,13 +91,24 @@ func DefaultConfig() Config { Trace: false, }, Signer: SignerConfig{ - SignerType: "file", - SignerPath: "config", - KmsKeyID: "", - KmsRegion: "", - KmsProfile: "", - KmsTimeout: DurationWrapper{10 * time.Second}, - KmsMaxRetries: 3, + SignerType: "file", + SignerPath: "config", + KMS: SignerKMSConfig{ + Provider: "", + AWS: SignerAWSKMSConfig{ + KeyID: "", + Region: "", + Profile: "", + Timeout: DurationWrapper{10 * time.Second}, + MaxRetries: 3, + }, + GCP: SignerGCPKMSConfig{ + KeyName: "", + CredentialsFile: "", + Timeout: DurationWrapper{10 * time.Second}, + MaxRetries: 3, + }, + }, }, RPC: RPCConfig{ Address: "127.0.0.1:7331", diff --git a/pkg/signer/aws/README.md b/pkg/signer/aws/README.md index d5a8234d22..2987eff35a 100644 --- a/pkg/signer/aws/README.md +++ b/pkg/signer/aws/README.md @@ -11,21 +11,25 @@ It uses KMS for `Sign` operations and caches the public key/address in memory af ## Configuration -Set `evnode.signer.signer_type` to `awskms` and provide at least `kms_key_id`. +Set `evnode.signer.signer_type` to `kms`, set `evnode.signer.kms.provider` to `aws`, +and provide at least `evnode.signer.kms.aws.key_id`. Example: ```yaml signer: - signer_type: awskms - kms_key_id: arn:aws:kms:eu-central-1:123456789012:key/00000000-0000-0000-0000-000000000000 - kms_region: eu-central-1 - kms_profile: default - kms_timeout: 1s - kms_max_retries: 3 + signer_type: kms + kms: + provider: aws + aws: + key_id: arn:aws:kms:eu-central-1:123456789012:key/00000000-0000-0000-0000-000000000000 + region: eu-central-1 + profile: default + timeout: 1s + max_retries: 3 ``` ## Notes -- `kms_timeout` is the timeout per KMS Sign request. -- `kms_max_retries` controls retries for transient KMS/API/network failures. +- `kms.aws.timeout` is the timeout per KMS Sign request. +- `kms.aws.max_retries` controls retries for transient KMS/API/network failures. diff --git a/pkg/signer/factory.go b/pkg/signer/factory.go index 3c7b7f52d5..187f625af2 100644 --- a/pkg/signer/factory.go +++ b/pkg/signer/factory.go @@ -10,6 +10,7 @@ import ( rollconf "github.com/evstack/ev-node/pkg/config" awssigner "github.com/evstack/ev-node/pkg/signer/aws" "github.com/evstack/ev-node/pkg/signer/file" + gcpsigner "github.com/evstack/ev-node/pkg/signer/gcp" ) // NewSigner creates a new Signer based on the configuration. @@ -48,12 +49,30 @@ func newSigner(ctx context.Context, config *rollconf.Config, passphrase string, return file.LoadFileSystemSigner(signerPath, []byte(passphrase)) - case "awskms": - opts := &awssigner.Options{ - Timeout: config.Signer.KmsTimeout.Duration, - MaxRetries: config.Signer.KmsMaxRetries, + case "kms": + switch config.Signer.KMS.Provider { + case "aws": + opts := &awssigner.Options{ + Timeout: config.Signer.KMS.AWS.Timeout.Duration, + MaxRetries: config.Signer.KMS.AWS.MaxRetries, + } + return awssigner.NewKmsSigner( + ctx, + config.Signer.KMS.AWS.Region, + config.Signer.KMS.AWS.Profile, + config.Signer.KMS.AWS.KeyID, + opts, + ) + case "gcp": + opts := &gcpsigner.Options{ + CredentialsFile: config.Signer.KMS.GCP.CredentialsFile, + Timeout: config.Signer.KMS.GCP.Timeout.Duration, + MaxRetries: config.Signer.KMS.GCP.MaxRetries, + } + return gcpsigner.NewKmsSigner(ctx, config.Signer.KMS.GCP.KeyName, opts) + default: + return nil, fmt.Errorf("unknown kms signer provider: %s", config.Signer.KMS.Provider) } - return awssigner.NewKmsSigner(ctx, config.Signer.KmsRegion, config.Signer.KmsProfile, config.Signer.KmsKeyID, opts) default: return nil, fmt.Errorf("unknown signer type: %s", config.Signer.SignerType) diff --git a/pkg/signer/factory_test.go b/pkg/signer/factory_test.go index f8b02ca252..efa135db94 100644 --- a/pkg/signer/factory_test.go +++ b/pkg/signer/factory_test.go @@ -32,14 +32,32 @@ func TestNewSigner_ErrorPaths(t *testing.T) { pass: "", wantErr: "passphrase is required when using local file signer", }, - "awskms-empty-key-id": { + "kms-aws-empty-key-id": { mutateCfg: func(cfg *rollconf.Config) { - cfg.Signer.SignerType = "awskms" - cfg.Signer.KmsKeyID = "" + cfg.Signer.SignerType = "kms" + cfg.Signer.KMS.Provider = "aws" + cfg.Signer.KMS.AWS.KeyID = "" }, pass: "test-passphrase", wantErr: "aws kms key ID is required", }, + "kms-gcp-empty-key-name": { + mutateCfg: func(cfg *rollconf.Config) { + cfg.Signer.SignerType = "kms" + cfg.Signer.KMS.Provider = "gcp" + cfg.Signer.KMS.GCP.KeyName = "" + }, + pass: "test-passphrase", + wantErr: "gcp kms key name is required", + }, + "kms-unknown-provider": { + mutateCfg: func(cfg *rollconf.Config) { + cfg.Signer.SignerType = "kms" + cfg.Signer.KMS.Provider = "azure" + }, + pass: "test-passphrase", + wantErr: "unknown kms signer provider: azure", + }, } for name, spec := range specs { diff --git a/pkg/signer/gcp/README.md b/pkg/signer/gcp/README.md new file mode 100644 index 0000000000..8c70ffe735 --- /dev/null +++ b/pkg/signer/gcp/README.md @@ -0,0 +1,35 @@ +# GCP KMS Signer + +This package implements `signer.Signer` using Google Cloud KMS. + +It uses KMS for `AsymmetricSign` operations and caches the public key/address in memory after initialization. + +## Requirements + +- Google Cloud credentials must be available via Application Default Credentials (ADC), or + `kms.gcp.credentials_file` must be set. +- The configured KMS key version must be an asymmetric **Ed25519** key version. + +## Configuration + +Set `evnode.signer.signer_type` to `kms`, set `evnode.signer.kms.provider` to `gcp`, +and provide at least `evnode.signer.kms.gcp.key_name`. + +Example: + +```yaml +signer: + signer_type: kms + kms: + provider: gcp + gcp: + key_name: projects/my-project/locations/global/keyRings/ev-node/cryptoKeys/signer/cryptoKeyVersions/1 + credentials_file: /path/to/service-account.json + timeout: 10s + max_retries: 3 +``` + +## Notes + +- `kms.gcp.timeout` is the timeout per KMS Sign request. +- `kms.gcp.max_retries` controls retries for transient KMS/API/network failures. diff --git a/pkg/signer/gcp/signer.go b/pkg/signer/gcp/signer.go new file mode 100644 index 0000000000..5cec1c108f --- /dev/null +++ b/pkg/signer/gcp/signer.go @@ -0,0 +1,306 @@ +// Package gcp implements a signer.Signer backed by Google Cloud KMS. +// It delegates signing to a remote KMS key and caches the public key locally. +package gcp + +import ( + "context" + "crypto/ed25519" + "crypto/sha256" + "crypto/x509" + "encoding/pem" + "errors" + "fmt" + "hash/crc32" + "net" + "sync" + "time" + + kms "cloud.google.com/go/kms/apiv1" + "cloud.google.com/go/kms/apiv1/kmspb" + "github.com/libp2p/go-libp2p/core/crypto" + "google.golang.org/api/option" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/wrapperspb" +) + +var castagnoliTable = crc32.MakeTable(crc32.Castagnoli) + +const ( + baseRetryBackoff = 100 * time.Millisecond + maxRetryBackoff = 5 * time.Second +) + +// KMSClient is the subset of the Google Cloud KMS client API that KmsSigner needs. +// This allows mocking in tests. +type KMSClient interface { + AsymmetricSign(ctx context.Context, req *kmspb.AsymmetricSignRequest) (*kmspb.AsymmetricSignResponse, error) + GetPublicKey(ctx context.Context, req *kmspb.GetPublicKeyRequest) (*kmspb.PublicKey, error) +} + +type cloudKMSClient struct { + client *kms.KeyManagementClient +} + +func (c *cloudKMSClient) AsymmetricSign(ctx context.Context, req *kmspb.AsymmetricSignRequest) (*kmspb.AsymmetricSignResponse, error) { + return c.client.AsymmetricSign(ctx, req) +} + +func (c *cloudKMSClient) GetPublicKey(ctx context.Context, req *kmspb.GetPublicKeyRequest) (*kmspb.PublicKey, error) { + return c.client.GetPublicKey(ctx, req) +} + +// Options configures optional KmsSigner behavior. +type Options struct { + // CredentialsFile is an optional path to a Google credentials JSON file. + // If empty, Application Default Credentials are used. + CredentialsFile string + // Timeout for individual KMS Sign API calls. Default: 10s. + Timeout time.Duration + // MaxRetries for transient KMS failures during Sign. Default: 3. + MaxRetries int +} + +func (o *Options) timeout() time.Duration { return o.Timeout } + +func (o *Options) maxRetries() int { return o.MaxRetries } + +// KmsSigner implements the signer.Signer interface using Google Cloud KMS. +type KmsSigner struct { + client KMSClient + keyName string + opts Options + mu sync.RWMutex + pubKey crypto.PubKey + address []byte +} + +// NewKmsSigner creates a new Signer backed by a Google Cloud KMS Ed25519 key version. +// It uses Application Default Credentials unless opts.CredentialsFile is provided. +func NewKmsSigner(ctx context.Context, keyName string, opts *Options) (*KmsSigner, error) { + if keyName == "" { + return nil, fmt.Errorf("gcp kms key name is required") + } + + var clientOpts []option.ClientOption + if opts != nil && opts.CredentialsFile != "" { + clientOpts = append(clientOpts, option.WithAuthCredentialsFile(option.ServiceAccount, opts.CredentialsFile)) + } + + client, err := kms.NewKeyManagementClient(ctx, clientOpts...) + if err != nil { + return nil, fmt.Errorf("failed to create Google Cloud KMS client: %w", err) + } + + return kmsSignerFromClient(ctx, &cloudKMSClient{client: client}, keyName, opts) +} + +// kmsSignerFromClient creates a KmsSigner from an existing KMS client. +// Useful for testing with a mock client. +func kmsSignerFromClient(ctx context.Context, client KMSClient, keyName string, opts *Options) (*KmsSigner, error) { + if keyName == "" { + return nil, fmt.Errorf("gcp kms key name is required") + } + if client == nil { + return nil, fmt.Errorf("gcp kms client is required") + } + + o := Options{Timeout: 10 * time.Second, MaxRetries: 3} + if opts != nil { + if opts.Timeout > 0 { + o.Timeout = opts.Timeout + } + if opts.MaxRetries >= 0 { + o.MaxRetries = opts.MaxRetries + } + } + + s := &KmsSigner{ + client: client, + keyName: keyName, + opts: o, + } + + // Fetch and cache the public key eagerly so we fail fast on misconfiguration. + if err := s.fetchPublicKey(ctx); err != nil { + return nil, fmt.Errorf("failed to fetch public key from Google Cloud KMS: %w", err) + } + + return s, nil +} + +// fetchPublicKey retrieves the public key from KMS and caches it. +func (s *KmsSigner) fetchPublicKey(ctx context.Context) error { + out, err := s.client.GetPublicKey(ctx, &kmspb.GetPublicKeyRequest{Name: s.keyName}) + if err != nil { + return fmt.Errorf("KMS GetPublicKey failed: %w", err) + } + + block, _ := pem.Decode([]byte(out.GetPem())) + if block == nil { + return fmt.Errorf("failed to decode PEM public key") + } + + pub, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return fmt.Errorf("failed to parse KMS public key: %w", err) + } + + edPubKey, ok := pub.(ed25519.PublicKey) + if !ok { + return fmt.Errorf("unsupported key type from KMS: expected ed25519, got %T", pub) + } + + cryptoPubKey, err := crypto.UnmarshalEd25519PublicKey(edPubKey) + if err != nil { + return fmt.Errorf("failed to convert to libp2p pubkey: %w", err) + } + + bz, err := cryptoPubKey.Raw() + if err != nil { + return fmt.Errorf("failed to get raw pubkey bytes: %w", err) + } + + address := sha256.Sum256(bz) + + s.mu.Lock() + defer s.mu.Unlock() + + s.pubKey = cryptoPubKey + s.address = address[:] + + return nil +} + +// Sign signs a message using the remote KMS key with configurable timeout +// and retry with exponential backoff. +func (s *KmsSigner) Sign(ctx context.Context, message []byte) ([]byte, error) { + var lastErr error + maxRetries := s.opts.maxRetries() + timeout := s.opts.timeout() + maxAttempts := maxRetries + 1 + + for attempt := 0; attempt < maxAttempts; attempt++ { + if attempt > 0 { + // Exponential backoff with cap: 100ms, 200ms, 400ms, ... up to 5s. + backoff := retryBackoff(attempt) + select { + case <-ctx.Done(): + return nil, fmt.Errorf("KMS Sign canceled: %w", ctx.Err()) + case <-time.After(backoff): + } + } + + callCtx, cancel := context.WithTimeout(ctx, timeout) + dataCRC32C := int64(crc32.Checksum(message, castagnoliTable)) + out, err := s.client.AsymmetricSign(callCtx, &kmspb.AsymmetricSignRequest{ + Name: s.keyName, + Data: message, + DataCrc32C: wrapperspb.Int64(dataCRC32C), + }) + cancel() + + if err != nil { + lastErr = err + if !isRetryableKMSError(err) { + return nil, fmt.Errorf("KMS Sign failed with non-retryable error: %w", err) + } + continue + } + + if err := verifySignResponse(out); err != nil { + lastErr = err + continue + } + + return out.GetSignature(), nil + } + + return nil, fmt.Errorf("KMS Sign failed after %d attempts: %w", maxAttempts, lastErr) +} + +func retryBackoff(attempt int) time.Duration { + if attempt <= 1 { + return baseRetryBackoff + } + + backoff := baseRetryBackoff + for i := 1; i < attempt; i++ { + if backoff >= maxRetryBackoff/2 { + return maxRetryBackoff + } + backoff *= 2 + } + + if backoff > maxRetryBackoff { + return maxRetryBackoff + } + + return backoff +} + +func verifySignResponse(out *kmspb.AsymmetricSignResponse) error { + if !out.GetVerifiedDataCrc32C() { + return fmt.Errorf("KMS Sign integrity check failed: verified_data_crc32c is false") + } + + signatureCRC32C := out.GetSignatureCrc32C() + if signatureCRC32C == nil { + return fmt.Errorf("KMS Sign integrity check failed: signature_crc32c is missing") + } + + signature := out.GetSignature() + expectedCRC32C := int64(crc32.Checksum(signature, castagnoliTable)) + if signatureCRC32C.GetValue() != expectedCRC32C { + return fmt.Errorf("KMS Sign integrity check failed: signature_crc32c mismatch") + } + + return nil +} + +// GetPublic returns the cached public key. +func (s *KmsSigner) GetPublic() (crypto.PubKey, error) { + s.mu.RLock() + pubKey := s.pubKey + s.mu.RUnlock() + + if pubKey == nil { + return nil, fmt.Errorf("public key not loaded") + } + + return pubKey, nil +} + +// GetAddress returns the cached address derived from the public key. +func (s *KmsSigner) GetAddress() ([]byte, error) { + s.mu.RLock() + defer s.mu.RUnlock() + + if s.address == nil { + return nil, fmt.Errorf("address not loaded") + } + + return s.address, nil +} + +func isRetryableKMSError(err error) bool { + if errors.Is(err, context.Canceled) { + return false + } + + if errors.Is(err, context.DeadlineExceeded) { + return true + } + + var netErr net.Error + if errors.As(err, &netErr) { + return true + } + + switch status.Code(err) { + case codes.DeadlineExceeded, codes.Unavailable, codes.ResourceExhausted, codes.Aborted, codes.Internal: + return true + default: + return false + } +} diff --git a/pkg/signer/gcp/signer_test.go b/pkg/signer/gcp/signer_test.go new file mode 100644 index 0000000000..a5f46875f8 --- /dev/null +++ b/pkg/signer/gcp/signer_test.go @@ -0,0 +1,338 @@ +package gcp + +import ( + "context" + "crypto/ed25519" + "crypto/x509" + "encoding/pem" + "fmt" + "hash/crc32" + "sync/atomic" + "testing" + "time" + + "cloud.google.com/go/kms/apiv1/kmspb" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/wrapperspb" +) + +// mockKMSClient is a test double implementing KMSClient. +type mockKMSClient struct { + publicKeyPEM string + signFn func(ctx context.Context, req *kmspb.AsymmetricSignRequest) (*kmspb.AsymmetricSignResponse, error) + getPubFn func(ctx context.Context, req *kmspb.GetPublicKeyRequest) (*kmspb.PublicKey, error) +} + +func (m *mockKMSClient) AsymmetricSign(ctx context.Context, req *kmspb.AsymmetricSignRequest) (*kmspb.AsymmetricSignResponse, error) { + if m.signFn != nil { + return m.signFn(ctx, req) + } + return &kmspb.AsymmetricSignResponse{Signature: []byte("mock-signature")}, nil +} + +func (m *mockKMSClient) GetPublicKey(ctx context.Context, req *kmspb.GetPublicKeyRequest) (*kmspb.PublicKey, error) { + if m.getPubFn != nil { + return m.getPubFn(ctx, req) + } + return &kmspb.PublicKey{Pem: m.publicKeyPEM}, nil +} + +// generateTestEd25519PEM generates an Ed25519 key pair and returns +// the public key in PEM format. +func generateTestEd25519PEM(t *testing.T) (ed25519.PublicKey, string) { + t.Helper() + pub, _, err := ed25519.GenerateKey(nil) + require.NoError(t, err) + + der, err := x509.MarshalPKIXPublicKey(pub) + require.NoError(t, err) + + pemBytes := pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: der}) + return pub, string(pemBytes) +} + +func TestNewKmsSignerFromClient_Success(t *testing.T) { + _, publicKeyPEM := generateTestEd25519PEM(t) + + mock := &mockKMSClient{publicKeyPEM: publicKeyPEM} + s, err := kmsSignerFromClient(context.Background(), mock, "projects/p/locations/global/keyRings/r/cryptoKeys/k/cryptoKeyVersions/1", nil) + require.NoError(t, err) + require.NotNil(t, s) + + // Verify public key was cached. + pubKey, err := s.GetPublic() + require.NoError(t, err) + require.NotNil(t, pubKey) + + // Verify address was cached. + addr, err := s.GetAddress() + require.NoError(t, err) + assert.Len(t, addr, 32) // sha256 output +} + +func TestNewKmsSignerFromClient_EmptyKeyName(t *testing.T) { + _, err := kmsSignerFromClient(context.Background(), &mockKMSClient{}, "", nil) + require.Error(t, err) + assert.Contains(t, err.Error(), "key name is required") +} + +func TestNewKmsSignerFromClient_NilClient(t *testing.T) { + _, err := kmsSignerFromClient(context.Background(), nil, "projects/p/locations/global/keyRings/r/cryptoKeys/k/cryptoKeyVersions/1", nil) + require.Error(t, err) + assert.Contains(t, err.Error(), "client is required") +} + +func TestNewKmsSignerFromClient_GetPublicKeyFails(t *testing.T) { + mock := &mockKMSClient{ + getPubFn: func(_ context.Context, _ *kmspb.GetPublicKeyRequest) (*kmspb.PublicKey, error) { + return nil, fmt.Errorf("permission denied") + }, + } + + _, err := kmsSignerFromClient(context.Background(), mock, "projects/p/locations/global/keyRings/r/cryptoKeys/k/cryptoKeyVersions/1", nil) + require.Error(t, err) + assert.Contains(t, err.Error(), "permission denied") +} + +func TestSign_Success(t *testing.T) { + _, publicKeyPEM := generateTestEd25519PEM(t) + + expectedSig := []byte("test-signature-bytes") + expectedMsg := []byte("hello world") + mock := &mockKMSClient{ + publicKeyPEM: publicKeyPEM, + signFn: func(_ context.Context, req *kmspb.AsymmetricSignRequest) (*kmspb.AsymmetricSignResponse, error) { + assert.Equal(t, "projects/p/locations/global/keyRings/r/cryptoKeys/k/cryptoKeyVersions/1", req.Name) + assert.Equal(t, expectedMsg, req.Data) + require.NotNil(t, req.DataCrc32C) + assert.Equal(t, int64(crc32.Checksum(expectedMsg, castagnoliTable)), req.DataCrc32C.GetValue()) + return &kmspb.AsymmetricSignResponse{ + Signature: expectedSig, + VerifiedDataCrc32C: true, + SignatureCrc32C: wrapperspb.Int64(int64(crc32.Checksum(expectedSig, castagnoliTable))), + }, nil + }, + } + + s, err := kmsSignerFromClient(context.Background(), mock, "projects/p/locations/global/keyRings/r/cryptoKeys/k/cryptoKeyVersions/1", nil) + require.NoError(t, err) + + sig, err := s.Sign(context.Background(), expectedMsg) + require.NoError(t, err) + assert.Equal(t, expectedSig, sig) +} + +func TestSign_KMSFailure(t *testing.T) { + _, publicKeyPEM := generateTestEd25519PEM(t) + + var calls int32 + mock := &mockKMSClient{ + publicKeyPEM: publicKeyPEM, + signFn: func(_ context.Context, _ *kmspb.AsymmetricSignRequest) (*kmspb.AsymmetricSignResponse, error) { + atomic.AddInt32(&calls, 1) + return nil, status.Error(codes.Unavailable, "temporarily unavailable") + }, + } + + s, err := kmsSignerFromClient(context.Background(), mock, "projects/p/locations/global/keyRings/r/cryptoKeys/k/cryptoKeyVersions/1", nil) + require.NoError(t, err) + + _, err = s.Sign(context.Background(), []byte("hello world")) + require.Error(t, err) + assert.Contains(t, err.Error(), "KMS Sign failed") + assert.Equal(t, int32(4), atomic.LoadInt32(&calls), "default retries should make 4 attempts") +} + +func TestSign_MaxRetriesZero_DisablesRetries(t *testing.T) { + _, publicKeyPEM := generateTestEd25519PEM(t) + + var calls int32 + mock := &mockKMSClient{ + publicKeyPEM: publicKeyPEM, + signFn: func(_ context.Context, _ *kmspb.AsymmetricSignRequest) (*kmspb.AsymmetricSignResponse, error) { + atomic.AddInt32(&calls, 1) + return nil, status.Error(codes.Unavailable, "temporarily unavailable") + }, + } + + s, err := kmsSignerFromClient(context.Background(), mock, "projects/p/locations/global/keyRings/r/cryptoKeys/k/cryptoKeyVersions/1", &Options{MaxRetries: 0}) + require.NoError(t, err) + + _, err = s.Sign(context.Background(), []byte("hello world")) + require.Error(t, err) + assert.Equal(t, int32(1), atomic.LoadInt32(&calls), "max retries 0 should only make one attempt") +} + +func TestSign_NonRetryableError_NoRetries(t *testing.T) { + _, publicKeyPEM := generateTestEd25519PEM(t) + + var calls int32 + mock := &mockKMSClient{ + publicKeyPEM: publicKeyPEM, + signFn: func(_ context.Context, _ *kmspb.AsymmetricSignRequest) (*kmspb.AsymmetricSignResponse, error) { + atomic.AddInt32(&calls, 1) + return nil, status.Error(codes.PermissionDenied, "permission denied") + }, + } + + s, err := kmsSignerFromClient(context.Background(), mock, "projects/p/locations/global/keyRings/r/cryptoKeys/k/cryptoKeyVersions/1", &Options{MaxRetries: 3}) + require.NoError(t, err) + + _, err = s.Sign(context.Background(), []byte("hello world")) + require.Error(t, err) + assert.Contains(t, err.Error(), "non-retryable") + assert.Equal(t, int32(1), atomic.LoadInt32(&calls), "non-retryable errors should fail fast") +} + +func TestSign_IntegrityCheckVerifiedDataFalse_RetriesAndFails(t *testing.T) { + _, publicKeyPEM := generateTestEd25519PEM(t) + + var calls int32 + mock := &mockKMSClient{ + publicKeyPEM: publicKeyPEM, + signFn: func(_ context.Context, _ *kmspb.AsymmetricSignRequest) (*kmspb.AsymmetricSignResponse, error) { + atomic.AddInt32(&calls, 1) + sig := []byte("sig") + return &kmspb.AsymmetricSignResponse{ + Signature: sig, + VerifiedDataCrc32C: false, + SignatureCrc32C: wrapperspb.Int64(int64(crc32.Checksum(sig, castagnoliTable))), + }, nil + }, + } + + s, err := kmsSignerFromClient( + context.Background(), + mock, + "projects/p/locations/global/keyRings/r/cryptoKeys/k/cryptoKeyVersions/1", + &Options{MaxRetries: 1}, + ) + require.NoError(t, err) + + _, err = s.Sign(context.Background(), []byte("hello world")) + require.Error(t, err) + assert.Contains(t, err.Error(), "verified_data_crc32c is false") + assert.Equal(t, int32(2), atomic.LoadInt32(&calls), "integrity failures should be retried") +} + +func TestSign_IntegrityCheckSignatureCRC32CMismatch_RetriesAndFails(t *testing.T) { + _, publicKeyPEM := generateTestEd25519PEM(t) + + var calls int32 + mock := &mockKMSClient{ + publicKeyPEM: publicKeyPEM, + signFn: func(_ context.Context, _ *kmspb.AsymmetricSignRequest) (*kmspb.AsymmetricSignResponse, error) { + atomic.AddInt32(&calls, 1) + return &kmspb.AsymmetricSignResponse{ + Signature: []byte("sig"), + VerifiedDataCrc32C: true, + SignatureCrc32C: wrapperspb.Int64(12345), + }, nil + }, + } + + s, err := kmsSignerFromClient( + context.Background(), + mock, + "projects/p/locations/global/keyRings/r/cryptoKeys/k/cryptoKeyVersions/1", + &Options{MaxRetries: 1}, + ) + require.NoError(t, err) + + _, err = s.Sign(context.Background(), []byte("hello world")) + require.Error(t, err) + assert.Contains(t, err.Error(), "signature_crc32c mismatch") + assert.Equal(t, int32(2), atomic.LoadInt32(&calls), "integrity failures should be retried") +} + +func TestSign_IntegrityCheckRecoversOnRetry(t *testing.T) { + _, publicKeyPEM := generateTestEd25519PEM(t) + + var calls int32 + expectedSig := []byte("valid-signature") + mock := &mockKMSClient{ + publicKeyPEM: publicKeyPEM, + signFn: func(_ context.Context, _ *kmspb.AsymmetricSignRequest) (*kmspb.AsymmetricSignResponse, error) { + attempt := atomic.AddInt32(&calls, 1) + if attempt == 1 { + return &kmspb.AsymmetricSignResponse{ + Signature: []byte("corrupted"), + VerifiedDataCrc32C: false, + SignatureCrc32C: wrapperspb.Int64(1), + }, nil + } + return &kmspb.AsymmetricSignResponse{ + Signature: expectedSig, + VerifiedDataCrc32C: true, + SignatureCrc32C: wrapperspb.Int64(int64(crc32.Checksum(expectedSig, castagnoliTable))), + }, nil + }, + } + + s, err := kmsSignerFromClient( + context.Background(), + mock, + "projects/p/locations/global/keyRings/r/cryptoKeys/k/cryptoKeyVersions/1", + &Options{MaxRetries: 2}, + ) + require.NoError(t, err) + + got, err := s.Sign(context.Background(), []byte("hello world")) + require.NoError(t, err) + assert.Equal(t, expectedSig, got) + assert.Equal(t, int32(2), atomic.LoadInt32(&calls), "second attempt should succeed") +} + +func TestRetryBackoff_Capped(t *testing.T) { + testCases := []struct { + name string + attempt int + expected time.Duration + }{ + {name: "attempt 1", attempt: 1, expected: 100 * time.Millisecond}, + {name: "attempt 2", attempt: 2, expected: 200 * time.Millisecond}, + {name: "attempt 6", attempt: 6, expected: 3200 * time.Millisecond}, + {name: "attempt 7 capped", attempt: 7, expected: 5 * time.Second}, + {name: "attempt 10 capped", attempt: 10, expected: 5 * time.Second}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, tc.expected, retryBackoff(tc.attempt)) + }) + } +} + +func TestGetPublic_Cached(t *testing.T) { + pub, publicKeyPEM := generateTestEd25519PEM(t) + + mock := &mockKMSClient{publicKeyPEM: publicKeyPEM} + s, err := kmsSignerFromClient(context.Background(), mock, "projects/p/locations/global/keyRings/r/cryptoKeys/k/cryptoKeyVersions/1", nil) + require.NoError(t, err) + + cryptoPub, err := s.GetPublic() + require.NoError(t, err) + + raw, err := cryptoPub.Raw() + require.NoError(t, err) + assert.Equal(t, []byte(pub), raw) +} + +func TestGetAddress_Deterministic(t *testing.T) { + _, publicKeyPEM := generateTestEd25519PEM(t) + + mock := &mockKMSClient{publicKeyPEM: publicKeyPEM} + s, err := kmsSignerFromClient(context.Background(), mock, "projects/p/locations/global/keyRings/r/cryptoKeys/k/cryptoKeyVersions/1", nil) + require.NoError(t, err) + + addr1, err := s.GetAddress() + require.NoError(t, err) + + addr2, err := s.GetAddress() + require.NoError(t, err) + + assert.Equal(t, addr1, addr2, "address should be deterministic") +} diff --git a/test/e2e/evm_aws_kms_e2e_test.go b/test/e2e/evm_kms_e2e_test.go similarity index 63% rename from test/e2e/evm_aws_kms_e2e_test.go rename to test/e2e/evm_kms_e2e_test.go index 94e6eb963d..7d882a24f4 100644 --- a/test/e2e/evm_aws_kms_e2e_test.go +++ b/test/e2e/evm_kms_e2e_test.go @@ -5,6 +5,7 @@ package e2e import ( "context" "flag" + "fmt" "os" "path/filepath" "strings" @@ -22,6 +23,7 @@ import ( // as the block signer. The test is skipped unless KMS env vars are provided. // export EVNODE_E2E_AWS_KMS_KEY_ID= // export EVNODE_E2E_AWS_KMS_REGION= +// export EVNODE_E2E_AWS_KMS_PROFILE= // go test -v -tags e2e,evm -run TestEvmSequencerWithAWSKMSSignerE2E -count=1 --evm-binary=$(pwd)/../../build/testapp func TestEvmSequencerWithAWSKMSSignerE2E(t *testing.T) { if testing.Short() { @@ -41,8 +43,85 @@ func TestEvmSequencerWithAWSKMSSignerE2E(t *testing.T) { ) kmsProfile := os.Getenv("EVNODE_E2E_AWS_KMS_PROFILE") + signerArgs := []string{ + "--evnode.signer.signer_type=kms", + "--evnode.signer.kms.provider=aws", + "--evnode.signer.kms.aws.key_id=" + kmsKeyID, + "--evnode.signer.kms.aws.timeout=10s", + "--evnode.signer.kms.aws.max_retries=3", + } + if kmsRegion != "" { + signerArgs = append(signerArgs, "--evnode.signer.kms.aws.region="+kmsRegion) + } + if kmsProfile != "" { + signerArgs = append(signerArgs, "--evnode.signer.kms.aws.profile="+kmsProfile) + } + + runEvmSequencerWithKMSSignerE2E(t, "aws", signerArgs) +} + +// TestEvmSequencerWithGCPKMSSignerE2E validates an EVM sequencer using GCP KMS +// as the block signer. The test is skipped unless KMS env vars are provided. +// export EVNODE_E2E_GCP_KMS_KEY_NAME= +// export EVNODE_E2E_GCP_KMS_CREDENTIALS_FILE= # optional; defaults to ADC when unset +// go test -v -tags e2e,evm -run TestEvmSequencerWithGCPKMSSignerE2E -count=1 --evm-binary=$(pwd)/../../build/testapp +func TestEvmSequencerWithGCPKMSSignerE2E(t *testing.T) { + if testing.Short() { + t.Skip("skip e2e in short mode") + } + flag.Parse() + + kmsKeyName := os.Getenv("EVNODE_E2E_GCP_KMS_KEY_NAME") + if kmsKeyName == "" { + t.Skip("set EVNODE_E2E_GCP_KMS_KEY_NAME to run GCP KMS EVM e2e test") + } + + kmsCredentialsFile := os.Getenv("EVNODE_E2E_GCP_KMS_CREDENTIALS_FILE") + + signerArgs := []string{ + "--evnode.signer.signer_type=kms", + "--evnode.signer.kms.provider=gcp", + "--evnode.signer.kms.gcp.key_name=" + kmsKeyName, + "--evnode.signer.kms.gcp.timeout=10s", + "--evnode.signer.kms.gcp.max_retries=3", + } + if kmsCredentialsFile != "" { + signerArgs = append(signerArgs, "--evnode.signer.kms.gcp.credentials_file="+kmsCredentialsFile) + } + + runEvmSequencerWithKMSSignerE2E(t, "gcp", signerArgs) +} + +func firstNonEmptyEVMKMS(values ...string) string { + for _, v := range values { + if v != "" { + return v + } + } + return "" +} + +func requireEVMBinary(t testing.TB, sut *SystemUnderTest) string { + t.Helper() + + helpOutput, err := sut.RunCmd(evmSingleBinaryPath, "start", "--help") + require.NoError(t, err, "failed to run start --help on -evm-binary=%q", evmSingleBinaryPath) + + if !strings.Contains(helpOutput, "--evm.jwt-secret-file") { + t.Skipf( + "-evm-binary=%q does not look like the EVM binary (missing --evm.jwt-secret-file). Pass the correct binary path via -evm-binary", + evmSingleBinaryPath, + ) + } + + return evmSingleBinaryPath +} + +func runEvmSequencerWithKMSSignerE2E(t *testing.T, provider string, signerArgs []string) { + t.Helper() + workDir := t.TempDir() - sequencerHome := filepath.Join(workDir, "evm-kms-agg") + sequencerHome := filepath.Join(workDir, fmt.Sprintf("evm-kms-%s-agg", provider)) sut := NewSystemUnderTest(t) evmBinary := requireEVMBinary(t, sut) @@ -55,20 +134,11 @@ func TestEvmSequencerWithAWSKMSSignerE2E(t *testing.T) { "init", "--home", sequencerHome, "--evnode.node.aggregator=true", - "--evnode.signer.signer_type=awskms", - "--evnode.signer.kms_key_id=" + kmsKeyID, - "--evnode.signer.kms_timeout=10s", - "--evnode.signer.kms_max_retries=3", - } - if kmsRegion != "" { - initArgs = append(initArgs, "--evnode.signer.kms_region="+kmsRegion) - } - if kmsProfile != "" { - initArgs = append(initArgs, "--evnode.signer.kms_profile="+kmsProfile) } + initArgs = append(initArgs, signerArgs...) output, err := sut.RunCmd(evmBinary, initArgs...) - require.NoError(t, err, "failed to init evm sequencer with awskms signer: %s", output) + require.NoError(t, err, "failed to init evm sequencer with %s kms signer: %s", provider, output) startArgs := []string{ "start", @@ -82,21 +152,12 @@ func TestEvmSequencerWithAWSKMSSignerE2E(t *testing.T) { "--evnode.da.batching_strategy", "immediate", "--evnode.rpc.address", evmEnv.Endpoints.GetRollkitRPCListen(), "--evnode.p2p.listen_address", evmEnv.Endpoints.GetRollkitP2PAddress(), - "--evnode.signer.signer_type=awskms", - "--evnode.signer.kms_key_id=" + kmsKeyID, - "--evnode.signer.kms_timeout=10s", - "--evnode.signer.kms_max_retries=3", "--evm.jwt-secret-file", jwtSecretFile, "--evm.genesis-hash", evmEnv.GenesisHash, "--evm.engine-url", evmEnv.Endpoints.GetSequencerEngineURL(), "--evm.eth-url", evmEnv.Endpoints.GetSequencerEthURL(), } - if kmsRegion != "" { - startArgs = append(startArgs, "--evnode.signer.kms_region="+kmsRegion) - } - if kmsProfile != "" { - startArgs = append(startArgs, "--evnode.signer.kms_profile="+kmsProfile) - } + startArgs = append(startArgs, signerArgs...) sut.ExecCmd(evmBinary, startArgs...) sut.AwaitNodeUp(t, evmEnv.Endpoints.GetRollkitRPCAddress(), NodeStartupTimeout) @@ -113,30 +174,5 @@ func TestEvmSequencerWithAWSKMSSignerE2E(t *testing.T) { return evm.CheckTxIncluded(client, tx.Hash()) }, 20*time.Second, 500*time.Millisecond, "tx should be included in a block") - t.Logf("KMS-backed EVM tx included: %s", tx.Hash().Hex()) -} - -func firstNonEmptyEVMKMS(values ...string) string { - for _, v := range values { - if v != "" { - return v - } - } - return "" -} - -func requireEVMBinary(t testing.TB, sut *SystemUnderTest) string { - t.Helper() - - helpOutput, err := sut.RunCmd(evmSingleBinaryPath, "start", "--help") - require.NoError(t, err, "failed to run start --help on -evm-binary=%q", evmSingleBinaryPath) - - if !strings.Contains(helpOutput, "--evm.jwt-secret-file") { - t.Skipf( - "-evm-binary=%q does not look like the EVM binary (missing --evm.jwt-secret-file). Pass the correct binary path via -evm-binary", - evmSingleBinaryPath, - ) - } - - return evmSingleBinaryPath + t.Logf("%s KMS-backed EVM tx included: %s", strings.ToUpper(provider), tx.Hash().Hex()) } diff --git a/test/e2e/go.mod b/test/e2e/go.mod index ddba0c9889..73eed97078 100644 --- a/test/e2e/go.mod +++ b/test/e2e/go.mod @@ -28,6 +28,13 @@ replace ( ) require ( + cloud.google.com/go v0.123.0 // indirect + cloud.google.com/go/auth v0.18.2 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect + cloud.google.com/go/compute/metadata v0.9.0 // indirect + cloud.google.com/go/iam v1.5.3 // indirect + cloud.google.com/go/kms v1.26.0 // indirect + cloud.google.com/go/longrunning v0.8.0 // indirect connectrpc.com/connect v1.19.1 // indirect connectrpc.com/grpcreflect v1.3.0 // indirect cosmossdk.io/api v0.9.2 // indirect @@ -151,7 +158,10 @@ require ( github.com/google/go-cmp v0.7.0 // indirect github.com/google/gopacket v1.1.19 // indirect github.com/google/orderedcode v0.0.1 // indirect + github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect + github.com/googleapis/gax-go/v2 v2.18.0 // indirect github.com/gorilla/handlers v1.5.2 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/websocket v1.5.3 // indirect @@ -298,6 +308,7 @@ require ( go.etcd.io/bbolt v1.4.0 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect go.opentelemetry.io/otel v1.42.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 // indirect @@ -316,18 +327,20 @@ require ( golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect golang.org/x/mod v0.33.0 // indirect golang.org/x/net v0.52.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect golang.org/x/sync v0.20.0 // indirect golang.org/x/sys v0.42.0 // indirect golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 // indirect golang.org/x/term v0.41.0 // indirect golang.org/x/text v0.35.0 // indirect - golang.org/x/time v0.12.0 // indirect + golang.org/x/time v0.15.0 // indirect golang.org/x/tools v0.42.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gonum.org/v1/gonum v0.17.0 // indirect - google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect + google.golang.org/api v0.272.0 // indirect + google.golang.org/genproto v0.0.0-20260217215200-42d3e9bedb6d // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260217215200-42d3e9bedb6d // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c // indirect google.golang.org/grpc v1.79.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/test/e2e/go.sum b/test/e2e/go.sum index 189caf5356..01cf97755b 100644 --- a/test/e2e/go.sum +++ b/test/e2e/go.sum @@ -1,18 +1,25 @@ +cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= +cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14= -cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU= -cloud.google.com/go/auth v0.16.4 h1:fXOAIQmkApVvcIn7Pc2+5J8QTMVbUGLscnSVNl11su8= -cloud.google.com/go/auth v0.16.4/go.mod h1:j10ncYwjX/g3cdX7GpEzsdM+d+ZNsXAbb6qXA7p1Y5M= +cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE= +cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= +cloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM= +cloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= -cloud.google.com/go/compute v1.38.0 h1:MilCLYQW2m7Dku8hRIIKo4r0oKastlD74sSu16riYKs= cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= -cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8= -cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE= -cloud.google.com/go/storage v1.41.0 h1:RusiwatSu6lHeEXe3kglxakAmAbfV+rhtPqA6i8RBx0= -cloud.google.com/go/storage v1.41.0/go.mod h1:J1WCa/Z2FcgdEDuPUY8DxT5I+d9mFKsCepp5vR6Sq80= +cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc= +cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU= +cloud.google.com/go/kms v1.26.0 h1:cK9mN2cf+9V63D3H1f6koxTatWy39aTI/hCjz1I+adU= +cloud.google.com/go/kms v1.26.0/go.mod h1:pHKOdFJm63hxBsiPkYtowZPltu9dW0MWvBa6IA4HM58= +cloud.google.com/go/longrunning v0.8.0 h1:LiKK77J3bx5gDLi4SMViHixjD2ohlkwBi+mKA7EhfW8= +cloud.google.com/go/longrunning v0.8.0/go.mod h1:UmErU2Onzi+fKDg2gR7dusz11Pe26aknR4kHmJJqIfk= +cloud.google.com/go/monitoring v1.24.3 h1:dde+gMNc0UhPZD1Azu6at2e79bfdztVDS5lvhOdsgaE= +cloud.google.com/go/monitoring v1.24.3/go.mod h1:nYP6W0tm3N9H/bOw8am7t62YTzZY+zUeQ+Bi6+2eonI= +cloud.google.com/go/storage v1.56.0 h1:iixmq2Fse2tqxMbWhLWC9HfBj1qdxqAmiK8/eqtsLxI= +cloud.google.com/go/storage v1.56.0/go.mod h1:Tpuj6t4NweCLzlNbw9Z9iwxEkrSem20AetIeH/shgVU= 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= @@ -66,6 +73,12 @@ github.com/DataDog/datadog-go v4.8.3+incompatible/go.mod h1:LButxg5PwREeZtORoXG3 github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/DataDog/zstd v1.5.7 h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE= github.com/DataDog/zstd v1.5.7/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 h1:owcC2UnmsZycprQ5RfRgjydWhuoxg71LUfyiQdijZuM= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0/go.mod h1:ZPpqegjbE99EPKsu3iUWV22A04wzGPcAY/ziSIQEEgs= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 h1:Ron4zCA/yk6U7WOBXhTJcDpsUBG9npumK6xw2auFltQ= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0/go.mod h1:cSgYe11MCNYunTnRXrKiR/tHc0eoKjICUuWpNZoVCOo= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= @@ -210,6 +223,8 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E= github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= @@ -332,7 +347,12 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= +github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g= +github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4= +github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA= github.com/ethereum/c-kzg-4844/v2 v2.1.6 h1:xQymkKCT5E2Jiaoqf3v4wsNgjZLY0lRSkZn27fRjSls= github.com/ethereum/c-kzg-4844/v2 v2.1.6/go.mod h1:8HMkUZ5JRv4hpw/XUrYWSQNAUzhHMg2UDb/U+5m+XNw= github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk= @@ -374,6 +394,8 @@ github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwv github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= +github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= @@ -499,10 +521,10 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= -github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= -github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= -github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= +github.com/googleapis/enterprise-certificate-proxy v0.3.14 h1:yh8ncqsbUY4shRD5dA6RlzjJaT4hi3kII+zYw8wmLb8= +github.com/googleapis/enterprise-certificate-proxy v0.3.14/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg= +github.com/googleapis/gax-go/v2 v2.18.0 h1:jxP5Uuo3bxm3M6gGtV94P4lliVetoCB4Wk2x8QA86LI= +github.com/googleapis/gax-go/v2 v2.18.0/go.mod h1:uSzZN4a356eRG985CzJ3WfbFSpqkLTjsnhWGJR6EwrE= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f h1:KMlcu9X58lhTA/KrfX8Bi1LQSO4pzoVjTiL3h4Jk+Zk= github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -947,6 +969,8 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= 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= @@ -1058,6 +1082,8 @@ github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= +github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= +github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= @@ -1150,6 +1176,8 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= 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/contrib/detectors/gcp v1.39.0 h1:kWRNZMsfBHZ+uHjiH4y7Etn2FK26LAGkNFw7RHv1DhE= +go.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= @@ -1290,8 +1318,8 @@ golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ= -golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= 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= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1404,8 +1432,8 @@ golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/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= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1444,8 +1472,8 @@ golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6f gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= -google.golang.org/api v0.247.0 h1:tSd/e0QrUlLsrwMKmkbQhYVa109qIintOls2Wh6bngc= -google.golang.org/api v0.247.0/go.mod h1:r1qZOPmxXffXg6xS5uhx16Fa/UFY8QU/K4bfKrnvovM= +google.golang.org/api v0.272.0 h1:eLUQZGnAS3OHn31URRf9sAmRk3w2JjMx37d2k8AjJmA= +google.golang.org/api v0.272.0/go.mod h1:wKjowi5LNJc5qarNvDCvNQBn3rVK8nSy6jg2SwRwzIA= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1461,12 +1489,12 @@ google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= -google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s= -google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0= -google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto v0.0.0-20260217215200-42d3e9bedb6d h1:vsOm753cOAMkt76efriTCDKjpCbK18XGHMJHo0JUKhc= +google.golang.org/genproto v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:0oz9d7g9QLSdv9/lgbIjowW1JoxMbxmBVNe8i6tORJI= +google.golang.org/genproto/googleapis/api v0.0.0-20260217215200-42d3e9bedb6d h1:EocjzKLywydp5uZ5tJ79iP6Q0UjDnyiHkGRWxuPBP8s= +google.golang.org/genproto/googleapis/api v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:48U2I+QQUYhsFrg2SY6r+nJzeOtjey7j//WBESw+qyQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c h1:xgCzyF2LFIO/0X2UAoVRiXKU5Xg6VjToG4i2/ecSswk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=