Skip to content

Commit 01b76a7

Browse files
authored
[CRE-1924] Support DONtime plugin config in CLD (#21299)
1 parent ffe062b commit 01b76a7

File tree

5 files changed

+340
-0
lines changed

5 files changed

+340
-0
lines changed

deployment/cre/capabilities_registry/v2/changeset/configure_capabilities_registry_test.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,105 @@ dons:
444444
assert.Equal(t, 40*time.Second, oc.ConsensusCapOffchainConfig.RequestTimeout)
445445
}
446446

447+
func TestConfigureCapabilitiesRegistryInput_YAMLFromFile_DontimeConfig(t *testing.T) {
448+
yamlConfig := `
449+
chainSelector: 421614
450+
capabilitiesRegistryAddress: "0x1234567890123456789012345678901234567890"
451+
nops:
452+
- admin: "0x1111111111111111111111111111111111111111"
453+
name: "Node Operator Alpha"
454+
capabilities:
455+
- capabilityID: "dontime@1.0.0"
456+
configurationContract: "0x0000000000000000000000000000000000000000"
457+
metadata:
458+
capabilityType: 2
459+
responseType: 0
460+
nodes:
461+
- nop: "test-nop"
462+
signer: ` + signer1 + `
463+
p2pID: ` + p2pID1 + `
464+
encryptionPublicKey: ` + encryptionPublicKey + `
465+
csaKey: ` + csaKey + `
466+
capabilityIDs: ["dontime@1.0.0"]
467+
dons:
468+
- name: "dontime-don"
469+
donFamilies: ["dontime"]
470+
config:
471+
defaultConfig: {}
472+
capabilityConfigurations:
473+
- capabilityID: "dontime@1.0.0"
474+
config:
475+
ocr3Configs:
476+
__default__:
477+
deltaProgressMillis: 3000
478+
deltaResendMillis: 5000
479+
deltaInitialMillis: 500
480+
deltaRoundMillis: 500
481+
deltaGraceMillis: 200
482+
deltaCertifiedCommitRequestMillis: 1000
483+
deltaStageMillis: 30000
484+
maxRoundsPerEpoch: 10
485+
transmissionSchedule: [7]
486+
maxFaultyOracles: 2
487+
uniqueReports: true
488+
maxDurationQueryMillis: 500
489+
maxDurationObservationMillis: 500
490+
maxDurationShouldAcceptMillis: 500
491+
maxDurationShouldTransmitMillis: 500
492+
dontimeOffchainConfig:
493+
maxQueryLengthBytes: 500000
494+
maxObservationLengthBytes: 500000
495+
maxOutcomeLengthBytes: 500000
496+
maxReportLengthBytes: 500000
497+
maxReportCount: 10
498+
maxBatchSize: 50
499+
minTimeIncrease: 100
500+
executionRemovalTime: "10m"
501+
nodes: [` + nodeID1 + `]
502+
f: 1
503+
isPublic: true
504+
acceptsWorkflows: false
505+
`
506+
507+
var input changeset.ConfigureCapabilitiesRegistryInput
508+
err := yaml.Unmarshal([]byte(yamlConfig), &input)
509+
require.NoError(t, err, "should be able to parse YAML config with dontime offchain config")
510+
511+
assert.Equal(t, uint64(421614), input.ChainSelector)
512+
require.Len(t, input.DONs, 1)
513+
assert.Equal(t, "dontime-don", input.DONs[0].Name)
514+
515+
require.Len(t, input.DONs[0].CapabilityConfigurations, 1)
516+
assert.Equal(t, "dontime@1.0.0", input.DONs[0].CapabilityConfigurations[0].CapabilityID)
517+
518+
ocr3Configs, ok := input.DONs[0].CapabilityConfigurations[0].Config["ocr3Configs"].(map[string]any)
519+
require.True(t, ok, "ocr3Configs should be a map")
520+
defaultEntry, ok := ocr3Configs["__default__"]
521+
require.True(t, ok, "__default__ key should exist in ocr3Configs")
522+
523+
ocrJSON, err := json.Marshal(defaultEntry)
524+
require.NoError(t, err)
525+
var oc ocr3.OracleConfig
526+
require.NoError(t, json.Unmarshal(ocrJSON, &oc))
527+
528+
assert.Equal(t, uint32(3000), oc.DeltaProgressMillis)
529+
assert.Equal(t, 2, oc.MaxFaultyOracles)
530+
assert.True(t, oc.UniqueReports)
531+
assert.Nil(t, oc.ConsensusCapOffchainConfig, "consensusCapOffchainConfig should be nil")
532+
assert.Nil(t, oc.ChainCapOffchainConfig, "chainCapOffchainConfig should be nil")
533+
534+
require.NotNil(t, oc.DontimeOffchainConfig, "dontimeOffchainConfig should be parsed")
535+
dt := oc.DontimeOffchainConfig
536+
assert.Equal(t, uint32(500000), dt.MaxQueryLengthBytes)
537+
assert.Equal(t, uint32(500000), dt.MaxObservationLengthBytes)
538+
assert.Equal(t, uint32(500000), dt.MaxOutcomeLengthBytes)
539+
assert.Equal(t, uint32(500000), dt.MaxReportLengthBytes)
540+
assert.Equal(t, uint32(10), dt.MaxReportCount)
541+
assert.Equal(t, uint32(50), dt.MaxBatchSize)
542+
assert.Equal(t, int64(100), dt.MinTimeIncrease)
543+
assert.Equal(t, 10*time.Minute, dt.ExecutionRemovalTime)
544+
}
545+
447546
// setupCapabilitiesRegistryWithMCMS sets up a test environment with MCMS infrastructure
448547
func setupCapabilitiesRegistryWithMCMS(t *testing.T) *testFixture {
449548
selector := chainselectors.TEST_90000001.Selector

deployment/cre/ocr3/config.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,14 @@ func getOffchainCfg(oracleCfg OracleConfig) (offchainConfig, error) {
231231
result = oracleCfg.ChainCapOffchainConfig
232232
}
233233

234+
if oracleCfg.DontimeOffchainConfig != nil {
235+
if result != nil {
236+
return nil, fmt.Errorf("multiple offchain configs specified: %+v. Only one allowed", oracleCfg)
237+
}
238+
239+
result = oracleCfg.DontimeOffchainConfig
240+
}
241+
234242
return result, nil
235243
}
236244

deployment/cre/ocr3/config_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,3 +196,60 @@ func loadTestData(t *testing.T, path string) []deployment.Node {
196196
require.Len(t, nodes, 10)
197197
return nodes
198198
}
199+
200+
func Test_getOffchainCfg(t *testing.T) {
201+
t.Run("nil when no offchain config set", func(t *testing.T) {
202+
cfg := OracleConfig{}
203+
got, err := getOffchainCfg(cfg)
204+
require.NoError(t, err)
205+
require.Nil(t, got)
206+
})
207+
t.Run("returns ConsensusCapOffchainConfig", func(t *testing.T) {
208+
cfg := OracleConfig{
209+
ConsensusCapOffchainConfig: &ConsensusCapOffchainConfig{MaxBatchSize: 1},
210+
}
211+
got, err := getOffchainCfg(cfg)
212+
require.NoError(t, err)
213+
require.Equal(t, cfg.ConsensusCapOffchainConfig, got)
214+
})
215+
t.Run("returns ChainCapOffchainConfig", func(t *testing.T) {
216+
cfg := OracleConfig{
217+
ChainCapOffchainConfig: &ChainCapOffchainConfig{MaxBatchSize: 2},
218+
}
219+
got, err := getOffchainCfg(cfg)
220+
require.NoError(t, err)
221+
require.Equal(t, cfg.ChainCapOffchainConfig, got)
222+
})
223+
t.Run("returns DontimeOffchainConfig", func(t *testing.T) {
224+
cfg := OracleConfig{
225+
DontimeOffchainConfig: &DontimeOffchainConfig{MaxBatchSize: 3},
226+
}
227+
got, err := getOffchainCfg(cfg)
228+
require.NoError(t, err)
229+
require.Equal(t, cfg.DontimeOffchainConfig, got)
230+
})
231+
t.Run("error when ConsensusCapOffchainConfig and DontimeOffchainConfig both set", func(t *testing.T) {
232+
cfg := OracleConfig{
233+
ConsensusCapOffchainConfig: &ConsensusCapOffchainConfig{MaxBatchSize: 1},
234+
DontimeOffchainConfig: &DontimeOffchainConfig{MaxBatchSize: 3},
235+
}
236+
_, err := getOffchainCfg(cfg)
237+
require.ErrorContains(t, err, "multiple offchain configs specified")
238+
})
239+
t.Run("error when ChainCapOffchainConfig and DontimeOffchainConfig both set", func(t *testing.T) {
240+
cfg := OracleConfig{
241+
ChainCapOffchainConfig: &ChainCapOffchainConfig{MaxBatchSize: 2},
242+
DontimeOffchainConfig: &DontimeOffchainConfig{MaxBatchSize: 3},
243+
}
244+
_, err := getOffchainCfg(cfg)
245+
require.ErrorContains(t, err, "multiple offchain configs specified")
246+
})
247+
t.Run("error when ConsensusCapOffchainConfig and ChainCapOffchainConfig both set", func(t *testing.T) {
248+
cfg := OracleConfig{
249+
ConsensusCapOffchainConfig: &ConsensusCapOffchainConfig{MaxBatchSize: 1},
250+
ChainCapOffchainConfig: &ChainCapOffchainConfig{MaxBatchSize: 2},
251+
}
252+
_, err := getOffchainCfg(cfg)
253+
require.ErrorContains(t, err, "multiple offchain configs specified")
254+
})
255+
}

deployment/cre/ocr3/oracle_config.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
capocr3types "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3/types"
1313
evmcapocr3types "github.com/smartcontractkit/chainlink-common/pkg/capabilities/v2/chain-capabilities/consensus/ocr3/types"
14+
dontimepb "github.com/smartcontractkit/chainlink-common/pkg/workflows/dontime/pb"
1415
)
1516

1617
type OracleConfig struct {
@@ -34,6 +35,7 @@ type OracleConfig struct {
3435

3536
ConsensusCapOffchainConfig *ConsensusCapOffchainConfig
3637
ChainCapOffchainConfig *ChainCapOffchainConfig
38+
DontimeOffchainConfig *DontimeOffchainConfig
3739
}
3840

3941
func (oc *OracleConfig) UnmarshalJSON(data []byte) error {
@@ -170,3 +172,67 @@ func (oc *ChainCapOffchainConfig) ToProto() (proto.Message, error) {
170172
MaxBatchSize: oc.MaxBatchSize,
171173
}, nil
172174
}
175+
176+
type DontimeOffchainConfig struct {
177+
MaxQueryLengthBytes uint32
178+
MaxObservationLengthBytes uint32
179+
MaxOutcomeLengthBytes uint32
180+
MaxReportLengthBytes uint32
181+
MaxReportCount uint32
182+
MaxBatchSize uint32
183+
MinTimeIncrease int64
184+
ExecutionRemovalTime time.Duration
185+
}
186+
187+
func (oc *DontimeOffchainConfig) UnmarshalJSON(data []byte) error {
188+
type aliasT DontimeOffchainConfig
189+
temp := &struct {
190+
ExecutionRemovalTime string `json:"ExecutionRemovalTime"`
191+
*aliasT
192+
}{
193+
aliasT: (*aliasT)(oc),
194+
}
195+
if err := json.Unmarshal(data, temp); err != nil {
196+
return fmt.Errorf("failed to unmarshal DontimeOffchainConfig: %w", err)
197+
}
198+
199+
if temp.ExecutionRemovalTime == "" {
200+
oc.ExecutionRemovalTime = 0
201+
} else {
202+
d, err := time.ParseDuration(temp.ExecutionRemovalTime)
203+
if err != nil {
204+
return fmt.Errorf("failed to parse ExecutionRemovalTime: %w", err)
205+
}
206+
oc.ExecutionRemovalTime = d
207+
}
208+
209+
return nil
210+
}
211+
212+
func (oc *DontimeOffchainConfig) MarshalJSON() ([]byte, error) {
213+
type aliasT DontimeOffchainConfig
214+
return json.Marshal(&struct {
215+
ExecutionRemovalTime string `json:"ExecutionRemovalTime"`
216+
*aliasT
217+
}{
218+
ExecutionRemovalTime: oc.ExecutionRemovalTime.String(),
219+
aliasT: (*aliasT)(oc),
220+
})
221+
}
222+
223+
func (oc *DontimeOffchainConfig) ToProto() (proto.Message, error) {
224+
var execRemovalTime *durationpb.Duration
225+
if oc.ExecutionRemovalTime > 0 {
226+
execRemovalTime = durationpb.New(oc.ExecutionRemovalTime)
227+
}
228+
return &dontimepb.Config{
229+
MaxQueryLengthBytes: oc.MaxQueryLengthBytes,
230+
MaxObservationLengthBytes: oc.MaxObservationLengthBytes,
231+
MaxOutcomeLengthBytes: oc.MaxOutcomeLengthBytes,
232+
MaxReportLengthBytes: oc.MaxReportLengthBytes,
233+
MaxReportCount: oc.MaxReportCount,
234+
MaxBatchSize: oc.MaxBatchSize,
235+
MinTimeIncrease: oc.MinTimeIncrease,
236+
ExecutionRemovalTime: execRemovalTime,
237+
}, nil
238+
}

deployment/cre/ocr3/oracle_config_test.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@ import (
55
"testing"
66
"time"
77

8+
"github.com/stretchr/testify/assert"
89
"github.com/stretchr/testify/require"
10+
"google.golang.org/protobuf/types/known/durationpb"
911
"gopkg.in/yaml.v3"
12+
13+
dontimepb "github.com/smartcontractkit/chainlink-common/pkg/workflows/dontime/pb"
1014
)
1115

1216
func TestOracleConfig_JSON(t *testing.T) {
@@ -48,6 +52,45 @@ func TestOracleConfig_JSON(t *testing.T) {
4852
asJSON, err := json.Marshal(cfg)
4953
require.NoError(t, err)
5054

55+
var fromJSON OracleConfig
56+
err = json.Unmarshal(asJSON, &fromJSON)
57+
require.NoError(t, err)
58+
require.Equal(t, cfg, fromJSON)
59+
})
60+
t.Run("Dontime OCR Config", func(t *testing.T) {
61+
var cfg OracleConfig
62+
err := json.Unmarshal([]byte(dontimeOcr3Cfg), &cfg)
63+
require.NoError(t, err)
64+
require.Equal(t, 2, cfg.MaxFaultyOracles)
65+
dt := cfg.DontimeOffchainConfig
66+
require.NotNil(t, dt)
67+
require.Equal(t, uint32(500000), dt.MaxQueryLengthBytes)
68+
require.Equal(t, uint32(500000), dt.MaxObservationLengthBytes)
69+
require.Equal(t, uint32(500000), dt.MaxOutcomeLengthBytes)
70+
require.Equal(t, uint32(500000), dt.MaxReportLengthBytes)
71+
require.Equal(t, uint32(10), dt.MaxReportCount)
72+
require.Equal(t, uint32(50), dt.MaxBatchSize)
73+
require.Equal(t, int64(100), dt.MinTimeIncrease)
74+
require.Equal(t, 10*time.Minute, dt.ExecutionRemovalTime)
75+
76+
asJSON, err := json.Marshal(cfg)
77+
require.NoError(t, err)
78+
var cfg2 OracleConfig
79+
err = json.Unmarshal(asJSON, &cfg2)
80+
require.NoError(t, err)
81+
require.Equal(t, cfg, cfg2)
82+
})
83+
t.Run("Dontime OCR Config with zero ExecutionRemovalTime", func(t *testing.T) {
84+
cfg := OracleConfig{
85+
DeltaProgressMillis: 3000,
86+
DontimeOffchainConfig: &DontimeOffchainConfig{
87+
MaxBatchSize: 25,
88+
MinTimeIncrease: 50,
89+
},
90+
}
91+
asJSON, err := json.Marshal(cfg)
92+
require.NoError(t, err)
93+
5194
var fromJSON OracleConfig
5295
err = json.Unmarshal(asJSON, &fromJSON)
5396
require.NoError(t, err)
@@ -107,3 +150,70 @@ var legacyOcr3Cfg = `
107150
"MaxDurationShouldTransmitMillis": 1000,
108151
"MaxFaultyOracles": 3
109152
}`
153+
154+
var dontimeOcr3Cfg = `
155+
{
156+
"DontimeOffchainConfig": {
157+
"MaxQueryLengthBytes": 500000,
158+
"MaxObservationLengthBytes": 500000,
159+
"MaxOutcomeLengthBytes": 500000,
160+
"MaxReportLengthBytes": 500000,
161+
"MaxReportCount": 10,
162+
"MaxBatchSize": 50,
163+
"MinTimeIncrease": 100,
164+
"ExecutionRemovalTime": "10m"
165+
},
166+
"UniqueReports": true,
167+
"DeltaProgressMillis": 5000,
168+
"DeltaResendMillis": 5000,
169+
"DeltaInitialMillis": 5000,
170+
"DeltaRoundMillis": 2000,
171+
"DeltaGraceMillis": 500,
172+
"DeltaCertifiedCommitRequestMillis": 1000,
173+
"DeltaStageMillis": 30000,
174+
"MaxRoundsPerEpoch": 10,
175+
"TransmissionSchedule": [7],
176+
"MaxDurationQueryMillis": 1000,
177+
"MaxDurationObservationMillis": 1000,
178+
"MaxDurationShouldAcceptMillis": 1000,
179+
"MaxDurationShouldTransmitMillis": 1000,
180+
"MaxFaultyOracles": 2
181+
}`
182+
183+
func TestDontimeOffchainConfig_ToProto(t *testing.T) {
184+
t.Run("all fields set", func(t *testing.T) {
185+
cfg := &DontimeOffchainConfig{
186+
MaxQueryLengthBytes: 100,
187+
MaxObservationLengthBytes: 200,
188+
MaxOutcomeLengthBytes: 300,
189+
MaxReportLengthBytes: 400,
190+
MaxReportCount: 5,
191+
MaxBatchSize: 10,
192+
MinTimeIncrease: 42,
193+
ExecutionRemovalTime: 5 * time.Minute,
194+
}
195+
msg, err := cfg.ToProto()
196+
require.NoError(t, err)
197+
pb, ok := msg.(*dontimepb.Config)
198+
require.True(t, ok)
199+
assert.Equal(t, uint32(100), pb.MaxQueryLengthBytes)
200+
assert.Equal(t, uint32(200), pb.MaxObservationLengthBytes)
201+
assert.Equal(t, uint32(300), pb.MaxOutcomeLengthBytes)
202+
assert.Equal(t, uint32(400), pb.MaxReportLengthBytes)
203+
assert.Equal(t, uint32(5), pb.MaxReportCount)
204+
assert.Equal(t, uint32(10), pb.MaxBatchSize)
205+
assert.Equal(t, int64(42), pb.MinTimeIncrease)
206+
assert.Equal(t, durationpb.New(5*time.Minute), pb.ExecutionRemovalTime)
207+
})
208+
t.Run("zero ExecutionRemovalTime yields nil", func(t *testing.T) {
209+
cfg := &DontimeOffchainConfig{
210+
MaxBatchSize: 10,
211+
}
212+
msg, err := cfg.ToProto()
213+
require.NoError(t, err)
214+
pb, ok := msg.(*dontimepb.Config)
215+
require.True(t, ok)
216+
assert.Nil(t, pb.ExecutionRemovalTime)
217+
assert.Equal(t, uint32(10), pb.MaxBatchSize)
218+
})
219+
}

0 commit comments

Comments
 (0)