From d2dd7525d6ede7322abce8169178196cf52ab307 Mon Sep 17 00:00:00 2001 From: stackman27 Date: Thu, 26 Feb 2026 12:13:42 -0800 Subject: [PATCH 01/15] Add additional providers --- chain/canton/provider/authentication/oauth.go | 170 ++++++++++++++++++ engine/cld/chains/chains.go | 56 +++++- engine/cld/config/env/config.go | 24 ++- 3 files changed, 240 insertions(+), 10 deletions(-) create mode 100644 chain/canton/provider/authentication/oauth.go diff --git a/chain/canton/provider/authentication/oauth.go b/chain/canton/provider/authentication/oauth.go new file mode 100644 index 000000000..b7b5a5818 --- /dev/null +++ b/chain/canton/provider/authentication/oauth.go @@ -0,0 +1,170 @@ +package authentication + +import ( + "context" + "crypto/rand" + "crypto/tls" + "encoding/base64" + "fmt" + "net" + "net/http" + "os/exec" + "runtime" + "time" + + "golang.org/x/oauth2" + "golang.org/x/oauth2/clientcredentials" + "google.golang.org/grpc/credentials" +) + +var _ Provider = (*OIDCProvider)(nil) + +// OIDCProvider implements Provider using OAuth2/OIDC token flows (client credentials or authorization code). +type OIDCProvider struct { + tokenSource oauth2.TokenSource +} + +// NewClientCredentialsProvider creates a provider that fetches tokens using the OAuth2 client credentials flow. +// Use in CI where ClientID, ClientSecret and AuthURL are available; tokens are obtained automatically. +func NewClientCredentialsProvider(ctx context.Context, authURL, clientID, clientSecret string) (*OIDCProvider, error) { + tokenURL := fmt.Sprintf("%s/v1/token", authURL) + + oauthCfg := &clientcredentials.Config{ + ClientID: clientID, + ClientSecret: clientSecret, + TokenURL: tokenURL, + Scopes: []string{"daml_ledger_api"}, + } + + tokenSource := oauthCfg.TokenSource(ctx) + + return &OIDCProvider{ + tokenSource: tokenSource, + }, nil +} + +// NewAuthorizationCodeProvider creates a provider that uses the OAuth2 authorization code flow with PKCE. +// It starts a local callback server, opens the browser to the auth URL, and exchanges the code for a token. +// Use locally to skip canton-login; only ClientID and AuthURL are required. +func NewAuthorizationCodeProvider(ctx context.Context, authURL, clientID string) (*OIDCProvider, error) { + verifier := oauth2.GenerateVerifier() + + port := 8400 + authEndpoint := fmt.Sprintf("%s/v1/authorize", authURL) + tokenEndpoint := fmt.Sprintf("%s/v1/token", authURL) + redirectURL := fmt.Sprintf("http://localhost:%d", port) + + oauthCfg := &oauth2.Config{ + ClientID: clientID, + RedirectURL: redirectURL + "/callback", + Scopes: []string{"openid", "daml_ledger_api"}, + Endpoint: oauth2.Endpoint{AuthURL: authEndpoint, TokenURL: tokenEndpoint}, + } + + state := generateState() + authCodeURL := oauthCfg.AuthCodeURL(state, oauth2.S256ChallengeOption(verifier)) + + callbackChan := make(chan *oauth2.Token) + + serveMux := http.NewServeMux() + serveMux.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) { + q := r.URL.Query() + code := q.Get("code") + receivedState := q.Get("state") + + if receivedState != state { + http.Error(w, "Invalid state parameter", http.StatusBadRequest) + return + } + + token, err := oauthCfg.Exchange(ctx, code, oauth2.VerifierOption(verifier)) + if err != nil { + http.Error(w, "Token exchange failed: "+err.Error(), http.StatusInternalServerError) + return + } + + callbackChan <- token + + html := ` + +Authentication Complete + +

Authentication complete!

+

You can safely close this window.

+ + +` + w.Header().Set("Content-Type", "text/html; charset=utf-8") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(html)) + }) + + server := http.Server{ + Addr: fmt.Sprintf(":%d", port), + Handler: serveMux, + ReadHeaderTimeout: 5 * time.Second, + } + + listener, err := new(net.ListenConfig).Listen(ctx, "tcp", server.Addr) + if err != nil { + return nil, fmt.Errorf("listening on port %d: %w", port, err) + } + + serverErr := make(chan error, 1) + go func() { + serverErr <- server.Serve(listener) + }() + + openBrowser(authCodeURL) + + select { + case err := <-serverErr: + _ = server.Shutdown(ctx) + return nil, fmt.Errorf("callback server error: %w", err) + case token := <-callbackChan: + tokenSource := oauthCfg.TokenSource(ctx, token) + _ = server.Shutdown(ctx) + return &OIDCProvider{ + tokenSource: tokenSource, + }, nil + case <-ctx.Done(): + _ = server.Shutdown(ctx) + return nil, ctx.Err() + } +} + +func (p *OIDCProvider) TokenSource() oauth2.TokenSource { + return p.tokenSource +} + +func (p *OIDCProvider) TransportCredentials() credentials.TransportCredentials { + return credentials.NewTLS(&tls.Config{ + MinVersion: tls.VersionTLS12, + }) +} + +func (p *OIDCProvider) PerRPCCredentials() credentials.PerRPCCredentials { + return secureTokenSource{ + TokenSource: p.tokenSource, + } +} + +func generateState() string { + b := make([]byte, 16) + if _, err := rand.Read(b); err != nil { + panic(err) + } + return base64.RawURLEncoding.EncodeToString(b) +} + +// openBrowser opens the default browser to url on supported platforms; otherwise it is a no-op. +func openBrowser(url string) { + switch runtime.GOOS { + case "darwin": + _ = exec.Command("open", url).Start() + case "linux": + _ = exec.Command("xdg-open", url).Start() + case "windows": + _ = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start() + } +} diff --git a/engine/cld/chains/chains.go b/engine/cld/chains/chains.go index c4e820ef1..910da0a59 100644 --- a/engine/cld/chains/chains.go +++ b/engine/cld/chains/chains.go @@ -217,10 +217,10 @@ func newChainLoaders( lggr.Info("Skipping Ton chains, no private key found in secrets") } - if cfg.Canton.JWTToken != "" { + if cantonAuthConfigured(cfg.Canton) { loaders[chainsel.FamilyCanton] = newChainLoaderCanton(networks, cfg) } else { - lggr.Info("Skipping Canton chains, no JWT token found in secrets") + lggr.Info("Skipping Canton chains, no Canton auth configured (set auth_type and jwt_token, or auth_url+client_id for OAuth)") } return loaders @@ -747,13 +747,11 @@ func (l *chainLoaderCanton) Load(ctx context.Context, selector uint64) (fchain.B return nil, fmt.Errorf("canton network %d: no participants found in metadata", selector) } - if l.cfg.Canton.JWTToken == "" { - return nil, fmt.Errorf("canton network %d: JWT token is required", selector) + authProvider, err := l.cantonAuthProvider(ctx, selector) + if err != nil { + return nil, err } - // Use TLS-enforcing auth provider for Canton participant endpoints. - authProvider := cantonauth.NewStaticProvider(l.cfg.Canton.JWTToken) - participants := make([]cantonprov.ParticipantConfig, len(md.Participants)) for i, participantMD := range md.Participants { participants[i] = cantonprov.ParticipantConfig{ @@ -779,6 +777,50 @@ func (l *chainLoaderCanton) Load(ctx context.Context, selector uint64) (fchain.B return c, nil } +// cantonAuthConfigured returns true if Canton auth is configured for at least one scheme (static, client_credentials, or authorization_code). +func cantonAuthConfigured(c cfgenv.CantonConfig) bool { + switch c.AuthType { + case cfgenv.CantonAuthTypeClientCredentials: + return c.AuthURL != "" && c.ClientID != "" && c.ClientSecret != "" + case cfgenv.CantonAuthTypeAuthorizationCode: + return c.AuthURL != "" && c.ClientID != "" + default: + // static or empty (backward compat: jwt_token alone enables Canton) + return c.JWTToken != "" + } +} + +// cantonAuthProvider builds a Canton auth Provider from config. Caller must ensure cantonAuthConfigured(cfg.Canton) is true. +func (l *chainLoaderCanton) cantonAuthProvider(ctx context.Context, selector uint64) (cantonauth.Provider, error) { + c := l.cfg.Canton + switch c.AuthType { + case cfgenv.CantonAuthTypeClientCredentials: + if c.AuthURL == "" || c.ClientID == "" || c.ClientSecret == "" { + return nil, fmt.Errorf("canton network %d: client_credentials requires auth_url, client_id, and client_secret", selector) + } + oidc, err := cantonauth.NewClientCredentialsProvider(ctx, c.AuthURL, c.ClientID, c.ClientSecret) + if err != nil { + return nil, fmt.Errorf("canton network %d: client_credentials auth: %w", selector, err) + } + return oidc, nil + case cfgenv.CantonAuthTypeAuthorizationCode: + if c.AuthURL == "" || c.ClientID == "" { + return nil, fmt.Errorf("canton network %d: authorization_code requires auth_url and client_id", selector) + } + oidc, err := cantonauth.NewAuthorizationCodeProvider(ctx, c.AuthURL, c.ClientID) + if err != nil { + return nil, fmt.Errorf("canton network %d: authorization_code auth: %w", selector, err) + } + return oidc, nil + default: + // static or empty + if c.JWTToken == "" { + return nil, fmt.Errorf("canton network %d: JWT token is required for static auth", selector) + } + return cantonauth.NewStaticProvider(c.JWTToken), nil + } +} + // useKMS returns true if both KeyID and KeyRegion are set in the provided KMS config. func useKMS(kmsCfg cfgenv.KMSConfig) bool { return kmsCfg.KeyID != "" && kmsCfg.KeyRegion != "" diff --git a/engine/cld/config/env/config.go b/engine/cld/config/env/config.go index fb5d66b00..a6bd4bf82 100644 --- a/engine/cld/config/env/config.go +++ b/engine/cld/config/env/config.go @@ -82,14 +82,28 @@ type TronConfig struct { DeployerKey string `mapstructure:"deployer_key" yaml:"deployer_key"` // Secret: The private key of the deployer account. } +// CantonAuthType is the authentication scheme for Canton participant APIs. +const ( + CantonAuthTypeStatic = "static" // Pre-obtained JWT (e.g. from canton-login). + CantonAuthTypeClientCredentials = "client_credentials" // CI: fetch token with client_id + client_secret + auth_url. + CantonAuthTypeAuthorizationCode = "authorization_code" // Local: browser flow with client_id + auth_url. +) + // CantonConfig is the configuration for the Canton Chains. // // WARNING: This data type contains sensitive fields and should not be logged or set in file // configuration. type CantonConfig struct { - // JWT token for authenticating with Canton participants. This token will be used for all participants. - // For more complex scenarios with different tokens per participant, use the network metadata. - JWTToken string `mapstructure:"jwt_token" yaml:"jwt_token"` // Secret: JWT token for Canton participant authentication. + // AuthType selects how to obtain the token: "static" (jwt_token), "client_credentials" (CI), or "authorization_code" (local browser). + AuthType string `mapstructure:"auth_type" yaml:"auth_type"` + // JWT token for static auth. Used when auth_type is "static". + JWTToken string `mapstructure:"jwt_token" yaml:"jwt_token"` // Secret + // AuthURL is the OIDC base URL (e.g. https://auth.example.com). Token URL is AuthURL/v1/token, authorize is AuthURL/v1/authorize. + AuthURL string `mapstructure:"auth_url" yaml:"auth_url"` + // ClientID is the OAuth2 client ID. Used for client_credentials and authorization_code. + ClientID string `mapstructure:"client_id" yaml:"client_id"` // Secret + // ClientSecret is the OAuth2 client secret. Required only for client_credentials (CI). + ClientSecret string `mapstructure:"client_secret" yaml:"client_secret"` // Secret } // JobDistributorConfig is the configuration for connecting and authenticating to the Job @@ -247,7 +261,11 @@ var ( "onchain.stellar.deployer_key": {"ONCHAIN_STELLAR_DEPLOYER_KEY"}, "onchain.ton.deployer_key": {"ONCHAIN_TON_DEPLOYER_KEY", "TON_DEPLOYER_KEY"}, "onchain.ton.wallet_version": {"ONCHAIN_TON_WALLET_VERSION", "TON_WALLET_VERSION"}, + "onchain.canton.auth_type": {"ONCHAIN_CANTON_AUTH_TYPE"}, "onchain.canton.jwt_token": {"ONCHAIN_CANTON_JWT_TOKEN"}, + "onchain.canton.auth_url": {"ONCHAIN_CANTON_AUTH_URL"}, + "onchain.canton.client_id": {"ONCHAIN_CANTON_CLIENT_ID"}, + "onchain.canton.client_secret": {"ONCHAIN_CANTON_CLIENT_SECRET"}, "offchain.job_distributor.auth.cognito_app_client_id": {"OFFCHAIN_JD_AUTH_COGNITO_APP_CLIENT_ID", "JD_AUTH_COGNITO_APP_CLIENT_ID"}, "offchain.job_distributor.auth.cognito_app_client_secret": {"OFFCHAIN_JD_AUTH_COGNITO_APP_CLIENT_SECRET", "JD_AUTH_COGNITO_APP_CLIENT_SECRET"}, "offchain.job_distributor.auth.aws_region": {"OFFCHAIN_JD_AUTH_AWS_REGION", "JD_AUTH_AWS_REGION"}, From f2eb21d4a990075b249f86354fd42f5be845e1aa Mon Sep 17 00:00:00 2001 From: stackman27 Date: Thu, 26 Feb 2026 13:04:44 -0800 Subject: [PATCH 02/15] fix ci --- chain/canton/provider/authentication/oauth.go | 24 +++++++++++-------- engine/cld/chains/chains.go | 1 + engine/cld/config/env/config_test.go | 12 ++++++++-- engine/cld/config/env/testdata/config.yml | 4 ++++ .../testdata/config_with_optional_values.yml | 4 ++++ 5 files changed, 33 insertions(+), 12 deletions(-) diff --git a/chain/canton/provider/authentication/oauth.go b/chain/canton/provider/authentication/oauth.go index b7b5a5818..d8e0fc1ba 100644 --- a/chain/canton/provider/authentication/oauth.go +++ b/chain/canton/provider/authentication/oauth.go @@ -10,6 +10,7 @@ import ( "net/http" "os/exec" "runtime" + "strconv" "time" "golang.org/x/oauth2" @@ -27,7 +28,7 @@ type OIDCProvider struct { // NewClientCredentialsProvider creates a provider that fetches tokens using the OAuth2 client credentials flow. // Use in CI where ClientID, ClientSecret and AuthURL are available; tokens are obtained automatically. func NewClientCredentialsProvider(ctx context.Context, authURL, clientID, clientSecret string) (*OIDCProvider, error) { - tokenURL := fmt.Sprintf("%s/v1/token", authURL) + tokenURL := authURL + "/v1/token" oauthCfg := &clientcredentials.Config{ ClientID: clientID, @@ -50,9 +51,9 @@ func NewAuthorizationCodeProvider(ctx context.Context, authURL, clientID string) verifier := oauth2.GenerateVerifier() port := 8400 - authEndpoint := fmt.Sprintf("%s/v1/authorize", authURL) - tokenEndpoint := fmt.Sprintf("%s/v1/token", authURL) - redirectURL := fmt.Sprintf("http://localhost:%d", port) + authEndpoint := authURL + "/v1/authorize" + tokenEndpoint := authURL + "/v1/token" + redirectURL := "http://localhost:" + strconv.Itoa(port) oauthCfg := &oauth2.Config{ ClientID: clientID, @@ -100,7 +101,7 @@ func NewAuthorizationCodeProvider(ctx context.Context, authURL, clientID string) }) server := http.Server{ - Addr: fmt.Sprintf(":%d", port), + Addr: ":" + strconv.Itoa(port), Handler: serveMux, ReadHeaderTimeout: 5 * time.Second, } @@ -115,15 +116,17 @@ func NewAuthorizationCodeProvider(ctx context.Context, authURL, clientID string) serverErr <- server.Serve(listener) }() - openBrowser(authCodeURL) + openBrowser(ctx, authCodeURL) select { case err := <-serverErr: _ = server.Shutdown(ctx) + return nil, fmt.Errorf("callback server error: %w", err) case token := <-callbackChan: tokenSource := oauthCfg.TokenSource(ctx, token) _ = server.Shutdown(ctx) + return &OIDCProvider{ tokenSource: tokenSource, }, nil @@ -144,6 +147,7 @@ func (p *OIDCProvider) TransportCredentials() credentials.TransportCredentials { } func (p *OIDCProvider) PerRPCCredentials() credentials.PerRPCCredentials { + return secureTokenSource{ TokenSource: p.tokenSource, } @@ -158,13 +162,13 @@ func generateState() string { } // openBrowser opens the default browser to url on supported platforms; otherwise it is a no-op. -func openBrowser(url string) { +func openBrowser(ctx context.Context, url string) { switch runtime.GOOS { case "darwin": - _ = exec.Command("open", url).Start() + _ = exec.CommandContext(ctx, "open", url).Start() case "linux": - _ = exec.Command("xdg-open", url).Start() + _ = exec.CommandContext(ctx, "xdg-open", url).Start() case "windows": - _ = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start() + _ = exec.CommandContext(ctx, "rundll32", "url.dll,FileProtocolHandler", url).Start() } } diff --git a/engine/cld/chains/chains.go b/engine/cld/chains/chains.go index 910da0a59..58795e9f3 100644 --- a/engine/cld/chains/chains.go +++ b/engine/cld/chains/chains.go @@ -802,6 +802,7 @@ func (l *chainLoaderCanton) cantonAuthProvider(ctx context.Context, selector uin if err != nil { return nil, fmt.Errorf("canton network %d: client_credentials auth: %w", selector, err) } + return oidc, nil case cfgenv.CantonAuthTypeAuthorizationCode: if c.AuthURL == "" || c.ClientID == "" { diff --git a/engine/cld/config/env/config_test.go b/engine/cld/config/env/config_test.go index 0d382cdc3..b4bf6d206 100644 --- a/engine/cld/config/env/config_test.go +++ b/engine/cld/config/env/config_test.go @@ -45,7 +45,11 @@ var ( DeployerKey: "0x567", }, Canton: CantonConfig{ - JWTToken: "", + AuthType: "", + JWTToken: "", + AuthURL: "", + ClientID: "", + ClientSecret: "", }, }, Offchain: OffchainConfig{ @@ -166,7 +170,11 @@ var ( WalletVersion: "V5R1", }, Canton: CantonConfig{ - JWTToken: "", + AuthType: "", + JWTToken: "", + AuthURL: "", + ClientID: "", + ClientSecret: "", }, }, Offchain: OffchainConfig{ diff --git a/engine/cld/config/env/testdata/config.yml b/engine/cld/config/env/testdata/config.yml index 6e4da91ba..28bba081f 100644 --- a/engine/cld/config/env/testdata/config.yml +++ b/engine/cld/config/env/testdata/config.yml @@ -25,7 +25,11 @@ onchain: stellar: deployer_key: "0x567" canton: + auth_type: "" jwt_token: "" + auth_url: "" + client_id: "" + client_secret: "" offchain: job_distributor: endpoints: diff --git a/engine/cld/config/env/testdata/config_with_optional_values.yml b/engine/cld/config/env/testdata/config_with_optional_values.yml index 00e87f011..2cd84dd8a 100644 --- a/engine/cld/config/env/testdata/config_with_optional_values.yml +++ b/engine/cld/config/env/testdata/config_with_optional_values.yml @@ -20,7 +20,11 @@ onchain: stellar: deployer_key: "0x567" canton: + auth_type: "" jwt_token: "" + auth_url: "" + client_id: "" + client_secret: "" offchain: job_distributor: endpoints: From 86bf16830c98515e7d5a0950e625b374e1796ba8 Mon Sep 17 00:00:00 2001 From: stackman27 Date: Thu, 26 Feb 2026 13:06:03 -0800 Subject: [PATCH 03/15] lint fix --- chain/canton/provider/authentication/oauth.go | 1 + engine/cld/config/env/config.go | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/chain/canton/provider/authentication/oauth.go b/chain/canton/provider/authentication/oauth.go index d8e0fc1ba..11d2953cc 100644 --- a/chain/canton/provider/authentication/oauth.go +++ b/chain/canton/provider/authentication/oauth.go @@ -132,6 +132,7 @@ func NewAuthorizationCodeProvider(ctx context.Context, authURL, clientID string) }, nil case <-ctx.Done(): _ = server.Shutdown(ctx) + return nil, ctx.Err() } } diff --git a/engine/cld/config/env/config.go b/engine/cld/config/env/config.go index a6bd4bf82..9a650ea06 100644 --- a/engine/cld/config/env/config.go +++ b/engine/cld/config/env/config.go @@ -84,9 +84,9 @@ type TronConfig struct { // CantonAuthType is the authentication scheme for Canton participant APIs. const ( - CantonAuthTypeStatic = "static" // Pre-obtained JWT (e.g. from canton-login). - CantonAuthTypeClientCredentials = "client_credentials" // CI: fetch token with client_id + client_secret + auth_url. - CantonAuthTypeAuthorizationCode = "authorization_code" // Local: browser flow with client_id + auth_url. + CantonAuthTypeStatic = "static" // Pre-obtained JWT (e.g. from canton-login). + CantonAuthTypeClientCredentials = "client_credentials" // CI: fetch token with client_id + client_secret + auth_url. + CantonAuthTypeAuthorizationCode = "authorization_code" // Local: browser flow with client_id + auth_url. ) // CantonConfig is the configuration for the Canton Chains. @@ -264,8 +264,8 @@ var ( "onchain.canton.auth_type": {"ONCHAIN_CANTON_AUTH_TYPE"}, "onchain.canton.jwt_token": {"ONCHAIN_CANTON_JWT_TOKEN"}, "onchain.canton.auth_url": {"ONCHAIN_CANTON_AUTH_URL"}, - "onchain.canton.client_id": {"ONCHAIN_CANTON_CLIENT_ID"}, - "onchain.canton.client_secret": {"ONCHAIN_CANTON_CLIENT_SECRET"}, + "onchain.canton.client_id": {"ONCHAIN_CANTON_CLIENT_ID"}, + "onchain.canton.client_secret": {"ONCHAIN_CANTON_CLIENT_SECRET"}, "offchain.job_distributor.auth.cognito_app_client_id": {"OFFCHAIN_JD_AUTH_COGNITO_APP_CLIENT_ID", "JD_AUTH_COGNITO_APP_CLIENT_ID"}, "offchain.job_distributor.auth.cognito_app_client_secret": {"OFFCHAIN_JD_AUTH_COGNITO_APP_CLIENT_SECRET", "JD_AUTH_COGNITO_APP_CLIENT_SECRET"}, "offchain.job_distributor.auth.aws_region": {"OFFCHAIN_JD_AUTH_AWS_REGION", "JD_AUTH_AWS_REGION"}, From 2a2621c7453c8aad407e71f3668c6f48a930f9c1 Mon Sep 17 00:00:00 2001 From: stackman27 Date: Thu, 26 Feb 2026 14:01:58 -0800 Subject: [PATCH 04/15] fix lint --- chain/canton/provider/authentication/oauth.go | 2 +- engine/cld/chains/chains.go | 2 ++ engine/cld/config/env/config_test.go | 16 ++++++++-------- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/chain/canton/provider/authentication/oauth.go b/chain/canton/provider/authentication/oauth.go index 11d2953cc..4f46d47b4 100644 --- a/chain/canton/provider/authentication/oauth.go +++ b/chain/canton/provider/authentication/oauth.go @@ -148,7 +148,6 @@ func (p *OIDCProvider) TransportCredentials() credentials.TransportCredentials { } func (p *OIDCProvider) PerRPCCredentials() credentials.PerRPCCredentials { - return secureTokenSource{ TokenSource: p.tokenSource, } @@ -159,6 +158,7 @@ func generateState() string { if _, err := rand.Read(b); err != nil { panic(err) } + return base64.RawURLEncoding.EncodeToString(b) } diff --git a/engine/cld/chains/chains.go b/engine/cld/chains/chains.go index 58795e9f3..75f445b6b 100644 --- a/engine/cld/chains/chains.go +++ b/engine/cld/chains/chains.go @@ -812,12 +812,14 @@ func (l *chainLoaderCanton) cantonAuthProvider(ctx context.Context, selector uin if err != nil { return nil, fmt.Errorf("canton network %d: authorization_code auth: %w", selector, err) } + return oidc, nil default: // static or empty if c.JWTToken == "" { return nil, fmt.Errorf("canton network %d: JWT token is required for static auth", selector) } + return cantonauth.NewStaticProvider(c.JWTToken), nil } } diff --git a/engine/cld/config/env/config_test.go b/engine/cld/config/env/config_test.go index b4bf6d206..46a115904 100644 --- a/engine/cld/config/env/config_test.go +++ b/engine/cld/config/env/config_test.go @@ -45,10 +45,10 @@ var ( DeployerKey: "0x567", }, Canton: CantonConfig{ - AuthType: "", - JWTToken: "", - AuthURL: "", - ClientID: "", + AuthType: "", + JWTToken: "", + AuthURL: "", + ClientID: "", ClientSecret: "", }, }, @@ -170,10 +170,10 @@ var ( WalletVersion: "V5R1", }, Canton: CantonConfig{ - AuthType: "", - JWTToken: "", - AuthURL: "", - ClientID: "", + AuthType: "", + JWTToken: "", + AuthURL: "", + ClientID: "", ClientSecret: "", }, }, From d724bf9473748d5959dca2c8dd912283fd35f12e Mon Sep 17 00:00:00 2001 From: stackman27 Date: Thu, 26 Feb 2026 17:05:52 -0800 Subject: [PATCH 05/15] add additional providers --- .changeset/canton-chain-support.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/canton-chain-support.md diff --git a/.changeset/canton-chain-support.md b/.changeset/canton-chain-support.md new file mode 100644 index 000000000..7497ab4fc --- /dev/null +++ b/.changeset/canton-chain-support.md @@ -0,0 +1,5 @@ +--- +"chainlink-deployments-framework": minor +--- + +Add Canton as a supported chain: config (static, client_credentials, authorization_code auth), chain loader in CLD engine, and OAuth providers for CI and local use. From 310dd8068d32b1a895f24e56961b8c7f1154cea4 Mon Sep 17 00:00:00 2001 From: stackman27 Date: Tue, 26 May 2026 22:58:00 -0700 Subject: [PATCH 06/15] Refactor Canton OAuth auth to match chainlink-canton patterns. Split oauth.go into clientcredentials and authorizationcode packages with RFC 8414 discovery, rename AuthType to AuthStrategy, and add test coverage. --- .changeset/canton-chain-support.md | 2 +- .../authorizationcode/authorizationcode.go | 267 ++++++++++++++++++ .../authorizationcode_test.go | 217 ++++++++++++++ .../clientcredentials/clientcredentials.go | 118 ++++++++ .../clientcredentials_test.go | 164 +++++++++++ .../provider/authentication/metadata.go | 66 +++++ .../provider/authentication/metadata_test.go | 62 ++++ chain/canton/provider/authentication/oauth.go | 175 ------------ engine/cld/chains/chains.go | 38 ++- engine/cld/chains/chains_test.go | 72 +++++ engine/cld/config/env/config.go | 16 +- engine/cld/config/env/config_test.go | 22 +- engine/cld/config/env/testdata/config.yml | 2 +- .../testdata/config_with_optional_values.yml | 2 +- go.mod | 3 +- go.sum | 2 + 16 files changed, 1014 insertions(+), 214 deletions(-) create mode 100644 chain/canton/provider/authentication/authorizationcode/authorizationcode.go create mode 100644 chain/canton/provider/authentication/authorizationcode/authorizationcode_test.go create mode 100644 chain/canton/provider/authentication/clientcredentials/clientcredentials.go create mode 100644 chain/canton/provider/authentication/clientcredentials/clientcredentials_test.go create mode 100644 chain/canton/provider/authentication/metadata.go create mode 100644 chain/canton/provider/authentication/metadata_test.go delete mode 100644 chain/canton/provider/authentication/oauth.go diff --git a/.changeset/canton-chain-support.md b/.changeset/canton-chain-support.md index 7497ab4fc..e3b9a7454 100644 --- a/.changeset/canton-chain-support.md +++ b/.changeset/canton-chain-support.md @@ -2,4 +2,4 @@ "chainlink-deployments-framework": minor --- -Add Canton as a supported chain: config (static, client_credentials, authorization_code auth), chain loader in CLD engine, and OAuth providers for CI and local use. +Extend Canton chain authentication with OAuth2 client credentials (CI) and authorization code (local dev) providers, using RFC 8414 metadata discovery aligned with chainlink-canton authentication packages. diff --git a/chain/canton/provider/authentication/authorizationcode/authorizationcode.go b/chain/canton/provider/authentication/authorizationcode/authorizationcode.go new file mode 100644 index 000000000..3136a050c --- /dev/null +++ b/chain/canton/provider/authentication/authorizationcode/authorizationcode.go @@ -0,0 +1,267 @@ +// Package authorizationcode provides OAuth2 authorization code flow authentication for Canton gRPC connections. +// This flow is intended for local development where a browser-based login is available; it is not suitable for CI. +package authorizationcode + +import ( + "context" + "crypto/tls" + "fmt" + "net" + "net/http" + "net/url" + "os/exec" + "runtime" + "slices" + "time" + + "golang.org/x/oauth2" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/oauth" + + cantonauth "github.com/smartcontractkit/chainlink-deployments-framework/chain/canton/provider/authentication" +) + +var _ cantonauth.Provider = Provider{} + +// Provider implements authentication.Provider using the OAuth2 authorization code flow with PKCE (S256). +type Provider struct { + tokenSource oauth.TokenSource + transportCredentials credentials.TransportCredentials +} + +type authorizationCodeProviderConfig struct { + scopes []string + transportCredentials credentials.TransportCredentials + callbackURL string + openBrowser bool + timeout time.Duration +} + +func defaultAuthorizationCodeProviderConfig() *authorizationCodeProviderConfig { + return &authorizationCodeProviderConfig{ + scopes: []string{"openid", "daml_ledger_api"}, + transportCredentials: credentials.NewTLS(&tls.Config{ + MinVersion: tls.VersionTLS12, + }), + callbackURL: "http://127.0.0.1:8400/callback", + openBrowser: true, + } +} + +// ProviderOption configures the authorization code Provider. +type ProviderOption func(*authorizationCodeProviderConfig) + +// WithScopes configures the scopes requested from the authorization server. +func WithScopes(scopes ...string) ProviderOption { + return func(config *authorizationCodeProviderConfig) { + config.scopes = scopes + } +} + +// WithTransportCredentials configures transport credentials for gRPC connections. +func WithTransportCredentials(creds credentials.TransportCredentials) ProviderOption { + return func(config *authorizationCodeProviderConfig) { + config.transportCredentials = creds + } +} + +// WithCallbackURL configures the local redirect URI used by the authorization server. +func WithCallbackURL(callbackURL string) ProviderOption { + return func(config *authorizationCodeProviderConfig) { + config.callbackURL = callbackURL + } +} + +// WithOpenBrowser controls whether the default browser is opened automatically. +func WithOpenBrowser(openBrowser bool) ProviderOption { + return func(config *authorizationCodeProviderConfig) { + config.openBrowser = openBrowser + } +} + +// WithTimeout configures a timeout for the overall authorization flow. +func WithTimeout(timeout time.Duration) ProviderOption { + return func(config *authorizationCodeProviderConfig) { + config.timeout = timeout + } +} + +// NewDiscoveryProvider creates a provider using OAuth2 Authorization Server Metadata discovery (RFC 8414). +// PKCE with the S256 challenge method is required. +func NewDiscoveryProvider( + ctx context.Context, + authorizationServerURL, clientID string, + options ...ProviderOption, +) (*Provider, error) { + metadata, err := cantonauth.GetAuthorizationServerMetadata(ctx, authorizationServerURL) + if err != nil { + return nil, fmt.Errorf("failed to get authorization server metadata: %w", err) + } + + if !slices.Contains(metadata.CodeChallengeMethodsSupported, "S256") { + return nil, fmt.Errorf("authorization server does not support S256 PKCE challenges") + } + + return NewProvider(ctx, metadata.AuthorizationEndpoint, metadata.TokenEndpoint, clientID, options...) +} + +// NewProvider creates a provider that performs the OAuth2 authorization code flow with PKCE (S256). +func NewProvider( + ctx context.Context, + authURL, tokenURL, clientID string, + options ...ProviderOption, +) (*Provider, error) { + cfg := defaultAuthorizationCodeProviderConfig() + for _, option := range options { + option(cfg) + } + + if authURL == "" { + return nil, fmt.Errorf("authURL cannot be empty") + } + if tokenURL == "" { + return nil, fmt.Errorf("tokenURL cannot be empty") + } + if clientID == "" { + return nil, fmt.Errorf("clientID cannot be empty") + } + + flowCtx := ctx + if cfg.timeout > 0 { + var cancel context.CancelFunc + flowCtx, cancel = context.WithTimeout(ctx, cfg.timeout) + defer cancel() + } + + callbackURL, err := url.Parse(cfg.callbackURL) + if err != nil { + return nil, fmt.Errorf("failed to parse callback URL: %w", err) + } + + oauthCfg := &oauth2.Config{ + ClientID: clientID, + RedirectURL: callbackURL.String(), + Scopes: cfg.scopes, + Endpoint: oauth2.Endpoint{AuthURL: authURL, TokenURL: tokenURL}, + } + + state := oauth2.GenerateVerifier() + verifier := oauth2.GenerateVerifier() + authCodeURL := oauthCfg.AuthCodeURL(state, oauth2.S256ChallengeOption(verifier)) + + callbackChan := make(chan *oauth2.Token, 1) + + serveMux := http.NewServeMux() + serveMux.HandleFunc(callbackURL.Path, func(w http.ResponseWriter, r *http.Request) { + q := r.URL.Query() + code := q.Get("code") + receivedState := q.Get("state") + + if receivedState != state { + http.Error(w, "Invalid state parameter", http.StatusBadRequest) + return + } + if code == "" { + http.Error(w, "No code parameter received", http.StatusBadRequest) + return + } + + token, err := oauthCfg.Exchange(flowCtx, code, oauth2.VerifierOption(verifier)) + if err != nil { + http.Error(w, "Token exchange failed", http.StatusInternalServerError) + return + } + + select { + case callbackChan <- token: + default: + } + + html := ` + +Authentication Complete + +

Authentication complete!

+

You can safely close this window.

+ + +` + w.Header().Set("Content-Type", "text/html; charset=utf-8") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(html)) + }) + + server := http.Server{ + Addr: callbackURL.Host, + Handler: serveMux, + ReadHeaderTimeout: time.Second, + ReadTimeout: 5 * time.Second, + WriteTimeout: 5 * time.Second, + } + + listener, err := new(net.ListenConfig).Listen(flowCtx, "tcp", server.Addr) + if err != nil { + return nil, fmt.Errorf("creating listener: %w", err) + } + + serverErr := make(chan error, 1) + go func() { + serverErr <- server.Serve(listener) + }() + + if cfg.openBrowser { + fmt.Println("Attempting to open your default browser.") + fmt.Println("If the browser does not open, visit the following URL:") + fmt.Println(authCodeURL) + openBrowser(flowCtx, authCodeURL) + } else { + fmt.Println("Visit the following URL:") + fmt.Println(authCodeURL) + } + + shutdown := func() { + shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + _ = server.Shutdown(shutdownCtx) + } + + select { + case err := <-serverErr: + shutdown() + return nil, fmt.Errorf("callback server error: %w", err) + case token := <-callbackChan: + shutdown() + tokenSource := oauthCfg.TokenSource(flowCtx, token) + + return &Provider{ + tokenSource: oauth.TokenSource{TokenSource: tokenSource}, + transportCredentials: cfg.transportCredentials, + }, nil + case <-flowCtx.Done(): + shutdown() + return nil, flowCtx.Err() + } +} + +func (p Provider) TokenSource() oauth2.TokenSource { + return p.tokenSource.TokenSource +} + +func (p Provider) TransportCredentials() credentials.TransportCredentials { + return p.transportCredentials +} + +func (p Provider) PerRPCCredentials() credentials.PerRPCCredentials { + return p.tokenSource +} + +func openBrowser(ctx context.Context, targetURL string) { + switch runtime.GOOS { + case "darwin": + _ = exec.CommandContext(ctx, "open", targetURL).Start() + case "linux": + _ = exec.CommandContext(ctx, "xdg-open", targetURL).Start() + case "windows": + _ = exec.CommandContext(ctx, "rundll32", "url.dll,FileProtocolHandler", targetURL).Start() + } +} diff --git a/chain/canton/provider/authentication/authorizationcode/authorizationcode_test.go b/chain/canton/provider/authentication/authorizationcode/authorizationcode_test.go new file mode 100644 index 000000000..208760916 --- /dev/null +++ b/chain/canton/provider/authentication/authorizationcode/authorizationcode_test.go @@ -0,0 +1,217 @@ +package authorizationcode + +import ( + "bytes" + "encoding/json" + "io" + "net" + "net/http" + "net/http/httptest" + "net/url" + "os" + "strings" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +type safeBuffer struct { + mu sync.Mutex + b bytes.Buffer +} + +var stdoutMu sync.Mutex + +func (s *safeBuffer) Write(p []byte) (int, error) { + s.mu.Lock() + defer s.mu.Unlock() + + return s.b.Write(p) +} + +func (s *safeBuffer) String() string { + s.mu.Lock() + defer s.mu.Unlock() + + return s.b.String() +} + +func captureStdout(t *testing.T) (*safeBuffer, func()) { + t.Helper() + + stdoutMu.Lock() + original := os.Stdout + reader, writer, err := os.Pipe() + require.NoError(t, err) + + buffer := &safeBuffer{} + done := make(chan struct{}) + + os.Stdout = writer + go func() { + _, _ = io.Copy(buffer, reader) + close(done) + }() + + return buffer, func() { + _ = writer.Close() + os.Stdout = original + <-done + stdoutMu.Unlock() + } +} + +func freePort(t *testing.T) string { + t.Helper() + + listener, err := net.Listen("tcp", "127.0.0.1:0") //nolint:noctx + require.NoError(t, err) + addr := listener.Addr().String() + _ = listener.Close() + + return addr +} + +func extractFirstURL(output string) string { + for line := range strings.SplitSeq(output, "\n") { + candidate := strings.TrimSpace(line) + if strings.HasPrefix(candidate, "http://") || strings.HasPrefix(candidate, "https://") { + if parsed, err := url.Parse(candidate); err == nil && parsed.Scheme != "" && parsed.Host != "" { + return candidate + } + } + } + + return "" +} + +func newTokenServer(t *testing.T) *httptest.Server { + t.Helper() + + return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + require.NoError(t, r.ParseForm()) + require.Equal(t, "authorization_code", r.Form.Get("grant_type")) + require.NotEmpty(t, r.Form.Get("code")) + require.NotEmpty(t, r.Form.Get("code_verifier")) + + payload, err := json.Marshal(map[string]any{ + "access_token": "auth-code-token", + "token_type": "Bearer", + "expires_in": 3600, + }) + require.NoError(t, err) + + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write(payload) + })) +} + +func TestNewProvider_ValidatesInputs(t *testing.T) { + t.Parallel() + ctx := t.Context() + + tests := []struct { + name string + authURL string + tokenURL string + clientID string + }{ + {name: "missing auth url", authURL: "", tokenURL: "https://example.test/token", clientID: "client-id"}, + {name: "missing token url", authURL: "https://example.test/auth", tokenURL: "", clientID: "client-id"}, + {name: "missing client id", authURL: "https://example.test/auth", tokenURL: "https://example.test/token", clientID: ""}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + _, err := NewProvider(ctx, test.authURL, test.tokenURL, test.clientID, WithOpenBrowser(false)) + require.Error(t, err) + }) + } +} + +func TestNewDiscoveryProvider_RequiresS256(t *testing.T) { + t.Parallel() + ctx := t.Context() + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + payload, err := json.Marshal(map[string]any{ + "issuer": "http://" + r.Host, + "authorization_endpoint": "https://example.test/auth", + "token_endpoint": "https://example.test/token", + "code_challenge_methods_supported": []string{"plain"}, + }) + require.NoError(t, err) + + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write(payload) + })) + t.Cleanup(server.Close) + + _, err := NewDiscoveryProvider(ctx, server.URL, "client-id") + require.Error(t, err) +} + +func TestNewProvider_FlowCompletes(t *testing.T) { + t.Parallel() + ctx := t.Context() + callbackHost := freePort(t) + + tokenServer := newTokenServer(t) + t.Cleanup(tokenServer.Close) + + output, restore := captureStdout(t) + defer restore() + + resultCh := make(chan struct { + provider *Provider + err error + }, 1) + + go func() { + provider, err := NewProvider( + ctx, + tokenServer.URL+"/auth", + tokenServer.URL+"/token", + "client-id", + WithCallbackURL("http://"+callbackHost+"/callback"), + WithOpenBrowser(false), + WithTimeout(5*time.Second), + ) + resultCh <- struct { + provider *Provider + err error + }{provider: provider, err: err} + }() + + var authCodeURL string + deadline := time.Now().Add(2 * time.Second) + for time.Now().Before(deadline) { + authCodeURL = extractFirstURL(output.String()) + if authCodeURL != "" { + break + } + time.Sleep(10 * time.Millisecond) + } + require.NotEmpty(t, authCodeURL) + + parsed, err := url.Parse(authCodeURL) + require.NoError(t, err) + state := parsed.Query().Get("state") + require.NotEmpty(t, state) + + callbackURL := "http://" + callbackHost + "/callback?code=code123&state=" + url.QueryEscape(state) + response, err := http.Get(callbackURL) //nolint:noctx + require.NoError(t, err) + require.NoError(t, response.Body.Close()) + + result := <-resultCh + require.NoError(t, result.err) + require.NotNil(t, result.provider) + + token, err := result.provider.TokenSource().Token() + require.NoError(t, err) + require.Equal(t, "auth-code-token", token.AccessToken) +} diff --git a/chain/canton/provider/authentication/clientcredentials/clientcredentials.go b/chain/canton/provider/authentication/clientcredentials/clientcredentials.go new file mode 100644 index 000000000..a2901b1af --- /dev/null +++ b/chain/canton/provider/authentication/clientcredentials/clientcredentials.go @@ -0,0 +1,118 @@ +// Package clientcredentials provides OAuth2 client credentials flow authentication for Canton gRPC connections. +// This flow is designed for machine-to-machine authentication and is intended for CI/CD environments. +package clientcredentials + +import ( + "context" + "crypto/tls" + "fmt" + + "golang.org/x/oauth2" + "golang.org/x/oauth2/clientcredentials" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/oauth" + + cantonauth "github.com/smartcontractkit/chainlink-deployments-framework/chain/canton/provider/authentication" +) + +var _ cantonauth.Provider = Provider{} + +// Provider implements authentication.Provider using the OAuth2 client credentials flow. +type Provider struct { + tokenSource oauth.TokenSource + transportCredentials credentials.TransportCredentials +} + +type clientCredentialsProviderConfig struct { + scopes []string + transportCredentials credentials.TransportCredentials +} + +func defaultClientCredentialsProviderConfig() *clientCredentialsProviderConfig { + return &clientCredentialsProviderConfig{ + scopes: []string{"daml_ledger_api"}, + transportCredentials: credentials.NewTLS(&tls.Config{ + MinVersion: tls.VersionTLS12, + }), + } +} + +// ProviderOption configures the client credentials Provider. +type ProviderOption func(*clientCredentialsProviderConfig) + +// WithScopes configures the scopes requested from the authorization server. +func WithScopes(scopes ...string) ProviderOption { + return func(config *clientCredentialsProviderConfig) { + config.scopes = scopes + } +} + +// WithTransportCredentials configures transport credentials for gRPC connections. +func WithTransportCredentials(creds credentials.TransportCredentials) ProviderOption { + return func(config *clientCredentialsProviderConfig) { + config.transportCredentials = creds + } +} + +// NewDiscoveryProvider creates a provider using OAuth2 Authorization Server Metadata discovery (RFC 8414). +func NewDiscoveryProvider( + ctx context.Context, + authorizationServerURL, clientID, clientSecret string, + options ...ProviderOption, +) (*Provider, error) { + metadata, err := cantonauth.GetAuthorizationServerMetadata(ctx, authorizationServerURL) + if err != nil { + return nil, fmt.Errorf("failed to get authorization server metadata: %w", err) + } + + return NewProvider(ctx, metadata.TokenEndpoint, clientID, clientSecret, options...) +} + +// NewProvider creates a provider that fetches tokens using the OAuth2 client credentials flow. +func NewProvider( + ctx context.Context, + tokenURL, clientID, clientSecret string, + options ...ProviderOption, +) (*Provider, error) { + cfg := defaultClientCredentialsProviderConfig() + for _, option := range options { + option(cfg) + } + + if tokenURL == "" { + return nil, fmt.Errorf("tokenURL cannot be empty") + } + if clientID == "" { + return nil, fmt.Errorf("clientID cannot be empty") + } + if clientSecret == "" { + return nil, fmt.Errorf("clientSecret cannot be empty") + } + + oauthCfg := &clientcredentials.Config{ + ClientID: clientID, + ClientSecret: clientSecret, + TokenURL: tokenURL, + Scopes: cfg.scopes, + } + + refreshCtx := context.WithoutCancel(ctx) + tokenSource := oauthCfg.TokenSource(refreshCtx) + + return &Provider{ + tokenSource: oauth.TokenSource{TokenSource: tokenSource}, + transportCredentials: cfg.transportCredentials, + }, nil +} + +func (p Provider) TokenSource() oauth2.TokenSource { + return p.tokenSource.TokenSource +} + +func (p Provider) TransportCredentials() credentials.TransportCredentials { + return p.transportCredentials +} + +func (p Provider) PerRPCCredentials() credentials.PerRPCCredentials { + return p.tokenSource +} diff --git a/chain/canton/provider/authentication/clientcredentials/clientcredentials_test.go b/chain/canton/provider/authentication/clientcredentials/clientcredentials_test.go new file mode 100644 index 000000000..cb710b2ab --- /dev/null +++ b/chain/canton/provider/authentication/clientcredentials/clientcredentials_test.go @@ -0,0 +1,164 @@ +package clientcredentials + +import ( + "crypto/tls" + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "net/url" + "strings" + "testing" + + "github.com/stretchr/testify/require" + "google.golang.org/grpc/credentials" +) + +type tokenResponse struct { + AccessToken string `json:"access_token"` + TokenType string `json:"token_type"` + ExpiresIn int `json:"expires_in"` +} + +func newTokenServer(t *testing.T, expectedScope string) *httptest.Server { + t.Helper() + + return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + require.Equal(t, http.MethodPost, r.Method) + + body, err := io.ReadAll(r.Body) + require.NoError(t, err) + + values, err := url.ParseQuery(string(body)) + require.NoError(t, err) + require.Equal(t, "client_credentials", values.Get("grant_type")) + + if expectedScope != "" { + require.Equal(t, expectedScope, values.Get("scope")) + } + + payload, err := json.Marshal(tokenResponse{ + AccessToken: "test-access-token", + TokenType: "Bearer", + ExpiresIn: 3600, + }) + require.NoError(t, err) + + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write(payload) + })) +} + +func TestNewProvider_ValidatesInputs(t *testing.T) { + t.Parallel() + ctx := t.Context() + + tests := []struct { + name string + tokenURL string + clientID string + clientSecret string + }{ + {name: "missing token url", tokenURL: "", clientID: "client-id", clientSecret: "client-secret"}, + {name: "missing client id", tokenURL: "https://example.test/token", clientID: "", clientSecret: "client-secret"}, + {name: "missing client secret", tokenURL: "https://example.test/token", clientID: "client-id", clientSecret: ""}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + _, err := NewProvider(ctx, test.tokenURL, test.clientID, test.clientSecret) + require.Error(t, err) + }) + } +} + +func TestNewProvider_UsesOptionsAndTokenSource(t *testing.T) { + t.Parallel() + ctx := t.Context() + server := newTokenServer(t, "scope-a scope-b") + t.Cleanup(server.Close) + + customCreds := credentials.NewTLS(&tls.Config{InsecureSkipVerify: true}) + + provider, err := NewProvider( + ctx, + server.URL, + "client-id", + "client-secret", + WithScopes("scope-a", "scope-b"), + WithTransportCredentials(customCreds), + ) + require.NoError(t, err) + require.Same(t, customCreds, provider.TransportCredentials()) + + token, err := provider.TokenSource().Token() + require.NoError(t, err) + require.Equal(t, "test-access-token", token.AccessToken) +} + +func TestNewDiscoveryProvider_UsesMetadataTokenEndpoint(t *testing.T) { + t.Parallel() + ctx := t.Context() + + mux := http.NewServeMux() + server := httptest.NewServer(mux) + t.Cleanup(server.Close) + + metadataPath := "/.well-known/oauth-authorization-server" + tokenPath := "/token" + + mux.HandleFunc(metadataPath, func(w http.ResponseWriter, r *http.Request) { + payload, err := json.Marshal(map[string]string{ + "issuer": server.URL, + "token_endpoint": server.URL + tokenPath, + }) + require.NoError(t, err) + + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write(payload) + }) + mux.HandleFunc(tokenPath, func(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + require.NoError(t, err) + + values, err := url.ParseQuery(string(body)) + require.NoError(t, err) + require.Equal(t, "client_credentials", values.Get("grant_type")) + require.Equal(t, "daml_ledger_api", values.Get("scope")) + + payload, err := json.Marshal(tokenResponse{ + AccessToken: "test-access-token", + TokenType: "Bearer", + ExpiresIn: 3600, + }) + require.NoError(t, err) + + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write(payload) + }) + + provider, err := NewDiscoveryProvider(ctx, server.URL, "client-id", "client-secret") + require.NoError(t, err) + + token, err := provider.TokenSource().Token() + require.NoError(t, err) + require.Equal(t, "test-access-token", token.AccessToken) +} + +func TestNewDiscoveryProvider_RequiresMetadataEndpoint(t *testing.T) { + t.Parallel() + ctx := t.Context() + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if strings.HasSuffix(r.URL.Path, "/.well-known/oauth-authorization-server") { + w.WriteHeader(http.StatusInternalServerError) + return + } + w.WriteHeader(http.StatusNotFound) + })) + t.Cleanup(server.Close) + + _, err := NewDiscoveryProvider(ctx, server.URL, "client-id", "client-secret") + require.Error(t, err) +} diff --git a/chain/canton/provider/authentication/metadata.go b/chain/canton/provider/authentication/metadata.go new file mode 100644 index 000000000..ab3bf284e --- /dev/null +++ b/chain/canton/provider/authentication/metadata.go @@ -0,0 +1,66 @@ +package authentication + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "strings" + "time" +) + +// AuthorizationServerMetadata represents a subset of the metadata provided by an OAuth 2.0 Authorization Server. +// See RFC 8414, Section 2 for the full specification: +// https://datatracker.ietf.org/doc/html/rfc8414#section-2 +type AuthorizationServerMetadata struct { + // Issuer is the authorization server's issuer identifier, which is a URL. + Issuer string `json:"issuer"` + // AuthorizationEndpoint is the URL of the authorization server's authorization endpoint. + AuthorizationEndpoint string `json:"authorization_endpoint"` + // TokenEndpoint is the URL of the authorization server's token endpoint. + TokenEndpoint string `json:"token_endpoint"` + // CodeChallengeMethodsSupported lists PKCE code challenge methods supported by this authorization server. + CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported"` +} + +// GetAuthorizationServerMetadata retrieves OAuth 2.0 authorization server metadata from the well-known endpoint. +func GetAuthorizationServerMetadata(ctx context.Context, authorizationServerURL string) (*AuthorizationServerMetadata, error) { + authorizationServerURL = strings.TrimSuffix(authorizationServerURL, "/") + request, err := http.NewRequestWithContext( + ctx, + http.MethodGet, + fmt.Sprintf("%s/.well-known/oauth-authorization-server", authorizationServerURL), + nil, + ) + if err != nil { + return nil, err + } + + client := http.Client{Timeout: 10 * time.Second} + response, err := client.Do(request) + if err != nil { + return nil, err + } + defer response.Body.Close() + + if response.StatusCode != http.StatusOK { + return nil, fmt.Errorf("unexpected status code: %d", response.StatusCode) + } + + body, err := io.ReadAll(response.Body) + if err != nil { + return nil, fmt.Errorf("reading response body: %w", err) + } + + metadata := &AuthorizationServerMetadata{} + if err := json.Unmarshal(body, metadata); err != nil { + return nil, fmt.Errorf("unmarshalling response body: %w", err) + } + + if metadata.Issuer != authorizationServerURL { + return nil, fmt.Errorf("metadata: unexpected issuer: %s", metadata.Issuer) + } + + return metadata, nil +} diff --git a/chain/canton/provider/authentication/metadata_test.go b/chain/canton/provider/authentication/metadata_test.go new file mode 100644 index 000000000..bad092ce1 --- /dev/null +++ b/chain/canton/provider/authentication/metadata_test.go @@ -0,0 +1,62 @@ +package authentication + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGetAuthorizationServerMetadata(t *testing.T) { + t.Parallel() + + ctx := t.Context() + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + require.Equal(t, "/.well-known/oauth-authorization-server", r.URL.Path) + + payload, err := json.Marshal(AuthorizationServerMetadata{ + Issuer: serverURL(t, r), + AuthorizationEndpoint: serverURL(t, r) + "/authorize", + TokenEndpoint: serverURL(t, r) + "/token", + CodeChallengeMethodsSupported: []string{"S256"}, + }) + require.NoError(t, err) + + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write(payload) + })) + t.Cleanup(server.Close) + + metadata, err := GetAuthorizationServerMetadata(ctx, server.URL) + require.NoError(t, err) + require.Equal(t, server.URL, metadata.Issuer) + require.Equal(t, server.URL+"/authorize", metadata.AuthorizationEndpoint) + require.Equal(t, server.URL+"/token", metadata.TokenEndpoint) +} + +func TestGetAuthorizationServerMetadata_UnexpectedIssuer(t *testing.T) { + t.Parallel() + + ctx := t.Context() + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + payload, err := json.Marshal(AuthorizationServerMetadata{ + Issuer: "https://other.example.com", + }) + require.NoError(t, err) + + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write(payload) + })) + t.Cleanup(server.Close) + + _, err := GetAuthorizationServerMetadata(ctx, server.URL) + require.Error(t, err) +} + +func serverURL(t *testing.T, r *http.Request) string { + t.Helper() + + return "http://" + r.Host +} diff --git a/chain/canton/provider/authentication/oauth.go b/chain/canton/provider/authentication/oauth.go deleted file mode 100644 index 4f46d47b4..000000000 --- a/chain/canton/provider/authentication/oauth.go +++ /dev/null @@ -1,175 +0,0 @@ -package authentication - -import ( - "context" - "crypto/rand" - "crypto/tls" - "encoding/base64" - "fmt" - "net" - "net/http" - "os/exec" - "runtime" - "strconv" - "time" - - "golang.org/x/oauth2" - "golang.org/x/oauth2/clientcredentials" - "google.golang.org/grpc/credentials" -) - -var _ Provider = (*OIDCProvider)(nil) - -// OIDCProvider implements Provider using OAuth2/OIDC token flows (client credentials or authorization code). -type OIDCProvider struct { - tokenSource oauth2.TokenSource -} - -// NewClientCredentialsProvider creates a provider that fetches tokens using the OAuth2 client credentials flow. -// Use in CI where ClientID, ClientSecret and AuthURL are available; tokens are obtained automatically. -func NewClientCredentialsProvider(ctx context.Context, authURL, clientID, clientSecret string) (*OIDCProvider, error) { - tokenURL := authURL + "/v1/token" - - oauthCfg := &clientcredentials.Config{ - ClientID: clientID, - ClientSecret: clientSecret, - TokenURL: tokenURL, - Scopes: []string{"daml_ledger_api"}, - } - - tokenSource := oauthCfg.TokenSource(ctx) - - return &OIDCProvider{ - tokenSource: tokenSource, - }, nil -} - -// NewAuthorizationCodeProvider creates a provider that uses the OAuth2 authorization code flow with PKCE. -// It starts a local callback server, opens the browser to the auth URL, and exchanges the code for a token. -// Use locally to skip canton-login; only ClientID and AuthURL are required. -func NewAuthorizationCodeProvider(ctx context.Context, authURL, clientID string) (*OIDCProvider, error) { - verifier := oauth2.GenerateVerifier() - - port := 8400 - authEndpoint := authURL + "/v1/authorize" - tokenEndpoint := authURL + "/v1/token" - redirectURL := "http://localhost:" + strconv.Itoa(port) - - oauthCfg := &oauth2.Config{ - ClientID: clientID, - RedirectURL: redirectURL + "/callback", - Scopes: []string{"openid", "daml_ledger_api"}, - Endpoint: oauth2.Endpoint{AuthURL: authEndpoint, TokenURL: tokenEndpoint}, - } - - state := generateState() - authCodeURL := oauthCfg.AuthCodeURL(state, oauth2.S256ChallengeOption(verifier)) - - callbackChan := make(chan *oauth2.Token) - - serveMux := http.NewServeMux() - serveMux.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) { - q := r.URL.Query() - code := q.Get("code") - receivedState := q.Get("state") - - if receivedState != state { - http.Error(w, "Invalid state parameter", http.StatusBadRequest) - return - } - - token, err := oauthCfg.Exchange(ctx, code, oauth2.VerifierOption(verifier)) - if err != nil { - http.Error(w, "Token exchange failed: "+err.Error(), http.StatusInternalServerError) - return - } - - callbackChan <- token - - html := ` - -Authentication Complete - -

Authentication complete!

-

You can safely close this window.

- - -` - w.Header().Set("Content-Type", "text/html; charset=utf-8") - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(html)) - }) - - server := http.Server{ - Addr: ":" + strconv.Itoa(port), - Handler: serveMux, - ReadHeaderTimeout: 5 * time.Second, - } - - listener, err := new(net.ListenConfig).Listen(ctx, "tcp", server.Addr) - if err != nil { - return nil, fmt.Errorf("listening on port %d: %w", port, err) - } - - serverErr := make(chan error, 1) - go func() { - serverErr <- server.Serve(listener) - }() - - openBrowser(ctx, authCodeURL) - - select { - case err := <-serverErr: - _ = server.Shutdown(ctx) - - return nil, fmt.Errorf("callback server error: %w", err) - case token := <-callbackChan: - tokenSource := oauthCfg.TokenSource(ctx, token) - _ = server.Shutdown(ctx) - - return &OIDCProvider{ - tokenSource: tokenSource, - }, nil - case <-ctx.Done(): - _ = server.Shutdown(ctx) - - return nil, ctx.Err() - } -} - -func (p *OIDCProvider) TokenSource() oauth2.TokenSource { - return p.tokenSource -} - -func (p *OIDCProvider) TransportCredentials() credentials.TransportCredentials { - return credentials.NewTLS(&tls.Config{ - MinVersion: tls.VersionTLS12, - }) -} - -func (p *OIDCProvider) PerRPCCredentials() credentials.PerRPCCredentials { - return secureTokenSource{ - TokenSource: p.tokenSource, - } -} - -func generateState() string { - b := make([]byte, 16) - if _, err := rand.Read(b); err != nil { - panic(err) - } - - return base64.RawURLEncoding.EncodeToString(b) -} - -// openBrowser opens the default browser to url on supported platforms; otherwise it is a no-op. -func openBrowser(ctx context.Context, url string) { - switch runtime.GOOS { - case "darwin": - _ = exec.CommandContext(ctx, "open", url).Start() - case "linux": - _ = exec.CommandContext(ctx, "xdg-open", url).Start() - case "windows": - _ = exec.CommandContext(ctx, "rundll32", "url.dll,FileProtocolHandler", url).Start() - } -} diff --git a/engine/cld/chains/chains.go b/engine/cld/chains/chains.go index 3c1025a9c..d80cba165 100644 --- a/engine/cld/chains/chains.go +++ b/engine/cld/chains/chains.go @@ -16,6 +16,8 @@ import ( aptosprov "github.com/smartcontractkit/chainlink-deployments-framework/chain/aptos/provider" cantonprov "github.com/smartcontractkit/chainlink-deployments-framework/chain/canton/provider" cantonauth "github.com/smartcontractkit/chainlink-deployments-framework/chain/canton/provider/authentication" + cantonauthcode "github.com/smartcontractkit/chainlink-deployments-framework/chain/canton/provider/authentication/authorizationcode" + cantonclientcreds "github.com/smartcontractkit/chainlink-deployments-framework/chain/canton/provider/authentication/clientcredentials" fevm "github.com/smartcontractkit/chainlink-deployments-framework/chain/evm" evmprov "github.com/smartcontractkit/chainlink-deployments-framework/chain/evm/provider" evmclient "github.com/smartcontractkit/chainlink-deployments-framework/chain/evm/provider/rpcclient" @@ -220,7 +222,7 @@ func newChainLoaders( if cantonAuthConfigured(cfg.Canton) { loaders[chainsel.FamilyCanton] = newChainLoaderCanton(networks, cfg) } else { - lggr.Info("Skipping Canton chains, no Canton auth configured (set auth_type and jwt_token, or auth_url+client_id for OAuth)") + lggr.Info("Skipping Canton chains, no Canton auth configured (set auth_strategy and jwt_token, or auth_url+client_id for OAuth)") } return loaders @@ -731,7 +733,8 @@ func newChainLoaderCanton( } // Load loads a Canton Chain for a selector. -// Participant configurations come from network metadata, and JWT token from env config. +// Participant configurations come from network metadata. Authentication is configured via CantonConfig +// (static JWT, client_credentials for CI, or authorization_code for local development). func (l *chainLoaderCanton) Load(ctx context.Context, selector uint64) (fchain.BlockChain, error) { network, err := l.getNetwork(selector) if err != nil { @@ -780,12 +783,12 @@ func (l *chainLoaderCanton) Load(ctx context.Context, selector uint64) (fchain.B return c, nil } -// cantonAuthConfigured returns true if Canton auth is configured for at least one scheme (static, client_credentials, or authorization_code). +// cantonAuthConfigured returns true if Canton auth is configured for at least one strategy. func cantonAuthConfigured(c cfgenv.CantonConfig) bool { - switch c.AuthType { - case cfgenv.CantonAuthTypeClientCredentials: + switch c.AuthStrategy { + case cfgenv.CantonAuthStrategyClientCredentials: return c.AuthURL != "" && c.ClientID != "" && c.ClientSecret != "" - case cfgenv.CantonAuthTypeAuthorizationCode: + case cfgenv.CantonAuthStrategyAuthorizationCode: return c.AuthURL != "" && c.ClientID != "" default: // static or empty (backward compat: jwt_token alone enables Canton) @@ -793,32 +796,25 @@ func cantonAuthConfigured(c cfgenv.CantonConfig) bool { } } -// cantonAuthProvider builds a Canton auth Provider from config. Caller must ensure cantonAuthConfigured(cfg.Canton) is true. +// cantonAuthProvider builds a Canton auth Provider from config. func (l *chainLoaderCanton) cantonAuthProvider(ctx context.Context, selector uint64) (cantonauth.Provider, error) { c := l.cfg.Canton - switch c.AuthType { - case cfgenv.CantonAuthTypeClientCredentials: - if c.AuthURL == "" || c.ClientID == "" || c.ClientSecret == "" { - return nil, fmt.Errorf("canton network %d: client_credentials requires auth_url, client_id, and client_secret", selector) - } - oidc, err := cantonauth.NewClientCredentialsProvider(ctx, c.AuthURL, c.ClientID, c.ClientSecret) + switch c.AuthStrategy { + case cfgenv.CantonAuthStrategyClientCredentials: + provider, err := cantonclientcreds.NewDiscoveryProvider(ctx, c.AuthURL, c.ClientID, c.ClientSecret) if err != nil { return nil, fmt.Errorf("canton network %d: client_credentials auth: %w", selector, err) } - return oidc, nil - case cfgenv.CantonAuthTypeAuthorizationCode: - if c.AuthURL == "" || c.ClientID == "" { - return nil, fmt.Errorf("canton network %d: authorization_code requires auth_url and client_id", selector) - } - oidc, err := cantonauth.NewAuthorizationCodeProvider(ctx, c.AuthURL, c.ClientID) + return provider, nil + case cfgenv.CantonAuthStrategyAuthorizationCode: + provider, err := cantonauthcode.NewDiscoveryProvider(ctx, c.AuthURL, c.ClientID) if err != nil { return nil, fmt.Errorf("canton network %d: authorization_code auth: %w", selector, err) } - return oidc, nil + return provider, nil default: - // static or empty if c.JWTToken == "" { return nil, fmt.Errorf("canton network %d: JWT token is required for static auth", selector) } diff --git a/engine/cld/chains/chains_test.go b/engine/cld/chains/chains_test.go index 206da0076..ee1168fc3 100644 --- a/engine/cld/chains/chains_test.go +++ b/engine/cld/chains/chains_test.go @@ -1381,6 +1381,78 @@ func Test_chainLoaderCanton_Load(t *testing.T) { } } +func Test_cantonAuthConfigured(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + config cfgenv.CantonConfig + want bool + }{ + { + name: "static jwt", + config: cfgenv.CantonConfig{JWTToken: "token"}, + want: true, + }, + { + name: "client credentials complete", + config: cfgenv.CantonConfig{AuthStrategy: cfgenv.CantonAuthStrategyClientCredentials, AuthURL: "https://auth.example.com", ClientID: "id", ClientSecret: "secret"}, + want: true, + }, + { + name: "client credentials missing secret", + config: cfgenv.CantonConfig{AuthStrategy: cfgenv.CantonAuthStrategyClientCredentials, AuthURL: "https://auth.example.com", ClientID: "id"}, + want: false, + }, + { + name: "authorization code complete", + config: cfgenv.CantonConfig{AuthStrategy: cfgenv.CantonAuthStrategyAuthorizationCode, AuthURL: "https://auth.example.com", ClientID: "id"}, + want: true, + }, + { + name: "authorization code missing client id", + config: cfgenv.CantonConfig{AuthStrategy: cfgenv.CantonAuthStrategyAuthorizationCode, AuthURL: "https://auth.example.com"}, + want: false, + }, + { + name: "empty config", + config: cfgenv.CantonConfig{}, + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + assert.Equal(t, tt.want, cantonAuthConfigured(tt.config)) + }) + } +} + +func Test_chainLoaderCanton_cantonAuthProvider(t *testing.T) { + t.Parallel() + + ctx := t.Context() + loader := newChainLoaderCanton(nil, cfgenv.OnchainConfig{ + Canton: cfgenv.CantonConfig{JWTToken: "static-token"}, + }) + + provider, err := loader.cantonAuthProvider(ctx, chainsel.CANTON_LOCALNET.Selector) + require.NoError(t, err) + require.NotNil(t, provider) + + token, err := provider.TokenSource().Token() + require.NoError(t, err) + require.Equal(t, "static-token", token.AccessToken) + + loaderMissingJWT := newChainLoaderCanton(nil, cfgenv.OnchainConfig{ + Canton: cfgenv.CantonConfig{AuthStrategy: cfgenv.CantonAuthStrategyStatic}, + }) + _, err = loaderMissingJWT.cantonAuthProvider(ctx, chainsel.CANTON_LOCALNET.Selector) + require.Error(t, err) + require.ErrorContains(t, err, "JWT token is required") +} + // newFakeRPCServer returns a fake RPC server which always answers with a valid `eth_blockNumberā€œ // response for evm and a valid config for ton. // diff --git a/engine/cld/config/env/config.go b/engine/cld/config/env/config.go index 5c7f24024..f350a6734 100644 --- a/engine/cld/config/env/config.go +++ b/engine/cld/config/env/config.go @@ -82,11 +82,11 @@ type TronConfig struct { DeployerKey string `mapstructure:"deployer_key" yaml:"deployer_key"` // Secret: The private key of the deployer account. } -// CantonAuthType is the authentication scheme for Canton participant APIs. +// CantonAuthStrategy is the authentication strategy for Canton participant APIs. const ( - CantonAuthTypeStatic = "static" // Pre-obtained JWT (e.g. from canton-login). - CantonAuthTypeClientCredentials = "client_credentials" // CI: fetch token with client_id + client_secret + auth_url. - CantonAuthTypeAuthorizationCode = "authorization_code" // Local: browser flow with client_id + auth_url. + CantonAuthStrategyStatic = "static" // Pre-obtained JWT (e.g. from canton-login). + CantonAuthStrategyClientCredentials = "client_credentials" // CI: fetch token with client_id + client_secret + auth_url. + CantonAuthStrategyAuthorizationCode = "authorization_code" // Local dev: browser flow with client_id + auth_url (not for CI). ) // CantonConfig is the configuration for the Canton Chains. @@ -94,9 +94,9 @@ const ( // WARNING: This data type contains sensitive fields and should not be logged or set in file // configuration. type CantonConfig struct { - // AuthType selects how to obtain the token: "static" (jwt_token), "client_credentials" (CI), or "authorization_code" (local browser). - AuthType string `mapstructure:"auth_type" yaml:"auth_type"` - // JWT token for static auth. Used when auth_type is "static". + // AuthStrategy selects how to obtain the token: "static" (jwt_token), "client_credentials" (CI), or "authorization_code" (local browser). + AuthStrategy string `mapstructure:"auth_strategy" yaml:"auth_strategy"` + // JWT token for static auth. Used when auth_strategy is "static". JWTToken string `mapstructure:"jwt_token" yaml:"jwt_token"` // Secret // AuthURL is the OIDC base URL (e.g. https://auth.example.com). Token URL is AuthURL/v1/token, authorize is AuthURL/v1/authorize. AuthURL string `mapstructure:"auth_url" yaml:"auth_url"` @@ -280,7 +280,7 @@ var ( "onchain.stellar.deployer_key": {"ONCHAIN_STELLAR_DEPLOYER_KEY"}, "onchain.ton.deployer_key": {"ONCHAIN_TON_DEPLOYER_KEY", "TON_DEPLOYER_KEY"}, "onchain.ton.wallet_version": {"ONCHAIN_TON_WALLET_VERSION", "TON_WALLET_VERSION"}, - "onchain.canton.auth_type": {"ONCHAIN_CANTON_AUTH_TYPE"}, + "onchain.canton.auth_strategy": {"ONCHAIN_CANTON_AUTH_STRATEGY"}, "onchain.canton.jwt_token": {"ONCHAIN_CANTON_JWT_TOKEN"}, "onchain.canton.auth_url": {"ONCHAIN_CANTON_AUTH_URL"}, "onchain.canton.client_id": {"ONCHAIN_CANTON_CLIENT_ID"}, diff --git a/engine/cld/config/env/config_test.go b/engine/cld/config/env/config_test.go index d9eb8451b..9c6c76adc 100644 --- a/engine/cld/config/env/config_test.go +++ b/engine/cld/config/env/config_test.go @@ -45,7 +45,7 @@ var ( DeployerKey: "0x567", }, Canton: CantonConfig{ - AuthType: "", + AuthStrategy: "", JWTToken: "", AuthURL: "", ClientID: "", @@ -106,6 +106,11 @@ var ( "CATALOG_GRPC": "http://localhost:8080", "CATALOG_AUTH_KMS_KEY_ID": "123", "CATALOG_AUTH_KMS_KEY_REGION": "us-east-1", + "ONCHAIN_CANTON_AUTH_STRATEGY": "client_credentials", + "ONCHAIN_CANTON_AUTH_URL": "https://canton-auth.example.com", + "ONCHAIN_CANTON_CLIENT_ID": "canton-client-id", + "ONCHAIN_CANTON_CLIENT_SECRET": "canton-client-secret", + "ONCHAIN_CANTON_JWT_TOKEN": "canton-jwt-token", } legacyEnvVars = map[string]string{ @@ -131,6 +136,11 @@ var ( "TON_WALLET_VERSION": "V5R1", // These values do not have a legacy equivalent "ONCHAIN_STELLAR_DEPLOYER_KEY": "0x567", // Stellar is new, uses new-style env var + "ONCHAIN_CANTON_AUTH_STRATEGY": "client_credentials", + "ONCHAIN_CANTON_AUTH_URL": "https://canton-auth.example.com", + "ONCHAIN_CANTON_CLIENT_ID": "canton-client-id", + "ONCHAIN_CANTON_CLIENT_SECRET": "canton-client-secret", + "ONCHAIN_CANTON_JWT_TOKEN": "canton-jwt-token", "CATALOG_GRPC": "http://localhost:8080", "CATALOG_AUTH_KMS_KEY_ID": "123", "CATALOG_AUTH_KMS_KEY_REGION": "us-east-1", @@ -171,11 +181,11 @@ var ( WalletVersion: "V5R1", }, Canton: CantonConfig{ - AuthType: "", - JWTToken: "", - AuthURL: "", - ClientID: "", - ClientSecret: "", + AuthStrategy: "client_credentials", + JWTToken: "canton-jwt-token", + AuthURL: "https://canton-auth.example.com", + ClientID: "canton-client-id", + ClientSecret: "canton-client-secret", }, }, Offchain: OffchainConfig{ diff --git a/engine/cld/config/env/testdata/config.yml b/engine/cld/config/env/testdata/config.yml index 28bba081f..5e61f5033 100644 --- a/engine/cld/config/env/testdata/config.yml +++ b/engine/cld/config/env/testdata/config.yml @@ -25,7 +25,7 @@ onchain: stellar: deployer_key: "0x567" canton: - auth_type: "" + auth_strategy: "" jwt_token: "" auth_url: "" client_id: "" diff --git a/engine/cld/config/env/testdata/config_with_optional_values.yml b/engine/cld/config/env/testdata/config_with_optional_values.yml index 2cd84dd8a..28a6109c7 100644 --- a/engine/cld/config/env/testdata/config_with_optional_values.yml +++ b/engine/cld/config/env/testdata/config_with_optional_values.yml @@ -20,7 +20,7 @@ onchain: stellar: deployer_key: "0x567" canton: - auth_type: "" + auth_strategy: "" jwt_token: "" auth_url: "" client_id: "" diff --git a/go.mod b/go.mod index eb568e066..95164d566 100644 --- a/go.mod +++ b/go.mod @@ -61,6 +61,7 @@ require ( ) require ( + cloud.google.com/go/compute/metadata v0.9.0 // indirect github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 // indirect github.com/btcsuite/btcutil v1.0.2 // indirect @@ -331,7 +332,7 @@ require ( golang.org/x/sys v0.41.0 // indirect golang.org/x/term v0.40.0 // indirect golang.org/x/text v0.34.0 // indirect - golang.org/x/time v0.14.0 // indirect + golang.org/x/time v0.14.0 golang.org/x/tools v0.42.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 diff --git a/go.sum b/go.sum index 9ab48d6a1..1b7068536 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= From 72a7c65915b0f38a006ce17d8f189aa9de203278 Mon Sep 17 00:00:00 2001 From: stackman27 Date: Tue, 26 May 2026 23:23:14 -0700 Subject: [PATCH 07/15] Align Canton OAuth env vars with CLD secret naming. Use CANTON_OKTA_AUTHORIZER, CANTON_OKTA_CLIENT_ID, and CANTON_OKTA_CLIENT_SECRET so CLDF matches the keys registered in CLD CI. Co-authored-by: Cursor --- engine/cld/config/env/config.go | 12 ++++++------ engine/cld/config/env/config_test.go | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/engine/cld/config/env/config.go b/engine/cld/config/env/config.go index 8c9976c9c..a7f5718a9 100644 --- a/engine/cld/config/env/config.go +++ b/engine/cld/config/env/config.go @@ -98,11 +98,11 @@ type CantonConfig struct { AuthStrategy string `mapstructure:"auth_strategy" yaml:"auth_strategy"` // JWT token for static auth. Used when auth_strategy is "static". JWTToken string `mapstructure:"jwt_token" yaml:"jwt_token"` // Secret - // AuthURL is the OIDC base URL (e.g. https://auth.example.com). Token URL is AuthURL/v1/token, authorize is AuthURL/v1/authorize. + // AuthURL is the Okta authorization server URL (env: CANTON_OKTA_AUTHORIZER). AuthURL string `mapstructure:"auth_url" yaml:"auth_url"` - // ClientID is the OAuth2 client ID. Used for client_credentials and authorization_code. + // ClientID is the OAuth2 client ID (env: CANTON_OKTA_CLIENT_ID). ClientID string `mapstructure:"client_id" yaml:"client_id"` // Secret - // ClientSecret is the OAuth2 client secret. Required only for client_credentials (CI). + // ClientSecret is the OAuth2 client secret (env: CANTON_OKTA_CLIENT_SECRET). Required for client_credentials (CI). ClientSecret string `mapstructure:"client_secret" yaml:"client_secret"` // Secret } @@ -283,9 +283,9 @@ var ( "onchain.ton.wallet_version": {"ONCHAIN_TON_WALLET_VERSION", "TON_WALLET_VERSION"}, "onchain.canton.auth_strategy": {"ONCHAIN_CANTON_AUTH_STRATEGY"}, "onchain.canton.jwt_token": {"ONCHAIN_CANTON_JWT_TOKEN"}, - "onchain.canton.auth_url": {"ONCHAIN_CANTON_AUTH_URL"}, - "onchain.canton.client_id": {"ONCHAIN_CANTON_CLIENT_ID"}, - "onchain.canton.client_secret": {"ONCHAIN_CANTON_CLIENT_SECRET"}, + "onchain.canton.auth_url": {"CANTON_OKTA_AUTHORIZER"}, + "onchain.canton.client_id": {"CANTON_OKTA_CLIENT_ID"}, + "onchain.canton.client_secret": {"CANTON_OKTA_CLIENT_SECRET"}, "offchain.job_distributor.auth.cognito_app_client_id": {"OFFCHAIN_JD_AUTH_COGNITO_APP_CLIENT_ID", "JD_AUTH_COGNITO_APP_CLIENT_ID"}, "offchain.job_distributor.auth.cognito_app_client_secret": {"OFFCHAIN_JD_AUTH_COGNITO_APP_CLIENT_SECRET", "JD_AUTH_COGNITO_APP_CLIENT_SECRET"}, "offchain.job_distributor.auth.aws_region": {"OFFCHAIN_JD_AUTH_AWS_REGION", "JD_AUTH_AWS_REGION"}, diff --git a/engine/cld/config/env/config_test.go b/engine/cld/config/env/config_test.go index e0c5f9dc0..f936b40c3 100644 --- a/engine/cld/config/env/config_test.go +++ b/engine/cld/config/env/config_test.go @@ -107,9 +107,9 @@ var ( "CATALOG_AUTH_KMS_KEY_ID": "123", "CATALOG_AUTH_KMS_KEY_REGION": "us-east-1", "ONCHAIN_CANTON_AUTH_STRATEGY": "client_credentials", - "ONCHAIN_CANTON_AUTH_URL": "https://canton-auth.example.com", - "ONCHAIN_CANTON_CLIENT_ID": "canton-client-id", - "ONCHAIN_CANTON_CLIENT_SECRET": "canton-client-secret", + "CANTON_OKTA_AUTHORIZER": "https://canton-auth.example.com", + "CANTON_OKTA_CLIENT_ID": "canton-client-id", + "CANTON_OKTA_CLIENT_SECRET": "canton-client-secret", "ONCHAIN_CANTON_JWT_TOKEN": "canton-jwt-token", } @@ -137,9 +137,9 @@ var ( // These values do not have a legacy equivalent "ONCHAIN_STELLAR_DEPLOYER_KEY": "0x567", // Stellar is new, uses new-style env var "ONCHAIN_CANTON_AUTH_STRATEGY": "client_credentials", - "ONCHAIN_CANTON_AUTH_URL": "https://canton-auth.example.com", - "ONCHAIN_CANTON_CLIENT_ID": "canton-client-id", - "ONCHAIN_CANTON_CLIENT_SECRET": "canton-client-secret", + "CANTON_OKTA_AUTHORIZER": "https://canton-auth.example.com", + "CANTON_OKTA_CLIENT_ID": "canton-client-id", + "CANTON_OKTA_CLIENT_SECRET": "canton-client-secret", "ONCHAIN_CANTON_JWT_TOKEN": "canton-jwt-token", "CATALOG_GRPC": "http://localhost:8080", "CATALOG_AUTH_KMS_KEY_ID": "123", From 107e06db6b193e753bc24d979850464fe9136773 Mon Sep 17 00:00:00 2001 From: stackman27 Date: Sun, 31 May 2026 13:40:52 -0700 Subject: [PATCH 08/15] wire up readAs properly --- chain/mcms/adapters/chain_access.go | 26 +++++++++++++++++++----- chain/mcms/adapters/chain_access_test.go | 7 ++++--- engine/cld/chains/chains.go | 14 ++++++++----- engine/cld/chains/chains_test.go | 4 ++-- engine/cld/config/network/metadata.go | 7 ++++++- 5 files changed, 42 insertions(+), 16 deletions(-) diff --git a/chain/mcms/adapters/chain_access.go b/chain/mcms/adapters/chain_access.go index ac1b3aa58..4d236277b 100644 --- a/chain/mcms/adapters/chain_access.go +++ b/chain/mcms/adapters/chain_access.go @@ -6,6 +6,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" sol "github.com/gagliardetto/solana-go" solrpc "github.com/gagliardetto/solana-go/rpc" + mcmscanton "github.com/smartcontractkit/mcms/sdk/canton" "github.com/smartcontractkit/mcms/sdk/evm" mcmssui "github.com/smartcontractkit/mcms/sdk/sui" "github.com/stellar/go-stellar-sdk/clients/rpcclient" @@ -147,13 +148,28 @@ func (a *ChainAccessAdapter) StellarSigner(selector uint64) (cldfstellar.Stellar return ch.Signer, ok } -// CantonChain returns the Canton chain for the given selector. -// Canton chains contain participants with their own service clients, so we return the chain itself. -func (a *ChainAccessAdapter) CantonChain(selector uint64) (cldfcanton.Chain, bool) { +// CantonChain returns the MCMS Canton chain view for the given selector. +func (a *ChainAccessAdapter) CantonChain(selector uint64) (mcmscanton.Chain, bool) { ch, ok := a.inner.CantonChains()[selector] if !ok { - return cldfcanton.Chain{}, false + return mcmscanton.Chain{}, false } - return ch, true + return cantonChainFromCLDF(ch), true +} + +// cantonChainFromCLDF maps CLDF participant clients into mcms/sdk/canton.Chain for chainwrappers. +func cantonChainFromCLDF(ch cldfcanton.Chain) mcmscanton.Chain { + participants := make([]mcmscanton.Participant, 0, len(ch.Participants)) + for _, p := range ch.Participants { + participants = append(participants, mcmscanton.Participant{ + PartyID: p.PartyID, + LedgerServices: mcmscanton.LedgerServices{ + State: p.LedgerServices.State, + Command: p.LedgerServices.Command, + }, + }) + } + + return mcmscanton.Chain{Participants: participants} } diff --git a/chain/mcms/adapters/chain_access_test.go b/chain/mcms/adapters/chain_access_test.go index e1b553396..fa0816533 100644 --- a/chain/mcms/adapters/chain_access_test.go +++ b/chain/mcms/adapters/chain_access_test.go @@ -10,6 +10,8 @@ import ( "github.com/stretchr/testify/require" tonwallet "github.com/xssnick/tonutils-go/ton/wallet" + mcmscanton "github.com/smartcontractkit/mcms/sdk/canton" + "github.com/smartcontractkit/chainlink-deployments-framework/chain" "github.com/smartcontractkit/chainlink-deployments-framework/chain/aptos" aptosmocks "github.com/smartcontractkit/chainlink-deployments-framework/chain/aptos/mocks" @@ -70,7 +72,7 @@ func TestChainAccess_UnknownSelector(t *testing.T) { cantonChain, ok := a.CantonChain(999) require.False(t, ok) - require.Equal(t, chaincanton.Chain{}, cantonChain) + require.Equal(t, mcmscanton.Chain{}, cantonChain) } func TestChainAccess_SelectorsAndLookups(t *testing.T) { @@ -156,6 +158,5 @@ func TestChainAccess_SelectorsAndLookups(t *testing.T) { gotCanton, ok := a.CantonChain(cantonSel) require.True(t, ok) - require.Equal(t, cantonSel, gotCanton.Selector) - require.Nil(t, gotCanton.Participants) + require.Empty(t, gotCanton.Participants) } diff --git a/engine/cld/chains/chains.go b/engine/cld/chains/chains.go index 9f2cae70e..10cbcfae3 100644 --- a/engine/cld/chains/chains.go +++ b/engine/cld/chains/chains.go @@ -753,7 +753,7 @@ func (l *chainLoaderCanton) Load(ctx context.Context, selector uint64) (fchain.B return nil, fmt.Errorf("canton network %d: no participants found in metadata", selector) } - authProvider, err := l.cantonAuthProvider(ctx, selector) + authProvider, err := l.cantonAuthProvider(ctx, selector, md.InsecureTransport) if err != nil { return nil, err } @@ -767,9 +767,10 @@ func (l *chainLoaderCanton) Load(ctx context.Context, selector uint64) (fchain.B AdminAPIURL: participantMD.AdminAPIURL, ValidatorAPIURL: participantMD.ValidatorAPIURL, }, - UserID: participantMD.UserID, - PartyID: participantMD.PartyID, - AuthProvider: authProvider, + UserID: participantMD.UserID, + PartyID: participantMD.PartyID, + ReadAsPartyIDs: participantMD.ReadAsPartyIDs, + AuthProvider: authProvider, } } @@ -799,7 +800,7 @@ func cantonAuthConfigured(c cfgenv.CantonConfig) bool { } // cantonAuthProvider builds a Canton auth Provider from config. -func (l *chainLoaderCanton) cantonAuthProvider(ctx context.Context, selector uint64) (cantonauth.Provider, error) { +func (l *chainLoaderCanton) cantonAuthProvider(ctx context.Context, selector uint64, insecureTransport bool) (cantonauth.Provider, error) { c := l.cfg.Canton switch c.AuthStrategy { case cfgenv.CantonAuthStrategyClientCredentials: @@ -820,6 +821,9 @@ func (l *chainLoaderCanton) cantonAuthProvider(ctx context.Context, selector uin if c.JWTToken == "" { return nil, fmt.Errorf("canton network %d: JWT token is required for static auth", selector) } + if insecureTransport { + return cantonauth.NewInsecureStaticProvider(c.JWTToken), nil + } return cantonauth.NewStaticProvider(c.JWTToken), nil } diff --git a/engine/cld/chains/chains_test.go b/engine/cld/chains/chains_test.go index 43f9bd657..f75ed3eaa 100644 --- a/engine/cld/chains/chains_test.go +++ b/engine/cld/chains/chains_test.go @@ -1437,7 +1437,7 @@ func Test_chainLoaderCanton_cantonAuthProvider(t *testing.T) { Canton: cfgenv.CantonConfig{JWTToken: "static-token"}, }) - provider, err := loader.cantonAuthProvider(ctx, chainsel.CANTON_LOCALNET.Selector) + provider, err := loader.cantonAuthProvider(ctx, chainsel.CANTON_LOCALNET.Selector, true) require.NoError(t, err) require.NotNil(t, provider) @@ -1448,7 +1448,7 @@ func Test_chainLoaderCanton_cantonAuthProvider(t *testing.T) { loaderMissingJWT := newChainLoaderCanton(nil, cfgenv.OnchainConfig{ Canton: cfgenv.CantonConfig{AuthStrategy: cfgenv.CantonAuthStrategyStatic}, }) - _, err = loaderMissingJWT.cantonAuthProvider(ctx, chainsel.CANTON_LOCALNET.Selector) + _, err = loaderMissingJWT.cantonAuthProvider(ctx, chainsel.CANTON_LOCALNET.Selector, false) require.Error(t, err) require.ErrorContains(t, err, "JWT token is required") } diff --git a/engine/cld/config/network/metadata.go b/engine/cld/config/network/metadata.go index 68479a1e0..75e2a7146 100644 --- a/engine/cld/config/network/metadata.go +++ b/engine/cld/config/network/metadata.go @@ -33,12 +33,17 @@ type CantonParticipantMetadata struct { UserID string `yaml:"user_id"` // The PartyID of the party that should be used for accessing the participant's API endpoints PartyID string `yaml:"party_id"` + // ReadAsPartyIDs lists parties this user may read as (CanReadAs). When set, Canton CCIP + // factory deploy sequences use proposal-driven mode (MCMSEnabled) instead of direct ledger submit. + ReadAsPartyIDs []string `yaml:"read_as_party_ids,omitempty"` } // CantonMetadata holds metadata specific to Canton networks. // It contains the list of participants to connect to. type CantonMetadata struct { - Participants []CantonParticipantMetadata `yaml:"participants"` + // InsecureTransport uses plaintext gRPC (local docker compose). When false, static JWT auth uses TLS. + InsecureTransport bool `yaml:"insecure_transport,omitempty"` + Participants []CantonParticipantMetadata `yaml:"participants"` } // AnvilConfig holds the configuration for starting an Anvil node. From b780b0f7297dce6cc77d0b3f9db085b92188be1b Mon Sep 17 00:00:00 2001 From: stackman27 Date: Fri, 5 Jun 2026 11:11:22 -0700 Subject: [PATCH 09/15] rename --- engine/cld/config/env/config.go | 12 ++++++------ engine/cld/config/env/config_test.go | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/engine/cld/config/env/config.go b/engine/cld/config/env/config.go index a7f5718a9..83aa04b37 100644 --- a/engine/cld/config/env/config.go +++ b/engine/cld/config/env/config.go @@ -98,11 +98,11 @@ type CantonConfig struct { AuthStrategy string `mapstructure:"auth_strategy" yaml:"auth_strategy"` // JWT token for static auth. Used when auth_strategy is "static". JWTToken string `mapstructure:"jwt_token" yaml:"jwt_token"` // Secret - // AuthURL is the Okta authorization server URL (env: CANTON_OKTA_AUTHORIZER). + // AuthURL is the Okta authorization server URL (env: ONCHAIN_CANTON_OKTA_AUTHORIZER). AuthURL string `mapstructure:"auth_url" yaml:"auth_url"` - // ClientID is the OAuth2 client ID (env: CANTON_OKTA_CLIENT_ID). + // ClientID is the OAuth2 client ID (env: ONCHAIN_CANTON_OKTA_CLIENT_ID). ClientID string `mapstructure:"client_id" yaml:"client_id"` // Secret - // ClientSecret is the OAuth2 client secret (env: CANTON_OKTA_CLIENT_SECRET). Required for client_credentials (CI). + // ClientSecret is the OAuth2 client secret (env: ONCHAIN_CANTON_OKTA_CLIENT_SECRET). Required for client_credentials (CI). ClientSecret string `mapstructure:"client_secret" yaml:"client_secret"` // Secret } @@ -283,9 +283,9 @@ var ( "onchain.ton.wallet_version": {"ONCHAIN_TON_WALLET_VERSION", "TON_WALLET_VERSION"}, "onchain.canton.auth_strategy": {"ONCHAIN_CANTON_AUTH_STRATEGY"}, "onchain.canton.jwt_token": {"ONCHAIN_CANTON_JWT_TOKEN"}, - "onchain.canton.auth_url": {"CANTON_OKTA_AUTHORIZER"}, - "onchain.canton.client_id": {"CANTON_OKTA_CLIENT_ID"}, - "onchain.canton.client_secret": {"CANTON_OKTA_CLIENT_SECRET"}, + "onchain.canton.auth_url": {"ONCHAIN_CANTON_OKTA_AUTHORIZER"}, + "onchain.canton.client_id": {"ONCHAIN_CANTON_OKTA_CLIENT_ID"}, + "onchain.canton.client_secret": {"ONCHAIN_CANTON_OKTA_CLIENT_SECRET"}, "offchain.job_distributor.auth.cognito_app_client_id": {"OFFCHAIN_JD_AUTH_COGNITO_APP_CLIENT_ID", "JD_AUTH_COGNITO_APP_CLIENT_ID"}, "offchain.job_distributor.auth.cognito_app_client_secret": {"OFFCHAIN_JD_AUTH_COGNITO_APP_CLIENT_SECRET", "JD_AUTH_COGNITO_APP_CLIENT_SECRET"}, "offchain.job_distributor.auth.aws_region": {"OFFCHAIN_JD_AUTH_AWS_REGION", "JD_AUTH_AWS_REGION"}, diff --git a/engine/cld/config/env/config_test.go b/engine/cld/config/env/config_test.go index f936b40c3..b2657e23c 100644 --- a/engine/cld/config/env/config_test.go +++ b/engine/cld/config/env/config_test.go @@ -107,9 +107,9 @@ var ( "CATALOG_AUTH_KMS_KEY_ID": "123", "CATALOG_AUTH_KMS_KEY_REGION": "us-east-1", "ONCHAIN_CANTON_AUTH_STRATEGY": "client_credentials", - "CANTON_OKTA_AUTHORIZER": "https://canton-auth.example.com", - "CANTON_OKTA_CLIENT_ID": "canton-client-id", - "CANTON_OKTA_CLIENT_SECRET": "canton-client-secret", + "ONCHAIN_CANTON_OKTA_AUTHORIZER": "https://canton-auth.example.com", + "ONCHAIN_CANTON_OKTA_CLIENT_ID": "canton-client-id", + "ONCHAIN_CANTON_OKTA_CLIENT_SECRET": "canton-client-secret", "ONCHAIN_CANTON_JWT_TOKEN": "canton-jwt-token", } @@ -137,9 +137,9 @@ var ( // These values do not have a legacy equivalent "ONCHAIN_STELLAR_DEPLOYER_KEY": "0x567", // Stellar is new, uses new-style env var "ONCHAIN_CANTON_AUTH_STRATEGY": "client_credentials", - "CANTON_OKTA_AUTHORIZER": "https://canton-auth.example.com", - "CANTON_OKTA_CLIENT_ID": "canton-client-id", - "CANTON_OKTA_CLIENT_SECRET": "canton-client-secret", + "ONCHAIN_CANTON_OKTA_AUTHORIZER": "https://canton-auth.example.com", + "ONCHAIN_CANTON_OKTA_CLIENT_ID": "canton-client-id", + "ONCHAIN_CANTON_OKTA_CLIENT_SECRET": "canton-client-secret", "ONCHAIN_CANTON_JWT_TOKEN": "canton-jwt-token", "CATALOG_GRPC": "http://localhost:8080", "CATALOG_AUTH_KMS_KEY_ID": "123", From 161b25dd7b82872d4c92987d6636ecb70a738e8f Mon Sep 17 00:00:00 2001 From: stackman27 Date: Fri, 5 Jun 2026 13:01:45 -0700 Subject: [PATCH 10/15] bump mcms --- go.mod | 84 ++++++++++++++------------- go.sum | 180 +++++++++++++++++++++++++++++---------------------------- 2 files changed, 134 insertions(+), 130 deletions(-) diff --git a/go.mod b/go.mod index 80a903770..f00c59d9d 100644 --- a/go.mod +++ b/go.mod @@ -31,20 +31,20 @@ require ( github.com/samber/lo v1.53.0 github.com/segmentio/ksuid v1.0.4 github.com/smartcontractkit/ccip-owner-contracts v0.1.0 - github.com/smartcontractkit/chain-selectors v1.0.100 + github.com/smartcontractkit/chain-selectors v1.0.101 github.com/smartcontractkit/chainlink-aptos v0.0.0-20260428085939-5c70de12dbfc - github.com/smartcontractkit/chainlink-ccip/chains/evm v0.0.0-20260415165642-49f23e4d76cc - github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260121163256-85accaf3d28d - github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5 + github.com/smartcontractkit/chainlink-ccip/chains/evm v0.0.0-20260512180815-d7a89b0a5784 + github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260129103204-4c8453dd8139 + github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260129103204-4c8453dd8139 github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0 - github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.2 + github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.4 github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.5 github.com/smartcontractkit/chainlink-ton v1.0.5-0.20260514223130-48bc90aca745 - github.com/smartcontractkit/chainlink-tron/relayer v0.0.11-0.20250908203554-5bd9d2fe9513 + github.com/smartcontractkit/chainlink-tron/relayer v0.0.11-0.20251014143056-a0c6328c91e9 github.com/smartcontractkit/freeport v0.1.3-0.20250828155247-add56fa28aad github.com/smartcontractkit/libocr v0.0.0-20260304194147-a03701e2c02e - github.com/smartcontractkit/mcms v0.45.0 + github.com/smartcontractkit/mcms v0.46.0 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 github.com/spf13/viper v1.21.0 @@ -55,7 +55,7 @@ require ( github.com/xssnick/tonutils-go v1.14.1 github.com/zksync-sdk/zksync2-go v1.1.1-0.20250620124214-2c742ee399c6 go.uber.org/zap v1.28.0 - golang.org/x/crypto v0.51.0 + golang.org/x/crypto v0.52.0 golang.org/x/oauth2 v0.36.0 google.golang.org/grpc v1.81.1 google.golang.org/protobuf v1.36.11 @@ -66,59 +66,61 @@ require ( cloud.google.com/go/compute/metadata v0.9.0 // indirect github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 // indirect - github.com/btcsuite/btcutil v1.0.2 // indirect + github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/crate-crypto/go-eth-kzg v1.5.0 // indirect github.com/creachadair/jrpc2 v1.2.0 // indirect github.com/creachadair/mds v0.13.4 // indirect github.com/dchest/siphash v1.2.3 // indirect github.com/emicklei/dot v1.6.2 // indirect - github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/emicklei/go-restful/v3 v3.12.1 // indirect github.com/ethereum/c-kzg-4844/v2 v2.1.6 // indirect github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab // indirect github.com/ferranbt/fastssz v0.1.4 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect - github.com/golang-jwt/jwt/v5 v5.3.0 // indirect + github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/google/gnostic-models v0.6.9 // indirect - github.com/google/gofuzz v1.2.0 // indirect github.com/grafana/otel-profiling-go v0.5.1 // indirect github.com/grafana/pyroscope-go v1.2.8 // indirect github.com/grafana/pyroscope-go/godeltaprof v0.1.9 // indirect + github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect github.com/jackc/pgx/v5 v5.9.2 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/klauspost/cpuid/v2 v2.2.10 // indirect - github.com/lib/pq v1.11.1 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect + github.com/lib/pq v1.12.3 // indirect github.com/minio/sha256-simd v1.0.1 // indirect - github.com/moby/moby/api v1.54.1 // indirect + github.com/moby/moby/api v1.54.2 // indirect github.com/moby/moby/client v0.4.0 // indirect github.com/moby/spdystream v0.5.1 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/onsi/ginkgo/v2 v2.22.1 // indirect github.com/onsi/gomega v1.36.2 // indirect + github.com/smartcontractkit/chainlink-canton v0.0.0-20260602133237-99f834640c9d // indirect github.com/smartcontractkit/chainlink-common v0.11.2-0.20260506120607-7f10be016c89 // indirect github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 // indirect github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260505131349-78e491b80735 // indirect github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b // indirect github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9 // indirect + github.com/smartcontractkit/go-daml v0.0.0-20260604143752-c6f6567940ba // indirect github.com/stellar/go-xdr v0.0.0-20260312225820-cc2b0611aabf // indirect go.uber.org/goleak v1.3.0 // indirect - go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/api v0.32.3 // indirect - k8s.io/apimachinery v0.32.3 // indirect + k8s.io/apimachinery v0.33.2 // indirect k8s.io/client-go v0.32.3 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect - k8s.io/utils v0.0.0-20241104163129-6fe5fd82f078 // indirect + k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect + k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) @@ -126,7 +128,7 @@ require ( dario.cat/mergo v1.0.2 // indirect filippo.io/edwards25519 v1.1.1 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect - github.com/BurntSushi/toml v1.5.0 // indirect + github.com/BurntSushi/toml v1.6.0 // indirect github.com/DataDog/zstd v1.5.6 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/VictoriaMetrics/fastcache v1.13.0 // indirect @@ -143,7 +145,7 @@ require ( 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.25.1 // indirect + github.com/aws/smithy-go v1.26.0 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -176,10 +178,10 @@ require ( github.com/deckarep/golang-set/v2 v2.8.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/distribution/reference v0.6.0 // indirect - github.com/docker/go-connections v0.6.0 // indirect + github.com/docker/go-connections v0.7.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/ebitengine/purego v0.10.0 // indirect - github.com/fatih/color v1.18.0 // indirect + github.com/fatih/color v1.19.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect @@ -202,7 +204,7 @@ require ( github.com/gorilla/websocket v1.5.3 // indirect github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect github.com/hashicorp/go-bexpr v0.1.10 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-plugin v1.8.0 // indirect @@ -265,12 +267,12 @@ require ( github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.66.1 // indirect + github.com/prometheus/common v1.20.99 // indirect github.com/prometheus/procfs v0.16.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rs/cors v1.11.1 // indirect - github.com/rs/zerolog v1.34.0 // indirect + github.com/rs/zerolog v1.35.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.11.0 // indirect github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect @@ -280,7 +282,7 @@ require ( github.com/shopspring/decimal v1.4.0 // indirect github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 // indirect github.com/sirupsen/logrus v1.9.4 // indirect - github.com/smartcontractkit/chainlink-sui v0.0.0-20260429183453-39df0198aed8 + github.com/smartcontractkit/chainlink-sui v0.0.0-20260527160341-aa3adc0abf67 github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect github.com/spf13/afero v1.15.0 // indirect @@ -293,7 +295,7 @@ require ( github.com/suzuki-shunsuke/go-convmap v0.2.1 github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tidwall/gjson v1.18.0 // indirect - github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/match v1.2.0 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect github.com/tklauser/numcpus v0.11.0 // indirect @@ -308,9 +310,9 @@ require ( go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 // indirect - go.opentelemetry.io/otel v1.43.0 // indirect + go.opentelemetry.io/otel v1.44.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect @@ -319,23 +321,23 @@ require ( go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 // indirect - go.opentelemetry.io/otel/log v0.19.0 // indirect - go.opentelemetry.io/otel/metric v1.43.0 // indirect - go.opentelemetry.io/otel/sdk v1.43.0 // indirect - go.opentelemetry.io/otel/sdk/log v0.19.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect - go.opentelemetry.io/otel/trace v1.43.0 // indirect + go.opentelemetry.io/otel/log v0.20.0 // indirect + go.opentelemetry.io/otel/metric v1.44.0 // indirect + go.opentelemetry.io/otel/sdk v1.44.0 // indirect + go.opentelemetry.io/otel/sdk/log v0.20.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.44.0 // indirect + go.opentelemetry.io/otel/trace v1.44.0 // indirect go.opentelemetry.io/proto/otlp v1.10.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/ratelimit v0.3.1 // indirect - golang.org/x/net v0.54.0 // indirect + golang.org/x/net v0.55.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.44.0 // indirect + golang.org/x/sys v0.45.0 // indirect golang.org/x/term v0.43.0 // indirect golang.org/x/text v0.37.0 // indirect golang.org/x/time v0.15.0 golang.org/x/tools v0.45.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 + google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect ) diff --git a/go.sum b/go.sum index a5a83763b..31a2be660 100644 --- a/go.sum +++ b/go.sum @@ -13,8 +13,8 @@ github.com/AlekSi/pointer v1.1.0/go.mod h1:y7BvfRI3wXPWKXEBhU71nbnIEEZX0QTSB2Bj4 github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= -github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= +github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/DataDog/zstd v1.5.6 h1:LbEglqepa/ipmmQJUDnSsfvA8e8IStVcGaFWDuxvGOY= github.com/DataDog/zstd v1.5.6/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Masterminds/semver/v3 v3.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE= @@ -74,8 +74,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB 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.25.1 h1:J8ERsGSU7d+aCmdQur5Txg6bVoYelvQJgtZehD12GkI= -github.com/aws/smithy-go v1.25.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/aws/smithy-go v1.26.0 h1:9ouqbi+NyKP7fV3Te7UElCwdAb6Y8uk7LGwPE5tVe/s= +github.com/aws/smithy-go v1.26.0/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df h1:GSoSVRLoBaFpOOds6QyY1L8AX7uoY+Ln3BHc22W40X0= @@ -111,8 +111,8 @@ github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6 github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts= -github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= @@ -168,7 +168,6 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v1.0.0-rc.2 h1:0SPgaNZPVWGEi4grZdV8VRYQn78y+nm6acgLGv/QzE4= github.com/containerd/platforms v1.0.0-rc.2/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= @@ -213,16 +212,16 @@ github.com/digital-asset/dazl-client/v8 v8.9.0 h1:F2qTUWtHAjhGyRGV+xTim+VAFwM99F github.com/digital-asset/dazl-client/v8 v8.9.0/go.mod h1:q1KevCJ8FpH8je2MnnjN8/QUfhstB4fKpyKyqDtqFh0= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= -github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= +github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c= +github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= -github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= -github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= +github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= 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.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= @@ -234,8 +233,8 @@ github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1 github.com/ethereum/go-ethereum v1.17.3 h1:Ev/sQHH+UdKZHWjuVzhu2pxhi/sXaPZl23Q+Q5LDd4Q= github.com/ethereum/go-ethereum v1.17.3/go.mod h1:f2EhRwqewIZkGoQekywI2Y2RZAMTSavLNkD9qItFy1A= 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/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= +github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= 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= @@ -312,7 +311,6 @@ github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= @@ -321,8 +319,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= -github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= +github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -376,14 +374,16 @@ github.com/grafana/pyroscope-go v1.2.8 h1:UvCwIhlx9DeV7F6TW/z8q1Mi4PIm3vuUJ2ZlCE github.com/grafana/pyroscope-go v1.2.8/go.mod h1:SSi59eQ1/zmKoY/BKwa5rSFsJaq+242Bcrr4wPix1g8= github.com/grafana/pyroscope-go/godeltaprof v0.1.9 h1:c1Us8i6eSmkW+Ez05d3co8kasnuOY813tbMN8i/a3Og= github.com/grafana/pyroscope-go/godeltaprof v0.1.9/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/graph-gophers/graphql-go v1.5.0 h1:fDqblo50TEpD0LY7RXk/LFVYEVqo3+tXMNMPSVXA1yc= github.com/graph-gophers/graphql-go v1.5.0/go.mod h1:YtmJZDLbF1YYNrlNAuiO5zAStUWc3XZT07iGsVqe1Os= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= @@ -461,8 +461,8 @@ github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6 github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE= github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= -github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= -github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -478,8 +478,8 @@ github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.11.1 h1:wuChtj2hfsGmmx3nf1m7xC2XpK6OtelS2shMY+bGMtI= -github.com/lib/pq v1.11.1/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= +github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ= +github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 h1:PTw+yKnXcOFCR6+8hHTyWBeQ/P4Nb7dd4/0ohEcWQuM= @@ -496,13 +496,10 @@ github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= @@ -528,8 +525,8 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8= github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU= -github.com/moby/moby/api v1.54.1 h1:TqVzuJkOLsgLDDwNLmYqACUuTehOHRGKiPhvH8V3Nn4= -github.com/moby/moby/api v1.54.1/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= +github.com/moby/moby/api v1.54.2 h1:wiat9QAhnDQjA7wk1kh/TqHz2I1uUA7M7t9SAl/JNXg= +github.com/moby/moby/api v1.54.2/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= github.com/moby/moby/client v0.4.0 h1:S+2XegzHQrrvTCvF6s5HFzcrywWQmuVnhOXe2kiWjIw= github.com/moby/moby/client v0.4.0/go.mod h1:QWPbvWchQbxBNdaLSpoKpCdf5E+WxFAgNHogCWDoa7g= github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U= @@ -625,8 +622,8 @@ github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UH github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= -github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= +github.com/prometheus/common v1.20.99 h1:vZEybF3CT0t6L0UjsOtHRML7vuIglHocmvJMMH/se4M= +github.com/prometheus/common v1.20.99/go.mod h1:VX44Tebe4qpuTK+MQWg25h4fJGKBqzObSdxuB7y8K/Y= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/prysmaticlabs/gohashtree v0.0.4-beta h1:H/EbCuXPeTV3lpKeXGPpEV9gsUpkqOOVnWapUyeWro4= @@ -639,9 +636,8 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= -github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= -github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= -github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= +github.com/rs/zerolog v1.35.1 h1:m7xQeoiLIiV0BCEY4Hs+j2NG4Gp2o2KPKmhnnLiazKI= +github.com/rs/zerolog v1.35.1/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= @@ -671,16 +667,18 @@ github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/smartcontractkit/ccip-owner-contracts v0.1.0 h1:GiBDtlx7539o7AKlDV+9LsA7vTMPv+0n7ClhSFnZFAk= github.com/smartcontractkit/ccip-owner-contracts v0.1.0/go.mod h1:NnT6w4Kj42OFFXhSx99LvJZWPpMjmo4+CpDEWfw61xY= -github.com/smartcontractkit/chain-selectors v1.0.100 h1:wpiSpmI/eFjY+wx/nPr5VuNF4hki0prIBMKEaQWn3g4= -github.com/smartcontractkit/chain-selectors v1.0.100/go.mod h1:qy7whtgG5g+7z0jt0nRyii9bLND9m15NZTzuQPkMZ5w= +github.com/smartcontractkit/chain-selectors v1.0.101 h1:TF4ma9h3QeyIZ8XoEmgI5lrUvZfzHAz8tfR0pV0+GCA= +github.com/smartcontractkit/chain-selectors v1.0.101/go.mod h1:qy7whtgG5g+7z0jt0nRyii9bLND9m15NZTzuQPkMZ5w= github.com/smartcontractkit/chainlink-aptos v0.0.0-20260428085939-5c70de12dbfc h1:Um9FBcf0JNSFuGbxgccDG1vM3cNrMGy0SdJ7r6VbX0o= github.com/smartcontractkit/chainlink-aptos v0.0.0-20260428085939-5c70de12dbfc/go.mod h1:zfE2R7887kiwXkGTHKPe5NBgwhFwIC3pnA2uAxrbvig= -github.com/smartcontractkit/chainlink-ccip/chains/evm v0.0.0-20260415165642-49f23e4d76cc h1:dP1ERzdTbiJbHVXfHYdBAi1+8NjgkyQuY2oFNWWWDsQ= -github.com/smartcontractkit/chainlink-ccip/chains/evm v0.0.0-20260415165642-49f23e4d76cc/go.mod h1:7XR5wfgT8hjSsiV+t0EAWvna+rYQeMPaoZf/0g+dios= -github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260121163256-85accaf3d28d h1:xdFpzbApEMz4Rojg2Y2OjFlrh0wu7eB10V2tSZGW5y8= -github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260121163256-85accaf3d28d/go.mod h1:bgmqE7x9xwmIVr8PqLbC0M5iPm4AV2DBl596lO6S5Sw= -github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5 h1:Z4t2ZY+ZyGWxtcXvPr11y4o3CGqhg3frJB5jXkCSvWA= -github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5/go.mod h1:xtZNi6pOKdC3sLvokDvXOhgHzT+cyBqH/gWwvxTxqrg= +github.com/smartcontractkit/chainlink-canton v0.0.0-20260602133237-99f834640c9d h1:aBQYdlGQvqftkcUo0GIgtaLDs/84FQzBYpF4tZUfPA0= +github.com/smartcontractkit/chainlink-canton v0.0.0-20260602133237-99f834640c9d/go.mod h1:/oTkN9bVrQ1ROUNtIJJSNdffl3NReClq+qINNkGAwlY= +github.com/smartcontractkit/chainlink-ccip/chains/evm v0.0.0-20260512180815-d7a89b0a5784 h1:s0rW7GaIKf9m+kfv0dFJPdSbHaMqQiv3xamQP2RVWNE= +github.com/smartcontractkit/chainlink-ccip/chains/evm v0.0.0-20260512180815-d7a89b0a5784/go.mod h1:0dcvbCc+HRK+nIweRSHu3FjkNTV9gi+irBIB3mUUe68= +github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260129103204-4c8453dd8139 h1:jkChf04hhdiMBApbb+lLDxHMY62Md6UeM7v++GSw3K8= +github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260129103204-4c8453dd8139/go.mod h1:wuhagkM/lU0GbV2YcrROOH0GlsfXJYwm6qmpa4CK70w= +github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260129103204-4c8453dd8139 h1:tw3K4UkH5XfW5SoyYkvAlbzrccoGSLdz/XkxD6nyGC8= +github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260129103204-4c8453dd8139/go.mod h1:1WcontO9PeuKdUf5HXfs3nuICtzUvFNnyCmrHkTCF9Y= github.com/smartcontractkit/chainlink-common v0.11.2-0.20260506120607-7f10be016c89 h1:5z3LQ27MJmhiaeqp9S2TzbF5Wm4GGvUKAYOtE9AauR8= github.com/smartcontractkit/chainlink-common v0.11.2-0.20260506120607-7f10be016c89/go.mod h1:G2AII0QmWzXx8Ag9IKnGN3h/gwwNnhHUOCviJievdvo= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 h1:FJAFgXS9oqASnkS03RE1HQwYQQxrO4l46O5JSzxqLgg= @@ -695,26 +693,28 @@ github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625 github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0 h1:hGEJFD2X3oNIPXQbtIPxCJyg5CcKglRCYBmESS+gmeQ= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0/go.mod h1:PjZD54vr6rIKEKQj6HNA4hllvYI/QpT+Zefj3tqkFAs= -github.com/smartcontractkit/chainlink-sui v0.0.0-20260429183453-39df0198aed8 h1:sWpTYRucOQQ/wXbKj52UE59JMMEq2Aq5g+sMdjYzfRM= -github.com/smartcontractkit/chainlink-sui v0.0.0-20260429183453-39df0198aed8/go.mod h1:k1HSbHyPaQWPOj6lXDIAe04EuwbC5ge1nK+cpG2E8hE= -github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.2 h1:p5zaHFFP1H05cpTl9evI4YBjSmfmljIxYbsZwmLiJxI= -github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.2/go.mod h1:wxgGfrJpzIdC1wyMJEGOfN4H4yPQTZD/DdrMRBxA0io= +github.com/smartcontractkit/chainlink-sui v0.0.0-20260527160341-aa3adc0abf67 h1:NNvPOgvf5vbOYVLxLST+5E88iPOAnpmzZGPihEx8DFc= +github.com/smartcontractkit/chainlink-sui v0.0.0-20260527160341-aa3adc0abf67/go.mod h1:k1HSbHyPaQWPOj6lXDIAe04EuwbC5ge1nK+cpG2E8hE= +github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.4 h1:8M+2pA0qx9rXaxmpKouUHj983vQCGzztHkG0XjE5Eew= +github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.4/go.mod h1:nyOjn4ADJGqRMe3+4ZXSV+J/7nWb1H2Vx8Qk57eLRYA= github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.5 h1:RwZXxdIAOyjp6cwc9Quxgr38k8r7ACz+Lxh9o/A6oH0= github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.5/go.mod h1:kHYJnZUqiPF7/xN5273prV+srrLJkS77GbBXHLKQpx0= github.com/smartcontractkit/chainlink-ton v1.0.5-0.20260514223130-48bc90aca745 h1:eieKLvYuzwBPh/FdbUS1gnIanI86zgWby1L10o90g4o= github.com/smartcontractkit/chainlink-ton v1.0.5-0.20260514223130-48bc90aca745/go.mod h1:8vXLeG//BxDF86GWRytzGIy6jc70htD1r/KtPfjrsK0= -github.com/smartcontractkit/chainlink-tron/relayer v0.0.11-0.20250908203554-5bd9d2fe9513 h1:XRNxgcNqagXu6e4smJuS1crRK5cUAcCVd7u+iLduHDM= -github.com/smartcontractkit/chainlink-tron/relayer v0.0.11-0.20250908203554-5bd9d2fe9513/go.mod h1:ccjEgNeqOO+bjPddnL4lUrNLzyCvGCxgBjJdhFX3wa8= +github.com/smartcontractkit/chainlink-tron/relayer v0.0.11-0.20251014143056-a0c6328c91e9 h1:7Ut0g+Pdm+gcu2J/Xv8OpQOVf7uLGErMX8yhC4b4tIA= +github.com/smartcontractkit/chainlink-tron/relayer v0.0.11-0.20251014143056-a0c6328c91e9/go.mod h1:h9hMs6K4hT1+mjYnJD3/SW1o7yC/sKjNi0Qh8hLfiCE= github.com/smartcontractkit/chainlink-tron/relayer/gotron-sdk v0.0.5-0.20250422175525-b7575d96bd4d h1:qLmSOOtB/Ogn79eIDkuujOu8M5Jd747V1H7Brk/nTvo= github.com/smartcontractkit/chainlink-tron/relayer/gotron-sdk v0.0.5-0.20250422175525-b7575d96bd4d/go.mod h1:4WhGgCA0smBbBud5mK+jnDb2wwndMvoqaWBJ3OV/7Bw= github.com/smartcontractkit/freeport v0.1.3-0.20250828155247-add56fa28aad h1:lgHxTHuzJIF3Vj6LSMOnjhqKgRqYW+0MV2SExtCYL1Q= github.com/smartcontractkit/freeport v0.1.3-0.20250828155247-add56fa28aad/go.mod h1:T4zH9R8R8lVWKfU7tUvYz2o2jMv1OpGCdpY2j2QZXzU= +github.com/smartcontractkit/go-daml v0.0.0-20260604143752-c6f6567940ba h1:peYJwUWOv54aigdk1VFzkmXdZmZK4xixfxv0Af1l6/I= +github.com/smartcontractkit/go-daml v0.0.0-20260604143752-c6f6567940ba/go.mod h1:SqWfl3Bp9NleC9jhzFUaOGzOZeKfldpY4QOW6A6NSNM= github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 h1:12ijqMM9tvYVEm+nR826WsrNi6zCKpwBhuApq127wHs= github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7/go.mod h1:FX7/bVdoep147QQhsOPkYsPEXhGZjeYx6lBSaSXtZOA= github.com/smartcontractkit/libocr v0.0.0-20260304194147-a03701e2c02e h1:poXTj5cFVM6XfC4HICIDYkDVc/A6OYB0eeID0wU2JQE= github.com/smartcontractkit/libocr v0.0.0-20260304194147-a03701e2c02e/go.mod h1:PLdNK6GlqfxIWXzziPkU7dCAVlVFeYkyyW7AQY0R+4Q= -github.com/smartcontractkit/mcms v0.45.0 h1:6Zx80KKLQOPXLhvrRkJKClANnBJmPa/r69CV5UUq/0I= -github.com/smartcontractkit/mcms v0.45.0/go.mod h1:/uOE69QmF7opKlaBNzp8djypmBoYSW0mk4V2iKWP418= +github.com/smartcontractkit/mcms v0.46.0 h1:IxXVZ6km/orQGNsXs/qs42s65CYnjxNMrnntWLUF9KA= +github.com/smartcontractkit/mcms v0.46.0/go.mod h1:PBWZPScZKC87jDMxcd5WvKDdlvTgA7k8qHkCeNkGBN8= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= @@ -769,8 +769,9 @@ github.com/testcontainers/testcontainers-go/modules/postgres v0.41.0 h1:AOtFXssr github.com/testcontainers/testcontainers-go/modules/postgres v0.41.0/go.mod h1:k2a09UKhgSp6vNpliIY0QSgm4Hi7GXVTzWvWgUemu/8= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM= +github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= @@ -818,12 +819,12 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= -go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= -go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= +go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0 h1:Dn8rkudDzY6KV9dr/D/bTUuWgqDf9xe0rr4G2elrn0Y= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0/go.mod h1:gMk9F0xDgyN9M/3Ed5Y1wKcx/9mlU91NXY2SNq7RQuU= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0 h1:HIBTQ3VO5aupLKjC90JgMqpezVXwFuq6Ryjn0/izoag= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0/go.mod h1:ji9vId85hMxqfvICA0Jt8JqEdrXaAkcpkI9HPXya0ro= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0 h1:owlhcJ3QO3X0YTDTCcDZ4V+6aVDkWbNmBoQ5NUp7Oww= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0/go.mod h1:MP4eemTiI9zC8fgg+DYynhYDYf3ba72S376TvP+Ye0Q= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 h1:8UQVDcZxOJLtX6gxtDt3vY2WTgvZqMQRzjsqiIHQdkc= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0/go.mod h1:2lmweYCiHYpEjQ/lSJBYhj9jP1zvCvQW4BqL9dnT7FQ= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 h1:w1K+pCJoPpQifuVpsKamUdn9U0zM3xUziVOqsGksUrY= @@ -840,23 +841,25 @@ go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0 h1:TC+BewnDpeiAmc go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0/go.mod h1:J/ZyF4vfPwsSr9xJSPyQ4LqtcTPULFR64KwTikGLe+A= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 h1:mS47AX77OtFfKG4vtp+84kuGSFZHTyxtXIN269vChY0= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0/go.mod h1:PJnsC41lAGncJlPUniSwM81gc80GkgWJWr3cu2nKEtU= -go.opentelemetry.io/otel/log v0.19.0 h1:KUZs/GOsw79TBBMfDWsXS+KZ4g2Ckzksd1ymzsIEbo4= -go.opentelemetry.io/otel/log v0.19.0/go.mod h1:5DQYeGmxVIr4n0/BcJvF4upsraHjg6vudJJpnkL6Ipk= +go.opentelemetry.io/otel/log v0.20.0 h1:/5i0vuHxCLWUfChWG41K9wkM0jafruPw9NU1/RCJirs= +go.opentelemetry.io/otel/log v0.20.0/go.mod h1:wOcMcjsZpG8x7Bak7IhSi/lg8wscV2C1VdrKCLPlt0E= go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= -go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= -go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= +go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= +go.opentelemetry.io/otel/metric/x v0.66.0 h1:YkCrx1zLOChi9ZcZ6euupOcsgzbVlec7D/xoEU1+cTA= +go.opentelemetry.io/otel/metric/x v0.66.0/go.mod h1:d1+BDj9t96do0/1LoU1ayfCv79ZgNE41qbhBvnMOBZk= go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= -go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= -go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= -go.opentelemetry.io/otel/sdk/log v0.19.0 h1:scYVLqT22D2gqXItnWiocLUKGH9yvkkeql5dBDiXyko= -go.opentelemetry.io/otel/sdk/log v0.19.0/go.mod h1:vFBowwXGLlW9AvpuF7bMgnNI95LiW10szrOdvzBHlAg= -go.opentelemetry.io/otel/sdk/log/logtest v0.19.0 h1:BEbF7ZBB6qQloV/Ub1+3NQoOUnVtcGkU3XX4Ws3GQfk= -go.opentelemetry.io/otel/sdk/log/logtest v0.19.0/go.mod h1:Lua81/3yM0wOmoHTokLj9y9ADeA02v1naRrVrkAZuKk= -go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= -go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= +go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0= +go.opentelemetry.io/otel/sdk/log v0.20.0 h1:vM3xI7TQgKPiSghe6urZtAkyFY7SodrSpC83CffDFuY= +go.opentelemetry.io/otel/sdk/log v0.20.0/go.mod h1:Knej2nmsTUzN79T2eeXdRsjjPcoxoq2pUyUHz9TFyyU= +go.opentelemetry.io/otel/sdk/log/logtest v0.20.0 h1:OqdRZ1guyzamK3M6LlRsmGqRrjkHWw6WZOKKli5ELpg= +go.opentelemetry.io/otel/sdk/log/logtest v0.20.0/go.mod h1:PuMIlm7zAt7c3z8zfOI5ox4iT1Z87We+PF6YoINux/M= +go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI= +go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= -go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= -go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= +go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -876,8 +879,6 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= -go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= -go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -891,8 +892,8 @@ golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= -golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0= golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= @@ -935,8 +936,8 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w= -golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= @@ -984,18 +985,16 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= -golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6 h1:HjU6IWBiAgRIdAJ9/y1rwCn+UELEmwV+VsTLzj/W4sE= golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6/go.mod h1:Eqhaxk/wZsWEH8CRxLwj6xzEJbz7k1EFGqx7nyCoabE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1052,10 +1051,10 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20210401141331-865547bb08e2/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA= -google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8= +google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/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= @@ -1109,21 +1108,24 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls= k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k= -k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U= -k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/apimachinery v0.33.2 h1:IHFVhqg59mb8PJWTLi8m1mAoepkUNYmptHsV+Z1m5jY= +k8s.io/apimachinery v0.33.2/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU= k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg= -k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas= -k8s.io/utils v0.0.0-20241104163129-6fe5fd82f078 h1:jGnCPejIetjiy2gqaJ5V0NLwTpF4wbQ6cZIItJCSHno= -k8s.io/utils v0.0.0-20241104163129-6fe5fd82f078/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= +k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= +k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= -sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA= -sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= +sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= From 70f524eb5757f6aef57332efaabc4bb197258978 Mon Sep 17 00:00:00 2001 From: stackman27 Date: Fri, 5 Jun 2026 13:04:54 -0700 Subject: [PATCH 11/15] go mod --- go.sum | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go.sum b/go.sum index 0c5e43fb1..61a412682 100644 --- a/go.sum +++ b/go.sum @@ -693,8 +693,8 @@ github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625 github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0 h1:hGEJFD2X3oNIPXQbtIPxCJyg5CcKglRCYBmESS+gmeQ= github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0/go.mod h1:PjZD54vr6rIKEKQj6HNA4hllvYI/QpT+Zefj3tqkFAs= -github.com/smartcontractkit/chainlink-sui v0.0.0-20260429183453-39df0198aed8 h1:sWpTYRucOQQ/wXbKj52UE59JMMEq2Aq5g+sMdjYzfRM= -github.com/smartcontractkit/chainlink-sui v0.0.0-20260429183453-39df0198aed8/go.mod h1:k1HSbHyPaQWPOj6lXDIAe04EuwbC5ge1nK+cpG2E8hE= +github.com/smartcontractkit/chainlink-sui v0.0.0-20260527160341-aa3adc0abf67 h1:NNvPOgvf5vbOYVLxLST+5E88iPOAnpmzZGPihEx8DFc= +github.com/smartcontractkit/chainlink-sui v0.0.0-20260527160341-aa3adc0abf67/go.mod h1:k1HSbHyPaQWPOj6lXDIAe04EuwbC5ge1nK+cpG2E8hE= github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.4 h1:8M+2pA0qx9rXaxmpKouUHj983vQCGzztHkG0XjE5Eew= github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.4/go.mod h1:nyOjn4ADJGqRMe3+4ZXSV+J/7nWb1H2Vx8Qk57eLRYA= github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.5 h1:RwZXxdIAOyjp6cwc9Quxgr38k8r7ACz+Lxh9o/A6oH0= From 1b99181e6ee1cc695092b8ff2174e54957d1c016 Mon Sep 17 00:00:00 2001 From: stackman27 Date: Fri, 5 Jun 2026 13:12:55 -0700 Subject: [PATCH 12/15] fix lint --- .../authorizationcode/authorizationcode.go | 13 ++++---- .../authorizationcode_test.go | 2 +- .../clientcredentials_test.go | 20 ++++++------- .../provider/authentication/metadata.go | 2 +- .../provider/authentication/metadata_test.go | 17 +++++------ engine/cld/config/env/config_test.go | 30 +++++++++---------- engine/cld/config/network/metadata.go | 2 +- 7 files changed, 43 insertions(+), 43 deletions(-) diff --git a/chain/canton/provider/authentication/authorizationcode/authorizationcode.go b/chain/canton/provider/authentication/authorizationcode/authorizationcode.go index 3136a050c..97f1cc6f9 100644 --- a/chain/canton/provider/authentication/authorizationcode/authorizationcode.go +++ b/chain/canton/provider/authentication/authorizationcode/authorizationcode.go @@ -5,6 +5,7 @@ package authorizationcode import ( "context" "crypto/tls" + "errors" "fmt" "net" "net/http" @@ -99,7 +100,7 @@ func NewDiscoveryProvider( } if !slices.Contains(metadata.CodeChallengeMethodsSupported, "S256") { - return nil, fmt.Errorf("authorization server does not support S256 PKCE challenges") + return nil, errors.New("authorization server does not support S256 PKCE challenges") } return NewProvider(ctx, metadata.AuthorizationEndpoint, metadata.TokenEndpoint, clientID, options...) @@ -117,13 +118,13 @@ func NewProvider( } if authURL == "" { - return nil, fmt.Errorf("authURL cannot be empty") + return nil, errors.New("authURL cannot be empty") } if tokenURL == "" { - return nil, fmt.Errorf("tokenURL cannot be empty") + return nil, errors.New("tokenURL cannot be empty") } if clientID == "" { - return nil, fmt.Errorf("clientID cannot be empty") + return nil, errors.New("clientID cannot be empty") } flowCtx := ctx @@ -166,8 +167,8 @@ func NewProvider( return } - token, err := oauthCfg.Exchange(flowCtx, code, oauth2.VerifierOption(verifier)) - if err != nil { + token, exchangeErr := oauthCfg.Exchange(flowCtx, code, oauth2.VerifierOption(verifier)) + if exchangeErr != nil { http.Error(w, "Token exchange failed", http.StatusInternalServerError) return } diff --git a/chain/canton/provider/authentication/authorizationcode/authorizationcode_test.go b/chain/canton/provider/authentication/authorizationcode/authorizationcode_test.go index 208760916..c2ff38a2f 100644 --- a/chain/canton/provider/authentication/authorizationcode/authorizationcode_test.go +++ b/chain/canton/provider/authentication/authorizationcode/authorizationcode_test.go @@ -203,7 +203,7 @@ func TestNewProvider_FlowCompletes(t *testing.T) { require.NotEmpty(t, state) callbackURL := "http://" + callbackHost + "/callback?code=code123&state=" + url.QueryEscape(state) - response, err := http.Get(callbackURL) //nolint:noctx + response, err := http.Get(callbackURL) //nolint:noctx,gosec // G107: test hits local callback server require.NoError(t, err) require.NoError(t, response.Body.Close()) diff --git a/chain/canton/provider/authentication/clientcredentials/clientcredentials_test.go b/chain/canton/provider/authentication/clientcredentials/clientcredentials_test.go index cb710b2ab..963630364 100644 --- a/chain/canton/provider/authentication/clientcredentials/clientcredentials_test.go +++ b/chain/canton/provider/authentication/clientcredentials/clientcredentials_test.go @@ -15,9 +15,9 @@ import ( ) type tokenResponse struct { - AccessToken string `json:"access_token"` - TokenType string `json:"token_type"` - ExpiresIn int `json:"expires_in"` + Token string `json:"access_token"` + TokenType string `json:"token_type"` + ExpiresIn int `json:"expires_in"` } func newTokenServer(t *testing.T, expectedScope string) *httptest.Server { @@ -38,9 +38,9 @@ func newTokenServer(t *testing.T, expectedScope string) *httptest.Server { } payload, err := json.Marshal(tokenResponse{ - AccessToken: "test-access-token", - TokenType: "Bearer", - ExpiresIn: 3600, + Token: "test-access-token", + TokenType: "Bearer", + ExpiresIn: 3600, }) require.NoError(t, err) @@ -79,7 +79,7 @@ func TestNewProvider_UsesOptionsAndTokenSource(t *testing.T) { server := newTokenServer(t, "scope-a scope-b") t.Cleanup(server.Close) - customCreds := credentials.NewTLS(&tls.Config{InsecureSkipVerify: true}) + customCreds := credentials.NewTLS(&tls.Config{InsecureSkipVerify: true}) //nolint:gosec // G402: intentional for transport-credentials test provider, err := NewProvider( ctx, @@ -128,9 +128,9 @@ func TestNewDiscoveryProvider_UsesMetadataTokenEndpoint(t *testing.T) { require.Equal(t, "daml_ledger_api", values.Get("scope")) payload, err := json.Marshal(tokenResponse{ - AccessToken: "test-access-token", - TokenType: "Bearer", - ExpiresIn: 3600, + Token: "test-access-token", + TokenType: "Bearer", + ExpiresIn: 3600, }) require.NoError(t, err) diff --git a/chain/canton/provider/authentication/metadata.go b/chain/canton/provider/authentication/metadata.go index ab3bf284e..cc30a7e56 100644 --- a/chain/canton/provider/authentication/metadata.go +++ b/chain/canton/provider/authentication/metadata.go @@ -30,7 +30,7 @@ func GetAuthorizationServerMetadata(ctx context.Context, authorizationServerURL request, err := http.NewRequestWithContext( ctx, http.MethodGet, - fmt.Sprintf("%s/.well-known/oauth-authorization-server", authorizationServerURL), + authorizationServerURL+"/.well-known/oauth-authorization-server", nil, ) if err != nil { diff --git a/chain/canton/provider/authentication/metadata_test.go b/chain/canton/provider/authentication/metadata_test.go index bad092ce1..33618fdff 100644 --- a/chain/canton/provider/authentication/metadata_test.go +++ b/chain/canton/provider/authentication/metadata_test.go @@ -6,6 +6,7 @@ import ( "net/http/httptest" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -14,15 +15,15 @@ func TestGetAuthorizationServerMetadata(t *testing.T) { ctx := t.Context() server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - require.Equal(t, "/.well-known/oauth-authorization-server", r.URL.Path) + assert.Equal(t, "/.well-known/oauth-authorization-server", r.URL.Path) payload, err := json.Marshal(AuthorizationServerMetadata{ - Issuer: serverURL(t, r), - AuthorizationEndpoint: serverURL(t, r) + "/authorize", - TokenEndpoint: serverURL(t, r) + "/token", + Issuer: serverURL(r), + AuthorizationEndpoint: serverURL(r) + "/authorize", + TokenEndpoint: serverURL(r) + "/token", CodeChallengeMethodsSupported: []string{"S256"}, }) - require.NoError(t, err) + assert.NoError(t, err) w.Header().Set("Content-Type", "application/json") _, _ = w.Write(payload) @@ -44,7 +45,7 @@ func TestGetAuthorizationServerMetadata_UnexpectedIssuer(t *testing.T) { payload, err := json.Marshal(AuthorizationServerMetadata{ Issuer: "https://other.example.com", }) - require.NoError(t, err) + assert.NoError(t, err) w.Header().Set("Content-Type", "application/json") _, _ = w.Write(payload) @@ -55,8 +56,6 @@ func TestGetAuthorizationServerMetadata_UnexpectedIssuer(t *testing.T) { require.Error(t, err) } -func serverURL(t *testing.T, r *http.Request) string { - t.Helper() - +func serverURL(r *http.Request) string { return "http://" + r.Host } diff --git a/engine/cld/config/env/config_test.go b/engine/cld/config/env/config_test.go index b2657e23c..dceededb2 100644 --- a/engine/cld/config/env/config_test.go +++ b/engine/cld/config/env/config_test.go @@ -81,7 +81,7 @@ var ( } // envVars is the environment variables that used to set the config. - envVars = map[string]string{ + envVars = map[string]string{ //nolint:gosec // G101: test fixture values, not real credentials "ONCHAIN_KMS_KEY_ID": "123", "ONCHAIN_KMS_KEY_REGION": "us-east-1", "ONCHAIN_EVM_DEPLOYER_KEY": "0x123", @@ -107,13 +107,13 @@ var ( "CATALOG_AUTH_KMS_KEY_ID": "123", "CATALOG_AUTH_KMS_KEY_REGION": "us-east-1", "ONCHAIN_CANTON_AUTH_STRATEGY": "client_credentials", - "ONCHAIN_CANTON_OKTA_AUTHORIZER": "https://canton-auth.example.com", - "ONCHAIN_CANTON_OKTA_CLIENT_ID": "canton-client-id", - "ONCHAIN_CANTON_OKTA_CLIENT_SECRET": "canton-client-secret", + "ONCHAIN_CANTON_OKTA_AUTHORIZER": "https://canton-auth.example.com", + "ONCHAIN_CANTON_OKTA_CLIENT_ID": "canton-client-id", + "ONCHAIN_CANTON_OKTA_CLIENT_SECRET": "canton-client-secret", "ONCHAIN_CANTON_JWT_TOKEN": "canton-jwt-token", } - legacyEnvVars = map[string]string{ + legacyEnvVars = map[string]string{ //nolint:gosec // G101: test fixture values, not real credentials "KMS_DEPLOYER_KEY_ID": "123", "KMS_DEPLOYER_KEY_REGION": "us-east-1", "TEST_WALLET_KEY": "0x123", @@ -135,15 +135,15 @@ var ( "TON_DEPLOYER_KEY": "0x123", "TON_WALLET_VERSION": "V5R1", // These values do not have a legacy equivalent - "ONCHAIN_STELLAR_DEPLOYER_KEY": "0x567", // Stellar is new, uses new-style env var - "ONCHAIN_CANTON_AUTH_STRATEGY": "client_credentials", - "ONCHAIN_CANTON_OKTA_AUTHORIZER": "https://canton-auth.example.com", - "ONCHAIN_CANTON_OKTA_CLIENT_ID": "canton-client-id", - "ONCHAIN_CANTON_OKTA_CLIENT_SECRET": "canton-client-secret", - "ONCHAIN_CANTON_JWT_TOKEN": "canton-jwt-token", - "CATALOG_GRPC": "http://localhost:8080", - "CATALOG_AUTH_KMS_KEY_ID": "123", - "CATALOG_AUTH_KMS_KEY_REGION": "us-east-1", + "ONCHAIN_STELLAR_DEPLOYER_KEY": "0x567", // Stellar is new, uses new-style env var + "ONCHAIN_CANTON_AUTH_STRATEGY": "client_credentials", + "ONCHAIN_CANTON_OKTA_AUTHORIZER": "https://canton-auth.example.com", + "ONCHAIN_CANTON_OKTA_CLIENT_ID": "canton-client-id", + "ONCHAIN_CANTON_OKTA_CLIENT_SECRET": "canton-client-secret", + "ONCHAIN_CANTON_JWT_TOKEN": "canton-jwt-token", + "CATALOG_GRPC": "http://localhost:8080", + "CATALOG_AUTH_KMS_KEY_ID": "123", + "CATALOG_AUTH_KMS_KEY_REGION": "us-east-1", } // envCfg is the config that is loaded from the environment variables. @@ -180,7 +180,7 @@ var ( DeployerKey: "0x123", WalletVersion: "V5R1", }, - Canton: CantonConfig{ + Canton: CantonConfig{ //nolint:gosec // G101: test fixture values, not real credentials AuthStrategy: "client_credentials", JWTToken: "canton-jwt-token", AuthURL: "https://canton-auth.example.com", diff --git a/engine/cld/config/network/metadata.go b/engine/cld/config/network/metadata.go index 75e2a7146..c3a407f9e 100644 --- a/engine/cld/config/network/metadata.go +++ b/engine/cld/config/network/metadata.go @@ -42,7 +42,7 @@ type CantonParticipantMetadata struct { // It contains the list of participants to connect to. type CantonMetadata struct { // InsecureTransport uses plaintext gRPC (local docker compose). When false, static JWT auth uses TLS. - InsecureTransport bool `yaml:"insecure_transport,omitempty"` + InsecureTransport bool `yaml:"insecure_transport,omitempty"` Participants []CantonParticipantMetadata `yaml:"participants"` } From c78e1d015b2a1b8907dddf8dda482f75c0a6c5cf Mon Sep 17 00:00:00 2001 From: stackman27 Date: Fri, 5 Jun 2026 13:23:16 -0700 Subject: [PATCH 13/15] more lint --- .../authorizationcode_test.go | 22 +++--- .../clientcredentials/clientcredentials.go | 7 +- .../clientcredentials_test.go | 67 +++++++++---------- 3 files changed, 49 insertions(+), 47 deletions(-) diff --git a/chain/canton/provider/authentication/authorizationcode/authorizationcode_test.go b/chain/canton/provider/authentication/authorizationcode/authorizationcode_test.go index c2ff38a2f..f721ca428 100644 --- a/chain/canton/provider/authentication/authorizationcode/authorizationcode_test.go +++ b/chain/canton/provider/authentication/authorizationcode/authorizationcode_test.go @@ -14,6 +14,7 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -91,17 +92,17 @@ func newTokenServer(t *testing.T) *httptest.Server { t.Helper() return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - require.NoError(t, r.ParseForm()) - require.Equal(t, "authorization_code", r.Form.Get("grant_type")) - require.NotEmpty(t, r.Form.Get("code")) - require.NotEmpty(t, r.Form.Get("code_verifier")) + assert.NoError(t, r.ParseForm()) + assert.Equal(t, "authorization_code", r.Form.Get("grant_type")) + assert.NotEmpty(t, r.Form.Get("code")) + assert.NotEmpty(t, r.Form.Get("code_verifier")) - payload, err := json.Marshal(map[string]any{ + payload, err := json.Marshal(map[string]any{ //nolint:gosec // G101: OAuth test fixture, not a real credential "access_token": "auth-code-token", "token_type": "Bearer", "expires_in": 3600, }) - require.NoError(t, err) + assert.NoError(t, err) w.Header().Set("Content-Type", "application/json") _, _ = w.Write(payload) @@ -118,8 +119,11 @@ func TestNewProvider_ValidatesInputs(t *testing.T) { tokenURL string clientID string }{ + //nolint:gosec // G101: test fixture {name: "missing auth url", authURL: "", tokenURL: "https://example.test/token", clientID: "client-id"}, + //nolint:gosec // G101: test fixture {name: "missing token url", authURL: "https://example.test/auth", tokenURL: "", clientID: "client-id"}, + //nolint:gosec // G101: test fixture {name: "missing client id", authURL: "https://example.test/auth", tokenURL: "https://example.test/token", clientID: ""}, } @@ -137,20 +141,20 @@ func TestNewDiscoveryProvider_RequiresS256(t *testing.T) { ctx := t.Context() server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - payload, err := json.Marshal(map[string]any{ + payload, err := json.Marshal(map[string]any{ //nolint:gosec // G101: OAuth metadata test fixture "issuer": "http://" + r.Host, "authorization_endpoint": "https://example.test/auth", "token_endpoint": "https://example.test/token", "code_challenge_methods_supported": []string{"plain"}, }) - require.NoError(t, err) + assert.NoError(t, err) w.Header().Set("Content-Type", "application/json") _, _ = w.Write(payload) })) t.Cleanup(server.Close) - _, err := NewDiscoveryProvider(ctx, server.URL, "client-id") + _, err := NewDiscoveryProvider(ctx, server.URL, "client-id") //nolint:gosec // G101: test fixture require.Error(t, err) } diff --git a/chain/canton/provider/authentication/clientcredentials/clientcredentials.go b/chain/canton/provider/authentication/clientcredentials/clientcredentials.go index a2901b1af..6c4f6dcd3 100644 --- a/chain/canton/provider/authentication/clientcredentials/clientcredentials.go +++ b/chain/canton/provider/authentication/clientcredentials/clientcredentials.go @@ -5,6 +5,7 @@ package clientcredentials import ( "context" "crypto/tls" + "errors" "fmt" "golang.org/x/oauth2" @@ -80,13 +81,13 @@ func NewProvider( } if tokenURL == "" { - return nil, fmt.Errorf("tokenURL cannot be empty") + return nil, errors.New("tokenURL cannot be empty") } if clientID == "" { - return nil, fmt.Errorf("clientID cannot be empty") + return nil, errors.New("clientID cannot be empty") } if clientSecret == "" { - return nil, fmt.Errorf("clientSecret cannot be empty") + return nil, errors.New("clientSecret cannot be empty") } oauthCfg := &clientcredentials.Config{ diff --git a/chain/canton/provider/authentication/clientcredentials/clientcredentials_test.go b/chain/canton/provider/authentication/clientcredentials/clientcredentials_test.go index 963630364..9ef158c7c 100644 --- a/chain/canton/provider/authentication/clientcredentials/clientcredentials_test.go +++ b/chain/canton/provider/authentication/clientcredentials/clientcredentials_test.go @@ -10,39 +10,40 @@ import ( "strings" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc/credentials" ) -type tokenResponse struct { - Token string `json:"access_token"` - TokenType string `json:"token_type"` - ExpiresIn int `json:"expires_in"` +const testBearerToken = "test-access-token" + +func tokenResponsePayload() map[string]any { //nolint:gosec // G101: OAuth test fixture, not a real credential + return map[string]any{ + "access_token": testBearerToken, + "token_type": "Bearer", + "expires_in": 3600, + } } func newTokenServer(t *testing.T, expectedScope string) *httptest.Server { t.Helper() return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - require.Equal(t, http.MethodPost, r.Method) + assert.Equal(t, http.MethodPost, r.Method) body, err := io.ReadAll(r.Body) - require.NoError(t, err) + assert.NoError(t, err) values, err := url.ParseQuery(string(body)) - require.NoError(t, err) - require.Equal(t, "client_credentials", values.Get("grant_type")) + assert.NoError(t, err) + assert.Equal(t, "client_credentials", values.Get("grant_type")) if expectedScope != "" { - require.Equal(t, expectedScope, values.Get("scope")) + assert.Equal(t, expectedScope, values.Get("scope")) } - payload, err := json.Marshal(tokenResponse{ - Token: "test-access-token", - TokenType: "Bearer", - ExpiresIn: 3600, - }) - require.NoError(t, err) + payload, err := json.Marshal(tokenResponsePayload()) + assert.NoError(t, err) w.Header().Set("Content-Type", "application/json") _, _ = w.Write(payload) @@ -59,9 +60,9 @@ func TestNewProvider_ValidatesInputs(t *testing.T) { clientID string clientSecret string }{ - {name: "missing token url", tokenURL: "", clientID: "client-id", clientSecret: "client-secret"}, - {name: "missing client id", tokenURL: "https://example.test/token", clientID: "", clientSecret: "client-secret"}, - {name: "missing client secret", tokenURL: "https://example.test/token", clientID: "client-id", clientSecret: ""}, + {name: "missing token url", tokenURL: "", clientID: "client-id", clientSecret: "client-secret"}, //nolint:gosec // G101: test fixture + {name: "missing client id", tokenURL: "https://example.test/token", clientID: "", clientSecret: "client-secret"}, //nolint:gosec // G101: test fixture + {name: "missing client secret", tokenURL: "https://example.test/token", clientID: "client-id", clientSecret: ""}, //nolint:gosec // G101: test fixture } for _, test := range tests { @@ -85,7 +86,7 @@ func TestNewProvider_UsesOptionsAndTokenSource(t *testing.T) { ctx, server.URL, "client-id", - "client-secret", + "client-secret", //nolint:gosec // G101: test fixture WithScopes("scope-a", "scope-b"), WithTransportCredentials(customCreds), ) @@ -94,7 +95,7 @@ func TestNewProvider_UsesOptionsAndTokenSource(t *testing.T) { token, err := provider.TokenSource().Token() require.NoError(t, err) - require.Equal(t, "test-access-token", token.AccessToken) + require.Equal(t, testBearerToken, token.AccessToken) } func TestNewDiscoveryProvider_UsesMetadataTokenEndpoint(t *testing.T) { @@ -113,37 +114,33 @@ func TestNewDiscoveryProvider_UsesMetadataTokenEndpoint(t *testing.T) { "issuer": server.URL, "token_endpoint": server.URL + tokenPath, }) - require.NoError(t, err) + assert.NoError(t, err) w.Header().Set("Content-Type", "application/json") _, _ = w.Write(payload) }) mux.HandleFunc(tokenPath, func(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(r.Body) - require.NoError(t, err) + assert.NoError(t, err) values, err := url.ParseQuery(string(body)) - require.NoError(t, err) - require.Equal(t, "client_credentials", values.Get("grant_type")) - require.Equal(t, "daml_ledger_api", values.Get("scope")) - - payload, err := json.Marshal(tokenResponse{ - Token: "test-access-token", - TokenType: "Bearer", - ExpiresIn: 3600, - }) - require.NoError(t, err) + assert.NoError(t, err) + assert.Equal(t, "client_credentials", values.Get("grant_type")) + assert.Equal(t, "daml_ledger_api", values.Get("scope")) + + payload, err := json.Marshal(tokenResponsePayload()) + assert.NoError(t, err) w.Header().Set("Content-Type", "application/json") _, _ = w.Write(payload) }) - provider, err := NewDiscoveryProvider(ctx, server.URL, "client-id", "client-secret") + provider, err := NewDiscoveryProvider(ctx, server.URL, "client-id", "client-secret") //nolint:gosec // G101: test fixture require.NoError(t, err) token, err := provider.TokenSource().Token() require.NoError(t, err) - require.Equal(t, "test-access-token", token.AccessToken) + require.Equal(t, testBearerToken, token.AccessToken) } func TestNewDiscoveryProvider_RequiresMetadataEndpoint(t *testing.T) { @@ -159,6 +156,6 @@ func TestNewDiscoveryProvider_RequiresMetadataEndpoint(t *testing.T) { })) t.Cleanup(server.Close) - _, err := NewDiscoveryProvider(ctx, server.URL, "client-id", "client-secret") + _, err := NewDiscoveryProvider(ctx, server.URL, "client-id", "client-secret") //nolint:gosec // G101: test fixture require.Error(t, err) } From 1c91b2075cd85ecaeb5b943352de205d2b5a0292 Mon Sep 17 00:00:00 2001 From: stackman27 Date: Sat, 6 Jun 2026 23:14:14 -0700 Subject: [PATCH 14/15] pablos feedback --- .../authorizationcode/authorizationcode.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/chain/canton/provider/authentication/authorizationcode/authorizationcode.go b/chain/canton/provider/authentication/authorizationcode/authorizationcode.go index 97f1cc6f9..ebae80037 100644 --- a/chain/canton/provider/authentication/authorizationcode/authorizationcode.go +++ b/chain/canton/provider/authentication/authorizationcode/authorizationcode.go @@ -10,9 +10,11 @@ import ( "net" "net/http" "net/url" + "os" "os/exec" "runtime" "slices" + "sync" "time" "golang.org/x/oauth2" @@ -151,6 +153,7 @@ func NewProvider( authCodeURL := oauthCfg.AuthCodeURL(state, oauth2.S256ChallengeOption(verifier)) callbackChan := make(chan *oauth2.Token, 1) + var deliverOnce sync.Once serveMux := http.NewServeMux() serveMux.HandleFunc(callbackURL.Path, func(w http.ResponseWriter, r *http.Request) { @@ -169,14 +172,14 @@ func NewProvider( token, exchangeErr := oauthCfg.Exchange(flowCtx, code, oauth2.VerifierOption(verifier)) if exchangeErr != nil { - http.Error(w, "Token exchange failed", http.StatusInternalServerError) + fmt.Fprintf(os.Stderr, "authorization code token exchange failed: %v\n", exchangeErr) + http.Error(w, fmt.Sprintf("Token exchange failed: %v", exchangeErr), http.StatusInternalServerError) return } - select { - case callbackChan <- token: - default: - } + deliverOnce.Do(func() { + callbackChan <- token + }) html := ` From 785d727c85e4caeb42bbb111d6a1c6e49e278d6e Mon Sep 17 00:00:00 2001 From: stackman27 Date: Sun, 7 Jun 2026 17:16:28 -0700 Subject: [PATCH 15/15] fix lint --- .../authorizationcode/authorizationcode.go | 4 +++- engine/cld/chains/chains.go | 9 ++++++--- engine/cld/chains/chains_test.go | 17 +++++++++++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/chain/canton/provider/authentication/authorizationcode/authorizationcode.go b/chain/canton/provider/authentication/authorizationcode/authorizationcode.go index ebae80037..66f7302d8 100644 --- a/chain/canton/provider/authentication/authorizationcode/authorizationcode.go +++ b/chain/canton/provider/authentication/authorizationcode/authorizationcode.go @@ -174,6 +174,7 @@ func NewProvider( if exchangeErr != nil { fmt.Fprintf(os.Stderr, "authorization code token exchange failed: %v\n", exchangeErr) http.Error(w, fmt.Sprintf("Token exchange failed: %v", exchangeErr), http.StatusInternalServerError) + return } @@ -235,7 +236,8 @@ func NewProvider( return nil, fmt.Errorf("callback server error: %w", err) case token := <-callbackChan: shutdown() - tokenSource := oauthCfg.TokenSource(flowCtx, token) + refreshCtx := context.WithoutCancel(ctx) + tokenSource := oauthCfg.TokenSource(refreshCtx, token) return &Provider{ tokenSource: oauth.TokenSource{TokenSource: tokenSource}, diff --git a/engine/cld/chains/chains.go b/engine/cld/chains/chains.go index 10cbcfae3..99055e816 100644 --- a/engine/cld/chains/chains.go +++ b/engine/cld/chains/chains.go @@ -793,9 +793,10 @@ func cantonAuthConfigured(c cfgenv.CantonConfig) bool { return c.AuthURL != "" && c.ClientID != "" && c.ClientSecret != "" case cfgenv.CantonAuthStrategyAuthorizationCode: return c.AuthURL != "" && c.ClientID != "" - default: - // static or empty (backward compat: jwt_token alone enables Canton) + case "", cfgenv.CantonAuthStrategyStatic: return c.JWTToken != "" + default: + return false } } @@ -817,7 +818,7 @@ func (l *chainLoaderCanton) cantonAuthProvider(ctx context.Context, selector uin } return provider, nil - default: + case "", cfgenv.CantonAuthStrategyStatic: if c.JWTToken == "" { return nil, fmt.Errorf("canton network %d: JWT token is required for static auth", selector) } @@ -826,6 +827,8 @@ func (l *chainLoaderCanton) cantonAuthProvider(ctx context.Context, selector uin } return cantonauth.NewStaticProvider(c.JWTToken), nil + default: + return nil, fmt.Errorf("canton network %d: unknown auth strategy: %q", selector, c.AuthStrategy) } } diff --git a/engine/cld/chains/chains_test.go b/engine/cld/chains/chains_test.go index f75ed3eaa..47cfab69f 100644 --- a/engine/cld/chains/chains_test.go +++ b/engine/cld/chains/chains_test.go @@ -1419,6 +1419,16 @@ func Test_cantonAuthConfigured(t *testing.T) { config: cfgenv.CantonConfig{}, want: false, }, + { + name: "unknown strategy with jwt", + config: cfgenv.CantonConfig{AuthStrategy: "typo", JWTToken: "token"}, + want: false, + }, + { + name: "explicit static strategy", + config: cfgenv.CantonConfig{AuthStrategy: cfgenv.CantonAuthStrategyStatic, JWTToken: "token"}, + want: true, + }, } for _, tt := range tests { @@ -1451,6 +1461,13 @@ func Test_chainLoaderCanton_cantonAuthProvider(t *testing.T) { _, err = loaderMissingJWT.cantonAuthProvider(ctx, chainsel.CANTON_LOCALNET.Selector, false) require.Error(t, err) require.ErrorContains(t, err, "JWT token is required") + + loaderUnknownStrategy := newChainLoaderCanton(nil, cfgenv.OnchainConfig{ + Canton: cfgenv.CantonConfig{AuthStrategy: "typo", JWTToken: "static-token"}, + }) + _, err = loaderUnknownStrategy.cantonAuthProvider(ctx, chainsel.CANTON_LOCALNET.Selector, false) + require.Error(t, err) + require.ErrorContains(t, err, "unknown auth strategy") } // newFakeRPCServer returns a fake RPC server which always answers with a valid `eth_blockNumberā€œ