diff --git a/cmd/secrets/common/browser_flow.go b/cmd/secrets/common/browser_flow.go index 8fe264af..fbb00e24 100644 --- a/cmd/secrets/common/browser_flow.go +++ b/cmd/secrets/common/browser_flow.go @@ -224,7 +224,7 @@ func (h *Handler) ExecuteBrowserVaultAuthorization(ctx context.Context, method s }) var exchangeResp struct { ExchangeAuthCodeToToken struct { - AccessToken string `json:"accessToken"` + AccessToken string `json:"accessToken"` // #nosec G117 -- OAuth token exchange response field ExpiresIn int `json:"expiresIn"` } `json:"exchangeAuthCodeToToken"` } diff --git a/cmd/workflow/hash/hash.go b/cmd/workflow/hash/hash.go index 7efdb434..eb7aed4c 100644 --- a/cmd/workflow/hash/hash.go +++ b/cmd/workflow/hash/hash.go @@ -24,7 +24,7 @@ type Inputs struct { WorkflowName string WorkflowPath string OwnerFromSettings string - PrivateKey string + PrivateKey string // #nosec G117 -- workflow owner private key flag value, not persisted SkipTypeChecks bool RegistryType settings.RegistryType DerivedOwner string diff --git a/internal/tenantctx/tenantctx.go b/internal/tenantctx/tenantctx.go index ec3f2064..cc810dce 100644 --- a/internal/tenantctx/tenantctx.go +++ b/internal/tenantctx/tenantctx.go @@ -38,13 +38,20 @@ type Forwarder struct { Address string `yaml:"address" json:"address"` } +// OnChainContract is a chain selector and contract address pair. +type OnChainContract struct { + ChainSelector uint64 `yaml:"chain_selector" json:"chainSelector"` + Address string `yaml:"address" json:"address"` +} + // EnvironmentContext holds user context for a single CLI environment. type EnvironmentContext struct { - TenantID string `yaml:"tenant_id"` - DefaultDonFamily string `yaml:"default_don_family"` - VaultGatewayURL string `yaml:"vault_gateway_url"` - Registries []*Registry `yaml:"registries"` - Forwarders []Forwarder `yaml:"forwarders,omitempty"` + TenantID string `yaml:"tenant_id"` + DefaultDonFamily string `yaml:"default_don_family"` + VaultGatewayURL string `yaml:"vault_gateway_url"` + CapabilitiesRegistry *OnChainContract `yaml:"capabilities_registry,omitempty"` + Registries []*Registry `yaml:"registries"` + Forwarders []Forwarder `yaml:"forwarders,omitempty"` } type gqlForwarder struct { @@ -52,12 +59,18 @@ type gqlForwarder struct { Address string `json:"address"` } +type gqlOnChainContract struct { + ChainSelector json.RawMessage `json:"chainSelector"` + Address string `json:"address"` +} + type getTenantConfigResponse struct { GetTenantConfig struct { - TenantID string `json:"tenantId"` - DefaultDonFamily string `json:"defaultDonFamily"` - VaultGatewayURL string `json:"vaultGatewayUrl"` - Registries []struct { + TenantID string `json:"tenantId"` + DefaultDonFamily string `json:"defaultDonFamily"` + VaultGatewayURL string `json:"vaultGatewayUrl"` + CapabilitiesRegistry gqlOnChainContract `json:"capabilitiesRegistry"` + Registries []struct { ID string `json:"id"` Label string `json:"label"` Type string `json:"type"` @@ -74,6 +87,10 @@ const getTenantConfigQuery = `query GetTenantConfig { tenantId defaultDonFamily vaultGatewayUrl + capabilitiesRegistry { + chainSelector + address + } registries { id label @@ -142,12 +159,25 @@ func FetchAndWriteContext(ctx context.Context, gqlClient *graphqlclient.Client, forwarders = append(forwarders, Forwarder{ChainSelector: sel, Address: addr}) } + capRegSel, err := parseChainSelectorJSON(tc.CapabilitiesRegistry.ChainSelector) + if err != nil { + return fmt.Errorf("invalid capabilitiesRegistry chainSelector: %w", err) + } + capRegAddr := strings.TrimSpace(tc.CapabilitiesRegistry.Address) + if capRegAddr == "" { + return fmt.Errorf("capabilitiesRegistry address is empty") + } + envCtx := &EnvironmentContext{ TenantID: tc.TenantID, DefaultDonFamily: tc.DefaultDonFamily, VaultGatewayURL: tc.VaultGatewayURL, - Registries: registries, - Forwarders: forwarders, + CapabilitiesRegistry: &OnChainContract{ + ChainSelector: capRegSel, + Address: capRegAddr, + }, + Registries: registries, + Forwarders: forwarders, } contextMap := map[string]*EnvironmentContext{ diff --git a/internal/tenantctx/tenantctx_test.go b/internal/tenantctx/tenantctx_test.go index 683c2f20..62f11480 100644 --- a/internal/tenantctx/tenantctx_test.go +++ b/internal/tenantctx/tenantctx_test.go @@ -43,6 +43,10 @@ func gqlResponseOnChainAndPrivate() map[string]any { "tenantId": "42", "defaultDonFamily": "zone-a", "vaultGatewayUrl": "https://gateway.example.com/", + "capabilitiesRegistry": map[string]any{ + "chainSelector": "16015286601757825753", + "address": "0x7f3191EaF73429177bAB3bAc5c36Ed2D5E39985f", + }, "registries": []any{ map[string]any{ "id": "ethereum-testnet-sepolia", @@ -77,6 +81,10 @@ func gqlResponsePrivateOnly() map[string]any { "tenantId": "99", "defaultDonFamily": "zone-b", "vaultGatewayUrl": "https://gateway-private.example.com/", + "capabilitiesRegistry": map[string]any{ + "chainSelector": "5009297550715157269", + "address": "0x76c9cf548b4179F8901cda1f8623568b58215E62", + }, "registries": []any{ map[string]any{ "id": "private", @@ -177,6 +185,16 @@ func TestFetchAndWriteContext_OnChainAndPrivate(t *testing.T) { if f.Address != "0x15fC6ae953E024d975e77382eEeC56A9101f9F88" { t.Errorf("forwarder address = %q, want Sepolia mock forwarder", f.Address) } + + if envCtx.CapabilitiesRegistry == nil { + t.Fatal("expected capabilitiesRegistry to be populated") + } + if envCtx.CapabilitiesRegistry.ChainSelector != 16015286601757825753 { + t.Errorf("capabilitiesRegistry chain selector = %d, want %d", envCtx.CapabilitiesRegistry.ChainSelector, uint64(16015286601757825753)) + } + if envCtx.CapabilitiesRegistry.Address != "0x7f3191EaF73429177bAB3bAc5c36Ed2D5E39985f" { + t.Errorf("capabilitiesRegistry address = %q, want staging mainline cap reg", envCtx.CapabilitiesRegistry.Address) + } } func TestFetchAndWriteContext_PrivateOnly(t *testing.T) { @@ -205,6 +223,12 @@ func TestFetchAndWriteContext_PrivateOnly(t *testing.T) { if len(envCtx.Forwarders) != 0 { t.Errorf("expected 0 forwarders, got %d", len(envCtx.Forwarders)) } + if envCtx.CapabilitiesRegistry == nil { + t.Fatal("expected capabilitiesRegistry to be populated") + } + if envCtx.CapabilitiesRegistry.ChainSelector != 5009297550715157269 { + t.Errorf("capabilitiesRegistry chain selector = %d, want %d", envCtx.CapabilitiesRegistry.ChainSelector, uint64(5009297550715157269)) + } } func TestParseChainSelectorJSON(t *testing.T) { diff --git a/test/multi_command_flows/workflow_private_registry.go b/test/multi_command_flows/workflow_private_registry.go index 92720350..b2004cb9 100644 --- a/test/multi_command_flows/workflow_private_registry.go +++ b/test/multi_command_flows/workflow_private_registry.go @@ -110,6 +110,10 @@ func workflowDeployPrivateRegistry(t *testing.T, tc TestConfig) string { "tenantId": "42", "defaultDonFamily": "test-don", "vaultGatewayUrl": "https://vault.example.test", + "capabilitiesRegistry": map[string]any{ + "chainSelector": "6433500567565415381", + "address": "0x5FbDB2315678afecb367f032d93F642f64180aa3", + }, "registries": []map[string]any{ { "id": "reg-test", @@ -325,6 +329,10 @@ func workflowPausePrivateRegistry(t *testing.T, tc TestConfig) string { "tenantId": "42", "defaultDonFamily": "test-don", "vaultGatewayUrl": "https://vault.example.test", + "capabilitiesRegistry": map[string]any{ + "chainSelector": "6433500567565415381", + "address": "0x5FbDB2315678afecb367f032d93F642f64180aa3", + }, "registries": []map[string]any{ { "id": "reg-test", @@ -495,6 +503,10 @@ func workflowActivatePrivateRegistry(t *testing.T, tc TestConfig) string { "tenantId": "42", "defaultDonFamily": "test-don", "vaultGatewayUrl": "https://vault.example.test", + "capabilitiesRegistry": map[string]any{ + "chainSelector": "6433500567565415381", + "address": "0x5FbDB2315678afecb367f032d93F642f64180aa3", + }, "registries": []map[string]any{ { "id": "reg-test", @@ -665,6 +677,10 @@ func workflowDeletePrivateRegistry(t *testing.T, tc TestConfig) string { "tenantId": "42", "defaultDonFamily": "test-don", "vaultGatewayUrl": "https://vault.example.test", + "capabilitiesRegistry": map[string]any{ + "chainSelector": "6433500567565415381", + "address": "0x5FbDB2315678afecb367f032d93F642f64180aa3", + }, "registries": []map[string]any{ { "id": "reg-test",