diff --git a/.changeset/canton-chain-support.md b/.changeset/canton-chain-support.md new file mode 100644 index 000000000..e3b9a7454 --- /dev/null +++ b/.changeset/canton-chain-support.md @@ -0,0 +1,5 @@ +--- +"chainlink-deployments-framework": minor +--- + +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..66f7302d8 --- /dev/null +++ b/chain/canton/provider/authentication/authorizationcode/authorizationcode.go @@ -0,0 +1,273 @@ +// 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" + "errors" + "fmt" + "net" + "net/http" + "net/url" + "os" + "os/exec" + "runtime" + "slices" + "sync" + "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, errors.New("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, errors.New("authURL cannot be empty") + } + if tokenURL == "" { + return nil, errors.New("tokenURL cannot be empty") + } + if clientID == "" { + return nil, errors.New("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) + var deliverOnce sync.Once + + 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, exchangeErr := oauthCfg.Exchange(flowCtx, code, oauth2.VerifierOption(verifier)) + 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 + } + + deliverOnce.Do(func() { + 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: 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() + refreshCtx := context.WithoutCancel(ctx) + tokenSource := oauthCfg.TokenSource(refreshCtx, 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..f721ca428 --- /dev/null +++ b/chain/canton/provider/authentication/authorizationcode/authorizationcode_test.go @@ -0,0 +1,221 @@ +package authorizationcode + +import ( + "bytes" + "encoding/json" + "io" + "net" + "net/http" + "net/http/httptest" + "net/url" + "os" + "strings" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "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) { + 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{ //nolint:gosec // G101: OAuth test fixture, not a real credential + "access_token": "auth-code-token", + "token_type": "Bearer", + "expires_in": 3600, + }) + assert.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 + }{ + //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: ""}, + } + + 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{ //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"}, + }) + 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") //nolint:gosec // G101: test fixture + 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,gosec // G107: test hits local callback server + 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..6c4f6dcd3 --- /dev/null +++ b/chain/canton/provider/authentication/clientcredentials/clientcredentials.go @@ -0,0 +1,119 @@ +// 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" + "errors" + "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, errors.New("tokenURL cannot be empty") + } + if clientID == "" { + return nil, errors.New("clientID cannot be empty") + } + if clientSecret == "" { + return nil, errors.New("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..9ef158c7c --- /dev/null +++ b/chain/canton/provider/authentication/clientcredentials/clientcredentials_test.go @@ -0,0 +1,161 @@ +package clientcredentials + +import ( + "crypto/tls" + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "net/url" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/credentials" +) + +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) { + assert.Equal(t, http.MethodPost, r.Method) + + body, err := io.ReadAll(r.Body) + assert.NoError(t, err) + + values, err := url.ParseQuery(string(body)) + assert.NoError(t, err) + assert.Equal(t, "client_credentials", values.Get("grant_type")) + + if expectedScope != "" { + assert.Equal(t, expectedScope, values.Get("scope")) + } + + payload, err := json.Marshal(tokenResponsePayload()) + assert.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"}, //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 { + 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}) //nolint:gosec // G402: intentional for transport-credentials test + + provider, err := NewProvider( + ctx, + server.URL, + "client-id", + "client-secret", //nolint:gosec // G101: test fixture + 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, testBearerToken, 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, + }) + 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) + assert.NoError(t, err) + + values, err := url.ParseQuery(string(body)) + 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") //nolint:gosec // G101: test fixture + require.NoError(t, err) + + token, err := provider.TokenSource().Token() + require.NoError(t, err) + require.Equal(t, testBearerToken, 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") //nolint:gosec // G101: test fixture + 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..cc30a7e56 --- /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, + authorizationServerURL+"/.well-known/oauth-authorization-server", + 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..33618fdff --- /dev/null +++ b/chain/canton/provider/authentication/metadata_test.go @@ -0,0 +1,61 @@ +package authentication + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + "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) { + assert.Equal(t, "/.well-known/oauth-authorization-server", r.URL.Path) + + payload, err := json.Marshal(AuthorizationServerMetadata{ + Issuer: serverURL(r), + AuthorizationEndpoint: serverURL(r) + "/authorize", + TokenEndpoint: serverURL(r) + "/token", + CodeChallengeMethodsSupported: []string{"S256"}, + }) + assert.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", + }) + assert.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(r *http.Request) string { + return "http://" + r.Host +} 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 01f24900c..99055e816 100644 --- a/engine/cld/chains/chains.go +++ b/engine/cld/chains/chains.go @@ -17,6 +17,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" @@ -219,10 +221,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_strategy and jwt_token, or auth_url+client_id for OAuth)") } return loaders @@ -733,7 +735,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 { @@ -750,13 +753,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, md.InsecureTransport) + 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{ @@ -766,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, } } @@ -784,6 +786,52 @@ 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 strategy. +func cantonAuthConfigured(c cfgenv.CantonConfig) bool { + switch c.AuthStrategy { + case cfgenv.CantonAuthStrategyClientCredentials: + return c.AuthURL != "" && c.ClientID != "" && c.ClientSecret != "" + case cfgenv.CantonAuthStrategyAuthorizationCode: + return c.AuthURL != "" && c.ClientID != "" + case "", cfgenv.CantonAuthStrategyStatic: + return c.JWTToken != "" + default: + return false + } +} + +// cantonAuthProvider builds a Canton auth Provider from config. +func (l *chainLoaderCanton) cantonAuthProvider(ctx context.Context, selector uint64, insecureTransport bool) (cantonauth.Provider, error) { + c := l.cfg.Canton + 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 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 provider, nil + case "", cfgenv.CantonAuthStrategyStatic: + 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 + default: + return nil, fmt.Errorf("canton network %d: unknown auth strategy: %q", selector, c.AuthStrategy) + } +} + // 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/chains/chains_test.go b/engine/cld/chains/chains_test.go index b45f0b1c1..47cfab69f 100644 --- a/engine/cld/chains/chains_test.go +++ b/engine/cld/chains/chains_test.go @@ -1381,6 +1381,95 @@ 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, + }, + { + 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 { + 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, true) + 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, 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ā€œ // 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 3e4817170..046b490b5 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. } +// CantonAuthStrategy is the authentication strategy for Canton participant APIs. +const ( + 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. // // 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. + // 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 OAuth2 authorization server URL. + AuthURL string `mapstructure:"auth_url" yaml:"auth_url"` + // ClientID is the OAuth2 client ID. + ClientID string `mapstructure:"client_id" yaml:"client_id"` // Secret + // ClientSecret is the OAuth2 client secret. Required for client_credentials (CI). + ClientSecret string `mapstructure:"client_secret" yaml:"client_secret"` // Secret } // JobDistributorConfig is the configuration for connecting and authenticating to the Job @@ -267,7 +281,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_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"}, "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 4ce2a12de..d94135ead 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: "", + AuthStrategy: "", + JWTToken: "", + AuthURL: "", + ClientID: "", + ClientSecret: "", }, }, Offchain: OffchainConfig{ @@ -77,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", @@ -102,9 +106,14 @@ 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{ + 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", @@ -127,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", @@ -166,8 +180,12 @@ var ( DeployerKey: "0x123", WalletVersion: "V5R1", }, - Canton: CantonConfig{ - JWTToken: "", + Canton: CantonConfig{ //nolint:gosec // G101: test fixture values, not real credentials + 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 6e4da91ba..5e61f5033 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_strategy: "" 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..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,11 @@ onchain: stellar: deployer_key: "0x567" canton: + auth_strategy: "" jwt_token: "" + auth_url: "" + client_id: "" + client_secret: "" offchain: job_distributor: endpoints: diff --git a/engine/cld/config/network/metadata.go b/engine/cld/config/network/metadata.go index 68479a1e0..c3a407f9e 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. diff --git a/go.mod b/go.mod index 839306b89..f4f135873 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.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 @@ -63,61 +63,64 @@ 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 + 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 ) @@ -125,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 @@ -175,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 @@ -264,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 @@ -279,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 @@ -292,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 diff --git a/go.sum b/go.sum index ba87a07e4..61a412682 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= @@ -11,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= @@ -109,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= @@ -166,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= @@ -211,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= @@ -232,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= @@ -310,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= @@ -319,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= @@ -374,6 +374,8 @@ 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= @@ -459,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= @@ -476,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= @@ -494,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= @@ -526,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= @@ -623,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= @@ -637,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= @@ -669,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= @@ -693,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-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= @@ -767,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= @@ -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= @@ -984,14 +985,12 @@ 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.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= @@ -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=