Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,105 @@ dons:
assert.Equal(t, 40*time.Second, oc.ConsensusCapOffchainConfig.RequestTimeout)
}

func TestConfigureCapabilitiesRegistryInput_YAMLFromFile_DontimeConfig(t *testing.T) {
yamlConfig := `
chainSelector: 421614
capabilitiesRegistryAddress: "0x1234567890123456789012345678901234567890"
nops:
- admin: "0x1111111111111111111111111111111111111111"
name: "Node Operator Alpha"
capabilities:
- capabilityID: "dontime@1.0.0"
configurationContract: "0x0000000000000000000000000000000000000000"
metadata:
capabilityType: 2
responseType: 0
nodes:
- nop: "test-nop"
signer: ` + signer1 + `
p2pID: ` + p2pID1 + `
encryptionPublicKey: ` + encryptionPublicKey + `
csaKey: ` + csaKey + `
capabilityIDs: ["dontime@1.0.0"]
dons:
- name: "dontime-don"
donFamilies: ["dontime"]
config:
defaultConfig: {}
capabilityConfigurations:
- capabilityID: "dontime@1.0.0"
config:
ocr3Configs:
__default__:
deltaProgressMillis: 3000
deltaResendMillis: 5000
deltaInitialMillis: 500
deltaRoundMillis: 500
deltaGraceMillis: 200
deltaCertifiedCommitRequestMillis: 1000
deltaStageMillis: 30000
maxRoundsPerEpoch: 10
transmissionSchedule: [7]
maxFaultyOracles: 2
uniqueReports: true
maxDurationQueryMillis: 500
maxDurationObservationMillis: 500
maxDurationShouldAcceptMillis: 500
maxDurationShouldTransmitMillis: 500
dontimeOffchainConfig:
maxQueryLengthBytes: 500000
maxObservationLengthBytes: 500000
maxOutcomeLengthBytes: 500000
maxReportLengthBytes: 500000
maxReportCount: 10
maxBatchSize: 50
minTimeIncrease: 100
executionRemovalTime: "10m"
nodes: [` + nodeID1 + `]
f: 1
isPublic: true
acceptsWorkflows: false
`

var input changeset.ConfigureCapabilitiesRegistryInput
err := yaml.Unmarshal([]byte(yamlConfig), &input)
require.NoError(t, err, "should be able to parse YAML config with dontime offchain config")

assert.Equal(t, uint64(421614), input.ChainSelector)
require.Len(t, input.DONs, 1)
assert.Equal(t, "dontime-don", input.DONs[0].Name)

require.Len(t, input.DONs[0].CapabilityConfigurations, 1)
assert.Equal(t, "dontime@1.0.0", input.DONs[0].CapabilityConfigurations[0].CapabilityID)

ocr3Configs, ok := input.DONs[0].CapabilityConfigurations[0].Config["ocr3Configs"].(map[string]any)
require.True(t, ok, "ocr3Configs should be a map")
defaultEntry, ok := ocr3Configs["__default__"]
require.True(t, ok, "__default__ key should exist in ocr3Configs")

ocrJSON, err := json.Marshal(defaultEntry)
require.NoError(t, err)
var oc ocr3.OracleConfig
require.NoError(t, json.Unmarshal(ocrJSON, &oc))

assert.Equal(t, uint32(3000), oc.DeltaProgressMillis)
assert.Equal(t, 2, oc.MaxFaultyOracles)
assert.True(t, oc.UniqueReports)
assert.Nil(t, oc.ConsensusCapOffchainConfig, "consensusCapOffchainConfig should be nil")
assert.Nil(t, oc.ChainCapOffchainConfig, "chainCapOffchainConfig should be nil")

require.NotNil(t, oc.DontimeOffchainConfig, "dontimeOffchainConfig should be parsed")
dt := oc.DontimeOffchainConfig
assert.Equal(t, uint32(500000), dt.MaxQueryLengthBytes)
assert.Equal(t, uint32(500000), dt.MaxObservationLengthBytes)
assert.Equal(t, uint32(500000), dt.MaxOutcomeLengthBytes)
assert.Equal(t, uint32(500000), dt.MaxReportLengthBytes)
assert.Equal(t, uint32(10), dt.MaxReportCount)
assert.Equal(t, uint32(50), dt.MaxBatchSize)
assert.Equal(t, int64(100), dt.MinTimeIncrease)
assert.Equal(t, 10*time.Minute, dt.ExecutionRemovalTime)
}

// setupCapabilitiesRegistryWithMCMS sets up a test environment with MCMS infrastructure
func setupCapabilitiesRegistryWithMCMS(t *testing.T) *testFixture {
selector := chainselectors.TEST_90000001.Selector
Expand Down
8 changes: 8 additions & 0 deletions deployment/cre/ocr3/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,14 @@ func getOffchainCfg(oracleCfg OracleConfig) (offchainConfig, error) {
result = oracleCfg.ChainCapOffchainConfig
}

if oracleCfg.DontimeOffchainConfig != nil {
if result != nil {
return nil, fmt.Errorf("multiple offchain configs specified: %+v. Only one allowed", oracleCfg)
}

result = oracleCfg.DontimeOffchainConfig
}

return result, nil
}

Expand Down
57 changes: 57 additions & 0 deletions deployment/cre/ocr3/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,60 @@ func loadTestData(t *testing.T, path string) []deployment.Node {
require.Len(t, nodes, 10)
return nodes
}

func Test_getOffchainCfg(t *testing.T) {
t.Run("nil when no offchain config set", func(t *testing.T) {
cfg := OracleConfig{}
got, err := getOffchainCfg(cfg)
require.NoError(t, err)
require.Nil(t, got)
})
t.Run("returns ConsensusCapOffchainConfig", func(t *testing.T) {
cfg := OracleConfig{
ConsensusCapOffchainConfig: &ConsensusCapOffchainConfig{MaxBatchSize: 1},
}
got, err := getOffchainCfg(cfg)
require.NoError(t, err)
require.Equal(t, cfg.ConsensusCapOffchainConfig, got)
})
t.Run("returns ChainCapOffchainConfig", func(t *testing.T) {
cfg := OracleConfig{
ChainCapOffchainConfig: &ChainCapOffchainConfig{MaxBatchSize: 2},
}
got, err := getOffchainCfg(cfg)
require.NoError(t, err)
require.Equal(t, cfg.ChainCapOffchainConfig, got)
})
t.Run("returns DontimeOffchainConfig", func(t *testing.T) {
cfg := OracleConfig{
DontimeOffchainConfig: &DontimeOffchainConfig{MaxBatchSize: 3},
}
got, err := getOffchainCfg(cfg)
require.NoError(t, err)
require.Equal(t, cfg.DontimeOffchainConfig, got)
})
t.Run("error when ConsensusCapOffchainConfig and DontimeOffchainConfig both set", func(t *testing.T) {
cfg := OracleConfig{
ConsensusCapOffchainConfig: &ConsensusCapOffchainConfig{MaxBatchSize: 1},
DontimeOffchainConfig: &DontimeOffchainConfig{MaxBatchSize: 3},
}
_, err := getOffchainCfg(cfg)
require.ErrorContains(t, err, "multiple offchain configs specified")
})
t.Run("error when ChainCapOffchainConfig and DontimeOffchainConfig both set", func(t *testing.T) {
cfg := OracleConfig{
ChainCapOffchainConfig: &ChainCapOffchainConfig{MaxBatchSize: 2},
DontimeOffchainConfig: &DontimeOffchainConfig{MaxBatchSize: 3},
}
_, err := getOffchainCfg(cfg)
require.ErrorContains(t, err, "multiple offchain configs specified")
})
t.Run("error when ConsensusCapOffchainConfig and ChainCapOffchainConfig both set", func(t *testing.T) {
cfg := OracleConfig{
ConsensusCapOffchainConfig: &ConsensusCapOffchainConfig{MaxBatchSize: 1},
ChainCapOffchainConfig: &ChainCapOffchainConfig{MaxBatchSize: 2},
}
_, err := getOffchainCfg(cfg)
require.ErrorContains(t, err, "multiple offchain configs specified")
})
}
66 changes: 66 additions & 0 deletions deployment/cre/ocr3/oracle_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

capocr3types "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3/types"
evmcapocr3types "github.com/smartcontractkit/chainlink-common/pkg/capabilities/v2/chain-capabilities/consensus/ocr3/types"
dontimepb "github.com/smartcontractkit/chainlink-common/pkg/workflows/dontime/pb"
)

type OracleConfig struct {
Expand All @@ -34,6 +35,7 @@ type OracleConfig struct {

ConsensusCapOffchainConfig *ConsensusCapOffchainConfig
ChainCapOffchainConfig *ChainCapOffchainConfig
DontimeOffchainConfig *DontimeOffchainConfig
}

func (oc *OracleConfig) UnmarshalJSON(data []byte) error {
Expand Down Expand Up @@ -170,3 +172,67 @@ func (oc *ChainCapOffchainConfig) ToProto() (proto.Message, error) {
MaxBatchSize: oc.MaxBatchSize,
}, nil
}

type DontimeOffchainConfig struct {
MaxQueryLengthBytes uint32
MaxObservationLengthBytes uint32
MaxOutcomeLengthBytes uint32
MaxReportLengthBytes uint32
MaxReportCount uint32
MaxBatchSize uint32
MinTimeIncrease int64
ExecutionRemovalTime time.Duration
}

func (oc *DontimeOffchainConfig) UnmarshalJSON(data []byte) error {
type aliasT DontimeOffchainConfig
temp := &struct {
ExecutionRemovalTime string `json:"ExecutionRemovalTime"`
*aliasT
}{
aliasT: (*aliasT)(oc),
}
if err := json.Unmarshal(data, temp); err != nil {
return fmt.Errorf("failed to unmarshal DontimeOffchainConfig: %w", err)
}

if temp.ExecutionRemovalTime == "" {
oc.ExecutionRemovalTime = 0
} else {
d, err := time.ParseDuration(temp.ExecutionRemovalTime)
if err != nil {
return fmt.Errorf("failed to parse ExecutionRemovalTime: %w", err)
}
oc.ExecutionRemovalTime = d
}

return nil
}

func (oc *DontimeOffchainConfig) MarshalJSON() ([]byte, error) {
type aliasT DontimeOffchainConfig
return json.Marshal(&struct {
ExecutionRemovalTime string `json:"ExecutionRemovalTime"`
*aliasT
}{
ExecutionRemovalTime: oc.ExecutionRemovalTime.String(),
aliasT: (*aliasT)(oc),
})
}

func (oc *DontimeOffchainConfig) ToProto() (proto.Message, error) {
var execRemovalTime *durationpb.Duration
if oc.ExecutionRemovalTime > 0 {
execRemovalTime = durationpb.New(oc.ExecutionRemovalTime)
}
return &dontimepb.Config{
MaxQueryLengthBytes: oc.MaxQueryLengthBytes,
MaxObservationLengthBytes: oc.MaxObservationLengthBytes,
MaxOutcomeLengthBytes: oc.MaxOutcomeLengthBytes,
MaxReportLengthBytes: oc.MaxReportLengthBytes,
MaxReportCount: oc.MaxReportCount,
MaxBatchSize: oc.MaxBatchSize,
MinTimeIncrease: oc.MinTimeIncrease,
ExecutionRemovalTime: execRemovalTime,
}, nil
}
110 changes: 110 additions & 0 deletions deployment/cre/ocr3/oracle_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ import (
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/durationpb"
"gopkg.in/yaml.v3"

dontimepb "github.com/smartcontractkit/chainlink-common/pkg/workflows/dontime/pb"
)

func TestOracleConfig_JSON(t *testing.T) {
Expand Down Expand Up @@ -48,6 +52,45 @@ func TestOracleConfig_JSON(t *testing.T) {
asJSON, err := json.Marshal(cfg)
require.NoError(t, err)

var fromJSON OracleConfig
err = json.Unmarshal(asJSON, &fromJSON)
require.NoError(t, err)
require.Equal(t, cfg, fromJSON)
})
t.Run("Dontime OCR Config", func(t *testing.T) {
var cfg OracleConfig
err := json.Unmarshal([]byte(dontimeOcr3Cfg), &cfg)
require.NoError(t, err)
require.Equal(t, 2, cfg.MaxFaultyOracles)
dt := cfg.DontimeOffchainConfig
require.NotNil(t, dt)
require.Equal(t, uint32(500000), dt.MaxQueryLengthBytes)
require.Equal(t, uint32(500000), dt.MaxObservationLengthBytes)
require.Equal(t, uint32(500000), dt.MaxOutcomeLengthBytes)
require.Equal(t, uint32(500000), dt.MaxReportLengthBytes)
require.Equal(t, uint32(10), dt.MaxReportCount)
require.Equal(t, uint32(50), dt.MaxBatchSize)
require.Equal(t, int64(100), dt.MinTimeIncrease)
require.Equal(t, 10*time.Minute, dt.ExecutionRemovalTime)

asJSON, err := json.Marshal(cfg)
require.NoError(t, err)
var cfg2 OracleConfig
err = json.Unmarshal(asJSON, &cfg2)
require.NoError(t, err)
require.Equal(t, cfg, cfg2)
})
t.Run("Dontime OCR Config with zero ExecutionRemovalTime", func(t *testing.T) {
cfg := OracleConfig{
DeltaProgressMillis: 3000,
DontimeOffchainConfig: &DontimeOffchainConfig{
MaxBatchSize: 25,
MinTimeIncrease: 50,
},
}
asJSON, err := json.Marshal(cfg)
require.NoError(t, err)

var fromJSON OracleConfig
err = json.Unmarshal(asJSON, &fromJSON)
require.NoError(t, err)
Expand Down Expand Up @@ -107,3 +150,70 @@ var legacyOcr3Cfg = `
"MaxDurationShouldTransmitMillis": 1000,
"MaxFaultyOracles": 3
}`

var dontimeOcr3Cfg = `
{
"DontimeOffchainConfig": {
"MaxQueryLengthBytes": 500000,
"MaxObservationLengthBytes": 500000,
"MaxOutcomeLengthBytes": 500000,
"MaxReportLengthBytes": 500000,
"MaxReportCount": 10,
"MaxBatchSize": 50,
"MinTimeIncrease": 100,
"ExecutionRemovalTime": "10m"
},
"UniqueReports": true,
"DeltaProgressMillis": 5000,
"DeltaResendMillis": 5000,
"DeltaInitialMillis": 5000,
"DeltaRoundMillis": 2000,
"DeltaGraceMillis": 500,
"DeltaCertifiedCommitRequestMillis": 1000,
"DeltaStageMillis": 30000,
"MaxRoundsPerEpoch": 10,
"TransmissionSchedule": [7],
"MaxDurationQueryMillis": 1000,
"MaxDurationObservationMillis": 1000,
"MaxDurationShouldAcceptMillis": 1000,
"MaxDurationShouldTransmitMillis": 1000,
"MaxFaultyOracles": 2
}`

func TestDontimeOffchainConfig_ToProto(t *testing.T) {
t.Run("all fields set", func(t *testing.T) {
cfg := &DontimeOffchainConfig{
MaxQueryLengthBytes: 100,
MaxObservationLengthBytes: 200,
MaxOutcomeLengthBytes: 300,
MaxReportLengthBytes: 400,
MaxReportCount: 5,
MaxBatchSize: 10,
MinTimeIncrease: 42,
ExecutionRemovalTime: 5 * time.Minute,
}
msg, err := cfg.ToProto()
require.NoError(t, err)
pb, ok := msg.(*dontimepb.Config)
require.True(t, ok)
assert.Equal(t, uint32(100), pb.MaxQueryLengthBytes)
assert.Equal(t, uint32(200), pb.MaxObservationLengthBytes)
assert.Equal(t, uint32(300), pb.MaxOutcomeLengthBytes)
assert.Equal(t, uint32(400), pb.MaxReportLengthBytes)
assert.Equal(t, uint32(5), pb.MaxReportCount)
assert.Equal(t, uint32(10), pb.MaxBatchSize)
assert.Equal(t, int64(42), pb.MinTimeIncrease)
assert.Equal(t, durationpb.New(5*time.Minute), pb.ExecutionRemovalTime)
})
t.Run("zero ExecutionRemovalTime yields nil", func(t *testing.T) {
cfg := &DontimeOffchainConfig{
MaxBatchSize: 10,
}
msg, err := cfg.ToProto()
require.NoError(t, err)
pb, ok := msg.(*dontimepb.Config)
require.True(t, ok)
assert.Nil(t, pb.ExecutionRemovalTime)
assert.Equal(t, uint32(10), pb.MaxBatchSize)
})
}
Loading