Skip to content

Commit 6396d35

Browse files
authored
Add validation for a minimum of 16 nops in ccip 2.0 testnet and mainnet env (#2006)
1 parent 247bc7e commit 6396d35

13 files changed

Lines changed: 311 additions & 20 deletions

deployment/v2_0_0/changesets/apply_executor_config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ func ApplyExecutorConfig(registry *adapters.ExecutorConfigRegistry) deployment.C
3131
if cfg.Topology == nil {
3232
return fmt.Errorf("topology is required")
3333
}
34+
if err := cfg.Topology.ValidateForEnvironment(e.Name); err != nil {
35+
return fmt.Errorf("topology validation failed: %w", err)
36+
}
3437

3538
if cfg.ExecutorQualifier == "" {
3639
return fmt.Errorf("executor qualifier is required")

deployment/v2_0_0/changesets/apply_executor_config_test.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ func newMinimalTopology(nopAliases []string, executorQualifier string, mode shar
6969
}
7070
}
7171

72+
func makeTestNOPAliases(count int) []string {
73+
aliases := make([]string, count)
74+
for i := range aliases {
75+
aliases[i] = fmt.Sprintf("nop%d", i+1)
76+
}
77+
return aliases
78+
}
79+
7280
func newTestExecutorEnv(t *testing.T, selectors []uint64) deployment.Environment {
7381
t.Helper()
7482
lggr := logger.Test(t)
@@ -125,7 +133,16 @@ func TestApplyExecutorConfig_Validation(t *testing.T) {
125133
},
126134
ExecutorQualifier: "pool1",
127135
},
128-
wantErr: "NOP topology with at least one NOP is required",
136+
wantErr: "nop_topology is required",
137+
},
138+
{
139+
name: "production topology with fewer than sixteen NOPs returns error",
140+
env: deployment.Environment{Name: "prod_mainnet", BlockChains: newExecutorTestBlockChains([]uint64{sel1})},
141+
input: changesets.ApplyExecutorConfigInput{
142+
Topology: newMinimalTopology([]string{"nop1"}, "pool1", ""),
143+
ExecutorQualifier: "pool1",
144+
},
145+
wantErr: "requires at least 16 unique NOPs",
129146
},
130147
{
131148
name: "missing executor qualifier returns error",
@@ -159,7 +176,7 @@ func TestApplyExecutorConfig_Validation(t *testing.T) {
159176
env: deployment.Environment{Name: "mainnet", BlockChains: newExecutorTestBlockChains([]uint64{sel1})},
160177
input: changesets.ApplyExecutorConfigInput{
161178
Topology: func() *offchain.EnvironmentTopology {
162-
topo := newMinimalTopology([]string{"nop1"}, "pool1", "")
179+
topo := newMinimalTopology(makeTestNOPAliases(16), "pool1", "")
163180
topo.PyroscopeURL = "http://pyroscope"
164181
return topo
165182
}(),

deployment/v2_0_0/changesets/apply_verifier_config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ func ApplyVerifierConfig(registry *adapters.VerifierConfigRegistry) deployment.C
3434
if cfg.Topology == nil {
3535
return fmt.Errorf("topology is required")
3636
}
37+
if err := cfg.Topology.ValidateForEnvironment(e.Name); err != nil {
38+
return fmt.Errorf("topology validation failed: %w", err)
39+
}
3740

3841
if cfg.Topology.NOPTopology == nil || len(cfg.Topology.NOPTopology.NOPs) == 0 {
3942
return fmt.Errorf("NOP topology with at least one NOP is required")

deployment/v2_0_0/changesets/apply_verifier_config_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ func TestApplyVerifierConfig_Validation(t *testing.T) {
177177
CommitteeQualifier: "c1",
178178
DefaultExecutorQualifier: "pool1",
179179
},
180-
wantErr: "NOP topology with at least one NOP is required",
180+
wantErr: "nop_topology is required",
181181
},
182182
{
183183
name: "committee with no aggregators returns error",
@@ -208,7 +208,7 @@ func TestApplyVerifierConfig_Validation(t *testing.T) {
208208
env: deployment.Environment{Name: "mainnet", BlockChains: cldf_chain.NewBlockChains(map[uint64]cldf_chain.BlockChain{sel1: cldfevm.Chain{Selector: sel1}})},
209209
input: changesets.ApplyVerifierConfigInput{
210210
Topology: func() *offchain.EnvironmentTopology {
211-
topo := newVerifierTopology([]string{"nop1"}, "c1", []uint64{sel1}, "")
211+
topo := newVerifierTopology(makeTestNOPAliases(16), "c1", []uint64{sel1}, "")
212212
topo.PyroscopeURL = "http://pyroscope"
213213
return topo
214214
}(),

deployment/v2_0_0/changesets/configure_chains_for_lanes_from_topology.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,9 @@ func ConfigureChainsForLanesFromTopology(
160160
if cfg.Topology == nil {
161161
return fmt.Errorf("topology is required")
162162
}
163+
if err := cfg.Topology.ValidateForEnvironment(e.Name); err != nil {
164+
return fmt.Errorf("topology validation failed: %w", err)
165+
}
163166
if cfg.Topology.NOPTopology == nil || len(cfg.Topology.NOPTopology.Committees) == 0 {
164167
return fmt.Errorf("no committees defined in topology")
165168
}

deployment/v2_0_0/changesets/configure_chains_for_lanes_from_topology_test.go

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -289,10 +289,10 @@ func TestConfigureChainsForLanesFromTopology_HappyPathAndCrossFamily(t *testing.
289289
CommitteeVerifiers: []changesets.CommitteeVerifierInputConfig{
290290
{
291291
CommitteeQualifier: "default",
292-
RemoteChains: map[uint64]changesets.CommitteeVerifierRemoteChainConfig{
293-
remoteEVM: {FeeUSDCents: ptrTo[uint16](10), GasForVerification: ptrTo[uint32](20), PayloadSizeBytes: ptrTo[uint16](30)},
294-
remoteSolana: {FeeUSDCents: ptrTo[uint16](40), GasForVerification: ptrTo[uint32](50), PayloadSizeBytes: ptrTo[uint16](60)},
295-
},
292+
RemoteChains: map[uint64]changesets.CommitteeVerifierRemoteChainConfig{
293+
remoteEVM: {FeeUSDCents: ptrTo[uint16](10), GasForVerification: ptrTo[uint32](20), PayloadSizeBytes: ptrTo[uint16](30)},
294+
remoteSolana: {FeeUSDCents: ptrTo[uint16](40), GasForVerification: ptrTo[uint32](50), PayloadSizeBytes: ptrTo[uint16](60)},
295+
},
296296
},
297297
},
298298
RemoteChains: map[uint64]changesets.PartialRemoteChainConfig{
@@ -919,12 +919,19 @@ func TestConfigureChainsForLanesFromTopology_VerifyPreconditions(t *testing.T) {
919919
)
920920
err := cs.VerifyPreconditions(env, changesets.ConfigureChainsForLanesFromTopologyConfig{
921921
Topology: &offchain.EnvironmentTopology{
922+
IndexerAddress: []string{"http://indexer:8080"},
922923
NOPTopology: &offchain.NOPTopology{
923-
NOPs: []offchain.NOPConfig{{Alias: "nop-1"}},
924+
NOPs: []offchain.NOPConfig{{Alias: "nop-1", Name: "nop-1-name"}},
924925
Committees: map[string]offchain.CommitteeConfig{
925-
"default": {Qualifier: "default", ChainConfigs: map[string]offchain.ChainCommitteeConfig{
926-
fmt.Sprintf("%d", remoteSelector): {NOPAliases: []string{"nop-1"}, Threshold: 1},
927-
}},
926+
"default": {
927+
Qualifier: "default",
928+
Aggregators: []offchain.AggregatorConfig{
929+
{Name: "agg-1", Address: "http://aggregator:8080"},
930+
},
931+
ChainConfigs: map[string]offchain.ChainCommitteeConfig{
932+
fmt.Sprintf("%d", remoteSelector): {NOPAliases: []string{"nop-1"}, Threshold: 1},
933+
},
934+
},
928935
},
929936
},
930937
},
@@ -949,12 +956,19 @@ func TestConfigureChainsForLanesFromTopology_VerifyPreconditions(t *testing.T) {
949956
)
950957
err := cs.VerifyPreconditions(env, changesets.ConfigureChainsForLanesFromTopologyConfig{
951958
Topology: &offchain.EnvironmentTopology{
959+
IndexerAddress: []string{"http://indexer:8080"},
952960
NOPTopology: &offchain.NOPTopology{
953-
NOPs: []offchain.NOPConfig{{Alias: "nop-1"}},
961+
NOPs: []offchain.NOPConfig{{Alias: "nop-1", Name: "nop-1-name"}},
954962
Committees: map[string]offchain.CommitteeConfig{
955-
"default": {Qualifier: "default", ChainConfigs: map[string]offchain.ChainCommitteeConfig{
956-
fmt.Sprintf("%d", remoteSelector): {NOPAliases: []string{"nop-1"}, Threshold: 1},
957-
}},
963+
"default": {
964+
Qualifier: "default",
965+
Aggregators: []offchain.AggregatorConfig{
966+
{Name: "agg-1", Address: "http://aggregator:8080"},
967+
},
968+
ChainConfigs: map[string]offchain.ChainCommitteeConfig{
969+
fmt.Sprintf("%d", remoteSelector): {NOPAliases: []string{"nop-1"}, Threshold: 1},
970+
},
971+
},
958972
},
959973
},
960974
},

deployment/v2_0_0/changesets/deploy_chain_contracts.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ func DeployChainContracts(registry *adapters.DeployChainContractsRegistry) deplo
6060
if cfg.Cfg.Topology == nil {
6161
return fmt.Errorf("topology is required")
6262
}
63+
if err := cfg.Cfg.Topology.ValidateForEnvironment(e.Name); err != nil {
64+
return fmt.Errorf("topology validation failed: %w", err)
65+
}
6366

6467
if cfg.Cfg.Topology.NOPTopology == nil || len(cfg.Cfg.Topology.NOPTopology.Committees) == 0 {
6568
return fmt.Errorf("no committees defined in topology")

deployment/v2_0_0/changesets/generate_aggregator_config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ func resolveAggregatorChainSelectors(e deployment.Environment, cfg GenerateAggre
6565
if cfg.Topology == nil {
6666
return nil, fmt.Errorf("topology is required")
6767
}
68+
if err := cfg.Topology.ValidateForEnvironment(e.Name); err != nil {
69+
return nil, fmt.Errorf("topology validation failed: %w", err)
70+
}
6871
if cfg.Topology.NOPTopology == nil {
6972
return nil, fmt.Errorf("NOP topology is required")
7073
}

deployment/v2_0_0/changesets/generate_aggregator_config_test.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,23 @@ func newAggregatorConfigRegistry() *adapters.AggregatorConfigRegistry {
6262
func newTopologyForCommittee(qualifier string, selectors []uint64) *offchain.EnvironmentTopology {
6363
chainConfigs := make(map[string]offchain.ChainCommitteeConfig, len(selectors))
6464
for _, sel := range selectors {
65-
chainConfigs[strconv.FormatUint(sel, 10)] = offchain.ChainCommitteeConfig{}
65+
chainConfigs[strconv.FormatUint(sel, 10)] = offchain.ChainCommitteeConfig{
66+
NOPAliases: []string{"nop1"},
67+
Threshold: 1,
68+
}
6669
}
6770

6871
return &offchain.EnvironmentTopology{
72+
IndexerAddress: []string{"http://indexer:8080"},
6973
NOPTopology: &offchain.NOPTopology{
74+
NOPs: []offchain.NOPConfig{{Alias: "nop1", Name: "nop1-name"}},
7075
Committees: map[string]offchain.CommitteeConfig{
7176
qualifier: {
7277
Qualifier: qualifier,
7378
ChainConfigs: chainConfigs,
79+
Aggregators: []offchain.AggregatorConfig{
80+
{Name: "agg-1", Address: "http://aggregator:8080"},
81+
},
7482
},
7583
},
7684
},
@@ -118,15 +126,19 @@ func TestGenerateAggregatorConfig_Validation(t *testing.T) {
118126
CommitteeQualifier: "default",
119127
Topology: &offchain.EnvironmentTopology{},
120128
},
121-
errContains: "NOP topology is required",
129+
errContains: "nop_topology is required",
122130
},
123131
{
124132
name: "missing committee in topology",
125133
input: changesets.GenerateAggregatorConfigInput{
126134
ServiceIdentifier: "agg",
127135
CommitteeQualifier: "default",
128136
Topology: &offchain.EnvironmentTopology{
129-
NOPTopology: &offchain.NOPTopology{Committees: map[string]offchain.CommitteeConfig{}},
137+
IndexerAddress: []string{"http://indexer:8080"},
138+
NOPTopology: &offchain.NOPTopology{
139+
NOPs: []offchain.NOPConfig{{Alias: "nop1", Name: "nop1-name"}},
140+
Committees: map[string]offchain.CommitteeConfig{},
141+
},
130142
},
131143
},
132144
errContains: `committee "default" not found in topology`,

deployment/v2_0_0/offchain/shared/types.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,12 @@ func ConvertStringToNopAliases(strs []string) []NOPAlias {
200200
}
201201

202202
func IsProductionEnvironment(env string) bool {
203-
return env == "mainnet"
203+
switch env {
204+
case "mainnet", "prod_mainnet", "prod_testnet":
205+
return true
206+
default:
207+
return false
208+
}
204209
}
205210

206211
func NOPAliasSliceToSet(slice []NOPAlias) map[NOPAlias]bool {

0 commit comments

Comments
 (0)