offchain tooling api#1031
Conversation
There was a problem hiding this comment.
I'm a little hesitant to bury modules in build/devenv. Should we put this in a new top-level package called platform and treat that directory as a sort of mono-repo?
There was a problem hiding this comment.
This is the EVM impl of offchain tooling and it is at the top level :-). Don't know if there's a better home for that
Where should the EVM impl live vs the interface declarations? We can rename chainlink-ccv/deployment to chainlink-ccv/platform if that's clearer
There was a problem hiding this comment.
I think this is fine for now, maybe we can have an evm/deployment which has the interface implementations of the offchain deployment interfaces?
I'm imagining that in the future evm could also hold the evm-specific implementations of the chainaccess interfaces as well.
The chainlink-ccv/platform module can then have the subdirectories devenv for the devenv package and deployment for the offchain deployment API?
There was a problem hiding this comment.
Pull request overview
This PR introduces a CCV “offchain tooling API” layer: chain-family adapters (starting with EVM) plus deployment operations/changesets for generating offchain service configs and managing Job Distributor (JD) job proposals, and updates the devenv tooling to use these new APIs.
Changes:
- Add a new
deploymentmodule with topology/config models, env-metadata persistence helpers, JD-facing operations (propose/revoke/sync), and changesets (apply verifier/executor configs; generate aggregator/indexer/token-verifier configs). - Add an
evmmodule that registers EVM adapter implementations for the new deployment registries. - Update
build/devenvto consume the CCV deployment package (with a TOML-based shim to interop with legacy CCIP topology types where needed).
Reviewed changes
Copilot reviewed 42 out of 44 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| evm/verifier_config.go | EVM adapter resolving verifier-related contract addresses from datastore. |
| evm/token_verifier_config.go | EVM adapter resolving token-verifier-related addresses from datastore. |
| evm/register.go | EVM init self-registration (chain type → family, address normalization, adapter registrations). |
| evm/indexer_config.go | EVM adapter resolving verifier resolver addresses for indexer config generation. |
| evm/go.mod | New nested Go module for EVM-specific deployment adapters. |
| evm/executor_config.go | EVM adapter building executor chain configuration from datastore. |
| evm/aggregator_config.go | EVM adapter scanning deployed committee state + resolving verifier address for aggregator config generation. |
| deployment/topology.go | New topology model + TOML load/write + validation helpers. |
| deployment/token_verifier.go | Token verifier generated config schema (JSON). |
| deployment/shared/types.go | Shared types for jobs/NOPs/scopes + job metadata helpers. |
| deployment/shared/nodes.go | JD node lookup utilities and guarded node fetching. |
| deployment/shared/jd_client.go | JD client interface abstraction used by operations. |
| deployment/shared/chain_validation.go | Helpers to validate NOP chain support and format user-facing errors. |
| deployment/shared/chain_type_registry.go | Registry for JD chain-type → chain-selectors family + address normalizers. |
| deployment/sequences/manage_job_proposals.go | Sequence to propose changed jobs and optionally revoke/cleanup orphaned jobs. |
| deployment/operations/sync_job_proposals/sync_job_proposals.go | Operation to sync proposal statuses/specs from JD and detect drift/orphans. |
| deployment/operations/revoke_jobs/revoke_jobs.go | Operation to revoke jobs via JD with node-ownership validation. |
| deployment/operations/propose_jobs/propose_jobs.go | Operation to propose jobs via JD and return proposal metadata. |
| deployment/operations/fetch_signing_keys/fetch_signing_keys.go | Operation to batch fetch OCR signing addresses from JD chain configs. |
| deployment/operations/fetch_node_chain_support/fetch_node_chain_support.go | Operation to fetch supported chain selectors per NOP from JD chain configs. |
| deployment/indexer.go | Indexer generated config schema (JSON). |
| deployment/go.mod | New nested Go module for deployment tooling and dependencies. |
| deployment/env_metadata_util.go | Persist/load CCV offchain configs + NOP jobs into datastore env metadata. |
| deployment/changesets/sync_job_proposals.go | Changeset wrapper around SyncJobProposals operation. |
| deployment/changesets/generate_token_verifier_config.go | Changeset to generate + store token verifier offchain config from onchain state. |
| deployment/changesets/generate_indexer_config.go | Changeset to generate + store indexer config by collecting resolver addresses. |
| deployment/changesets/generate_aggregator_config.go | Changeset to generate + store aggregator committee config from onchain state. |
| deployment/changesets/apply_verifier_config.go | Changeset to build verifier job specs, propose via JD, and manage revocations/cleanup. |
| deployment/changesets/apply_executor_config.go | Changeset to build executor job specs, propose via JD, and manage revocations/cleanup. |
| deployment/aggregator.go | Aggregator generated config schema (JSON). |
| deployment/adapters/verifier_config.go | Verifier adapter interface + registry keyed by chain family. |
| deployment/adapters/token_verifier_config.go | Token-verifier adapter interface + registry keyed by chain family. |
| deployment/adapters/indexer_config.go | Indexer adapter interface + registry + missing-address error type. |
| deployment/adapters/executor_config.go | Executor adapter interface + registry + helper to collect deployed chains. |
| deployment/adapters/aggregator_config.go | Aggregator adapter interface + registry for committee discovery and resolver lookup. |
| build/devenv/topology_compat.go | TOML encode/decode shim to convert CCV topology → legacy CCIP topology types. |
| build/devenv/offchainloader/loader.go | Switch offchain config loading to CCV deployment metadata types. |
| build/devenv/jobs/jd.go | Switch devenv job proposal syncing/verification to CCV changesets + shared types. |
| build/devenv/implcommon.go | Update devenv chain deploy/configure helpers to accept CCV topology (convert when calling CCIP code). |
| build/devenv/go.mod | Add local replaces + requires for new deployment and evm modules. |
| build/devenv/expand_ha_test.go | Update tests to use CCV topology types instead of legacy CCIP offchain types. |
| build/devenv/environment.go | Wire devenv environment generation to CCV changesets/adapters and import EVM adapters for registration. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if err := c.NOPTopology.Validate(); err != nil { | ||
| return fmt.Errorf("nop_topology validation failed: %w", err) | ||
| } |
There was a problem hiding this comment.
EnvironmentTopology.Validate dereferences c.NOPTopology without checking for nil. If the topology file omits nop_topology (or it fails to unmarshal), this will panic instead of returning a validation error. Add an explicit nil check for c.NOPTopology (and similarly handle a nil receiver in (*NOPTopology).Validate) and return a descriptive error like nop_topology is required.
makramkd
left a comment
There was a problem hiding this comment.
Pros: chain-agnostic interfaces, following existing patterns. Cons: config struct duplication, EnvironmentTopology seems committee-verifier specific, I feel like it either needs a better home or there needs to be some synergy b/w lane definitions, chain support, and committee verifier configurations, as all have been referred to as "topology" at some point in time and confusion is nigh upon us.
| type AggregatorConfigRegistry struct { | ||
| mu sync.Mutex | ||
| adapters map[string]AggregatorConfigAdapter | ||
| } |
There was a problem hiding this comment.
A developer ergonomics question: have we thought about potentially combining the registries into one, that ends up registering a struct of all these different interface types, in order to have a single entrypoint?
type Adapters struct {
AggregatorConfigAdapter AggregatorConfigAdapter
ExecutorConfigAdapter ExecutorConfigAdapter
// the rest of them ...
}
type AdapterRegistry struct {
mu sync.Mutex
adapters map[string]Adapters
}
func (r *AdapterRegistry) Register(family string, adapters Adapters) {
// register func same as below.
}There was a problem hiding this comment.
Yeah I remember us talking about this. We can switch these to a single register to start the pattern. This was mostly a lift and shift from what already existed
| IndexerAddress []string `toml:"indexer_address"` | ||
| PyroscopeURL string `toml:"pyroscope_url"` | ||
| Monitoring MonitoringConfig `toml:"monitoring"` | ||
| NOPTopology *NOPTopology `toml:"nop_topology"` |
There was a problem hiding this comment.
This being a pointer suggests it could be nil (and we'd have to do nil checks in a lot of places). Is this the case or is it actually required?
| // NOPTopology defines the node operator structure and committee membership. | ||
| type NOPTopology struct { | ||
| NOPs []NOPConfig `toml:"nops"` | ||
| Committees map[string]CommitteeConfig `toml:"committees"` |
There was a problem hiding this comment.
This NOPTopology type seems weird to me because its committee-verifier specific is it not? Or is the entire EnvironmentTopology structure committee-verifier specific?
| IndexerQueryLimit uint64 `toml:"indexer_query_limit"` | ||
| BackoffDuration time.Duration `toml:"backoff_duration"` | ||
| LookbackWindow time.Duration `toml:"lookback_window"` | ||
| ReaderCacheExpiry time.Duration `toml:"reader_cache_expiry"` | ||
| MaxRetryDuration time.Duration `toml:"max_retry_duration"` | ||
| WorkerCount int `toml:"worker_count"` | ||
| NtpServer string `toml:"ntp_server"` |
There was a problem hiding this comment.
These are the previously duplicated executor configs? Or were those somewhere else?
| type BeholderInput struct { | ||
| InsecureConnection bool | ||
| CACertFile string | ||
| OtelExporterGRPCEndpoint string | ||
| OtelExporterHTTPEndpoint string | ||
| LogStreamingEnabled bool | ||
| MetricReaderInterval int64 | ||
| TraceSampleRatio float64 | ||
| TraceBatchTimeout int64 | ||
| } |
There was a problem hiding this comment.
This seems to be duplicated in multiple places, can we import it from somewhere where its canonically defined?
| type SourceSelector = string | ||
|
|
||
| type DestinationSelector = string | ||
|
|
||
| type Signer struct { | ||
| Address string `json:"address"` | ||
| } | ||
|
|
||
| type QuorumConfig struct { | ||
| SourceVerifierAddress string `json:"sourceVerifierAddress"` | ||
| Signers []Signer `json:"signers"` | ||
| Threshold uint8 `json:"threshold"` | ||
| } | ||
|
|
||
| type Committee struct { | ||
| QuorumConfigs map[SourceSelector]*QuorumConfig `json:"quorumConfigs"` | ||
| DestinationVerifiers map[DestinationSelector]string `json:"destinationVerifiers"` | ||
| } |
| type IndexerVerifierConfig struct { | ||
| Name string `json:"name"` | ||
| IssuerAddresses []string `json:"issuerAddresses"` | ||
| } | ||
|
|
||
| type IndexerGeneratedConfig struct { | ||
| Verifiers []IndexerVerifierConfig `json:"verifiers"` | ||
| } |
There was a problem hiding this comment.
Ditto here, duplicated? Whats the plan in a follow up?
There was a problem hiding this comment.
We need to clean this up in general.
| type TokenVerifierGeneratedConfig struct { | ||
| PyroscopeURL string `json:"pyroscope_url"` | ||
| OnRampAddresses map[string]string `json:"on_ramp_addresses"` | ||
| RMNRemoteAddresses map[string]string `json:"rmn_remote_addresses"` | ||
| TokenVerifiers []TokenVerifierEntry `json:"token_verifiers"` | ||
| Monitoring TokenVerifierMonitoringConfig `json:"monitoring"` | ||
| } |
|
Code coverage report:
|
No description provided.