Skip to content

Commit 54fbf1c

Browse files
committed
[CRE] [4/5] ConfidentialModule, config, DB migration, syncer routing
Core abstractions for confidential workflow execution: - ConfidentialModule: implements host.ModuleV2, dispatches workflow execution to TEE enclave via the confidential-workflows capability - Syncer routing: detects confidential workflows via on-chain attributes, routes to ConfidentialModule instead of local WASM engine - Config: CRE.ConfidentialRelay TOML config for relay DON nodes - DB migration: adds attributes column to workflow_specs_v2 - WorkflowSpec.Attributes: persists on-chain workflow attributes Nothing is wired into CRE yet. Inert until PR 5/5. Part of #21635
1 parent d57a4b0 commit 54fbf1c

27 files changed

Lines changed: 1043 additions & 68 deletions

File tree

core/config/cre_config.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ type CRE interface {
1313
// When enabled, additional OTel tracing and logging is performed.
1414
DebugMode() bool
1515
LocalSecrets() map[string]string
16+
ConfidentialRelay() CREConfidentialRelay
1617
}
1718

1819
// WorkflowFetcher defines configuration for fetching workflow files
@@ -21,6 +22,13 @@ type WorkflowFetcher interface {
2122
URL() string
2223
}
2324

25+
// CREConfidentialRelay defines configuration for the confidential relay handler.
26+
type CREConfidentialRelay interface {
27+
Enabled() bool
28+
TrustedPCRs() string
29+
CARootsPEM() string
30+
}
31+
2432
// CRELinking defines configuration for connecting to the CRE linking service
2533
type CRELinking interface {
2634
URL() string

core/config/toml/types.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1895,14 +1895,24 @@ type CreConfig struct {
18951895
// Requires [Tracing].Enabled = true for traces to be exported (trace export is gated by
18961896
// Tracing.Enabled in initGlobals; Telemetry.Enabled is optional—traces work with or without it).
18971897
// WARNING: This is not suitable for production use due to performance overhead.
1898-
DebugMode *bool `toml:",omitempty"`
1898+
DebugMode *bool `toml:",omitempty"`
1899+
ConfidentialRelay *ConfidentialRelayConfig `toml:",omitempty"`
18991900
}
19001901

19011902
// WorkflowFetcherConfig holds the configuration for fetching workflow files
19021903
type WorkflowFetcherConfig struct {
19031904
URL *string `toml:",omitempty"`
19041905
}
19051906

1907+
// ConfidentialRelayConfig holds the configuration for the confidential relay handler.
1908+
// When Enabled is true, the node participates in the confidential relay DON,
1909+
// validating enclave attestations and proxying capability requests.
1910+
type ConfidentialRelayConfig struct {
1911+
Enabled *bool `toml:",omitempty"`
1912+
TrustedPCRs *string `toml:",omitempty"`
1913+
CARootsPEM *string `toml:",omitempty"`
1914+
}
1915+
19061916
// LinkingConfig holds the configuration for connecting to the CRE linking service
19071917
type LinkingConfig struct {
19081918
URL *string `toml:",omitempty"`
@@ -1956,6 +1966,21 @@ func (c *CreConfig) setFrom(f *CreConfig) {
19561966
if f.DebugMode != nil {
19571967
c.DebugMode = f.DebugMode
19581968
}
1969+
1970+
if f.ConfidentialRelay != nil {
1971+
if c.ConfidentialRelay == nil {
1972+
c.ConfidentialRelay = &ConfidentialRelayConfig{}
1973+
}
1974+
if v := f.ConfidentialRelay.Enabled; v != nil {
1975+
c.ConfidentialRelay.Enabled = v
1976+
}
1977+
if v := f.ConfidentialRelay.TrustedPCRs; v != nil {
1978+
c.ConfidentialRelay.TrustedPCRs = v
1979+
}
1980+
if v := f.ConfidentialRelay.CARootsPEM; v != nil {
1981+
c.ConfidentialRelay.CARootsPEM = v
1982+
}
1983+
}
19591984
}
19601985

19611986
func (w *WorkflowFetcherConfig) ValidateConfig() error {

core/scripts/go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,13 @@ require (
4646
github.com/shopspring/decimal v1.4.0
4747
github.com/smartcontractkit/chainlink-automation v0.8.1
4848
github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260317185256-d5f7db87ae70
49-
github.com/smartcontractkit/chainlink-common v0.11.0
49+
github.com/smartcontractkit/chainlink-common v0.11.1-0.20260323163826-2c5b95089478
5050
github.com/smartcontractkit/chainlink-common/keystore v1.0.2
5151
github.com/smartcontractkit/chainlink-data-streams v0.1.13
5252
github.com/smartcontractkit/chainlink-deployments-framework v0.86.3
5353
github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260320152158-2191d797b5ce
5454
github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260119171452-39c98c3b33cd
55-
github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260226130359-963f935e0396
55+
github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260320153346-314ec8dbe5a4
5656
github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0
5757
github.com/smartcontractkit/chainlink-testing-framework/framework v0.15.5
5858
github.com/smartcontractkit/chainlink-testing-framework/framework/components/dockercompose v0.1.20

core/scripts/go.sum

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/services/chainlink/config_cre.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,35 @@ func (c *creConfig) Linking() config.CRELinking {
105105
return &linkingConfig{url: url, tlsEnabled: tlsEnabled}
106106
}
107107

108+
type confidentialRelayConfig struct {
109+
enabled bool
110+
trustedPCRs string
111+
caRootsPEM string
112+
}
113+
114+
func (cr *confidentialRelayConfig) Enabled() bool { return cr.enabled }
115+
func (cr *confidentialRelayConfig) TrustedPCRs() string { return cr.trustedPCRs }
116+
func (cr *confidentialRelayConfig) CARootsPEM() string { return cr.caRootsPEM }
117+
118+
func (c *creConfig) ConfidentialRelay() config.CREConfidentialRelay {
119+
if c.c.ConfidentialRelay == nil {
120+
return &confidentialRelayConfig{}
121+
}
122+
enabled := false
123+
if c.c.ConfidentialRelay.Enabled != nil {
124+
enabled = *c.c.ConfidentialRelay.Enabled
125+
}
126+
trustedPCRs := ""
127+
if c.c.ConfidentialRelay.TrustedPCRs != nil {
128+
trustedPCRs = *c.c.ConfidentialRelay.TrustedPCRs
129+
}
130+
caRootsPEM := ""
131+
if c.c.ConfidentialRelay.CARootsPEM != nil {
132+
caRootsPEM = *c.c.ConfidentialRelay.CARootsPEM
133+
}
134+
return &confidentialRelayConfig{enabled: enabled, trustedPCRs: trustedPCRs, caRootsPEM: caRootsPEM}
135+
}
136+
108137
func (c *creConfig) LocalSecrets() map[string]string {
109138
return c.s.LocalSecrets
110139
}

core/services/job/models.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -930,6 +930,7 @@ type WorkflowSpec struct {
930930
CreatedAt time.Time `toml:"-"`
931931
UpdatedAt time.Time `toml:"-"`
932932
SpecType WorkflowSpecType `toml:"spec_type" db:"spec_type"`
933+
Attributes []byte `db:"attributes"`
933934
sdkWorkflow *sdk.WorkflowSpec
934935
rawSpec []byte
935936
config []byte

core/services/standardcapabilities/conversions/conversions.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ func GetCapabilityIDFromCommand(command string, config string) string {
3333
return "http-trigger@1.0.0-alpha"
3434
case "http_action":
3535
return "http-actions@1.0.0-alpha" // plural "actions"
36+
case "mock":
37+
return "mock@1.0.0"
3638
default:
3739
return ""
3840
}
@@ -52,6 +54,8 @@ func GetCommandFromCapabilityID(capabilityID string) string {
5254
return "http_trigger"
5355
case strings.HasPrefix(capabilityID, "http-actions"):
5456
return "http_action"
57+
case strings.HasPrefix(capabilityID, "mock"):
58+
return "mock"
5559
default:
5660
return ""
5761
}

core/services/workflows/artifacts/v2/orm.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ func (orm *orm) UpsertWorkflowSpec(ctx context.Context, spec *job.WorkflowSpec)
6363
config_url,
6464
created_at,
6565
updated_at,
66-
spec_type
66+
spec_type,
67+
attributes
6768
) VALUES (
6869
:workflow,
6970
:config,
@@ -76,7 +77,8 @@ func (orm *orm) UpsertWorkflowSpec(ctx context.Context, spec *job.WorkflowSpec)
7677
:config_url,
7778
:created_at,
7879
:updated_at,
79-
:spec_type
80+
:spec_type,
81+
:attributes
8082
) ON CONFLICT (workflow_id) DO UPDATE
8183
SET
8284
workflow = EXCLUDED.workflow,
@@ -89,7 +91,8 @@ func (orm *orm) UpsertWorkflowSpec(ctx context.Context, spec *job.WorkflowSpec)
8991
config_url = EXCLUDED.config_url,
9092
created_at = EXCLUDED.created_at,
9193
updated_at = EXCLUDED.updated_at,
92-
spec_type = EXCLUDED.spec_type
94+
spec_type = EXCLUDED.spec_type,
95+
attributes = EXCLUDED.attributes
9396
RETURNING id
9497
`
9598

core/services/workflows/syncer/fetcher.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -177,12 +177,20 @@ func newFileFetcher(basePath string, lggr logger.Logger) types.FetcherFunc {
177177
if err != nil {
178178
return nil, fmt.Errorf("invalid URL: %w", err)
179179
}
180-
fullPath := filepath.Clean(u.Path)
181180

182-
// ensure that the incoming request URL is either relative or absolute but within the basePath
183-
if !filepath.IsAbs(fullPath) {
184-
// If it's not absolute, we assume it's relative to the basePath
185-
fullPath = filepath.Join(basePath, fullPath)
181+
var fullPath string
182+
if u.Scheme == "http" || u.Scheme == "https" {
183+
// For HTTP(S) URLs, extract just the filename and resolve against basePath.
184+
// This supports confidential workflows where the on-chain URL must be HTTP
185+
// (so the enclave can fetch the binary), but the syncer reads from the local filesystem.
186+
fullPath = filepath.Join(basePath, filepath.Base(u.Path))
187+
} else {
188+
fullPath = filepath.Clean(u.Path)
189+
// ensure that the incoming request URL is either relative or absolute but within the basePath
190+
if !filepath.IsAbs(fullPath) {
191+
// If it's not absolute, we assume it's relative to the basePath
192+
fullPath = filepath.Join(basePath, fullPath)
193+
}
186194
}
187195
if !strings.HasPrefix(fullPath, basePath) {
188196
return nil, fmt.Errorf("request URL %s is not within the basePath %s", fullPath, basePath)

core/services/workflows/syncer/v2/fetcher.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -211,12 +211,20 @@ func newFileFetcher(basePath string, lggr logger.Logger) types.FetcherFunc {
211211
if err != nil {
212212
return nil, fmt.Errorf("invalid URL: %w", err)
213213
}
214-
fullPath := filepath.Clean(u.Path)
215214

216-
// ensure that the incoming request URL is either relative or absolute but within the basePath
217-
if !filepath.IsAbs(fullPath) {
218-
// If it's not absolute, we assume it's relative to the basePath
219-
fullPath = filepath.Join(basePath, fullPath)
215+
var fullPath string
216+
if u.Scheme == "http" || u.Scheme == "https" {
217+
// For HTTP(S) URLs, extract just the filename and resolve against basePath.
218+
// This supports confidential workflows where the on-chain URL must be HTTP
219+
// (so the enclave can fetch the binary), but the syncer reads from the local filesystem.
220+
fullPath = filepath.Join(basePath, filepath.Base(u.Path))
221+
} else {
222+
fullPath = filepath.Clean(u.Path)
223+
// ensure that the incoming request URL is either relative or absolute but within the basePath
224+
if !filepath.IsAbs(fullPath) {
225+
// If it's not absolute, we assume it's relative to the basePath
226+
fullPath = filepath.Join(basePath, fullPath)
227+
}
220228
}
221229
if !strings.HasPrefix(fullPath, basePath) {
222230
return nil, fmt.Errorf("request URL %s is not within the basePath %s", fullPath, basePath)

0 commit comments

Comments
 (0)