Skip to content

Commit

Permalink
core/capabilities/ccip: use OCR offchain config (#1264)
Browse files Browse the repository at this point in the history
We want to define and use the appropriate OCR offchain config for each
plugin.

Requires smartcontractkit/chainlink-ccip#36
  • Loading branch information
makramkd authored and asoliman92 committed Aug 7, 2024
1 parent 371e5f1 commit 3fcaec9
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 65 deletions.
136 changes: 93 additions & 43 deletions core/capabilities/ccip/ccip_integration_tests/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ import (
"encoding/hex"
"math/big"
"sort"
"strconv"
"testing"
"time"

"github.com/smartcontractkit/chainlink-ccip/chainconfig"
"github.com/smartcontractkit/chainlink-ccip/pluginconfig"
commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config"
"github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3"
"github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/ccip_integration_tests/integrationhelpers"
cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types"

Expand Down Expand Up @@ -53,9 +56,27 @@ const (
CapabilityLabelledName = "ccip"
CapabilityVersion = "v1.0.0"
NodeOperatorID = 1
// TODO: this is 8 hours now to match what is hardcoded in the exec plugin.
// Eventually this should drive what is set in the exec plugin.
FirstBlockAge = 8 * time.Hour

// These constants drive what is set in the plugin offchain configs.
FirstBlockAge = 8 * time.Hour
RemoteGasPriceBatchWriteFrequency = 30 * time.Minute
BatchGasLimit = 6_500_000
RelativeBoostPerWaitHour = 1.5
InflightCacheExpiry = 10 * time.Minute
RootSnoozeTime = 30 * time.Minute
BatchingStrategyID = 0
DeltaProgress = 30 * time.Second
DeltaResend = 10 * time.Second
DeltaInitial = 20 * time.Second
DeltaRound = 2 * time.Second
DeltaGrace = 2 * time.Second
DeltaCertifiedCommitRequest = 10 * time.Second
DeltaStage = 10 * time.Second
Rmax = 3
MaxDurationQuery = 50 * time.Millisecond
MaxDurationObservation = 5 * time.Second
MaxDurationShouldAcceptAttestedReport = 10 * time.Second
MaxDurationShouldTransmitAcceptedReport = 10 * time.Second
)

func e18Mult(amount uint64) *big.Int {
Expand Down Expand Up @@ -412,11 +433,18 @@ func AddChainConfig(
// Need to sort, otherwise _checkIsValidUniqueSubset onChain will fail
sortP2PIDS(p2pIDs)
// First Add ChainConfig that includes all p2pIDs as readers
chainConfig := integrationhelpers.SetupConfigInfo(chainSelector, p2pIDs, f, []byte(strconv.FormatUint(chainSelector, 10)))
encodedExtraChainConfig, err := chainconfig.EncodeChainConfig(chainconfig.ChainConfig{
GasPriceDeviationPPB: ccipocr3.NewBigIntFromInt64(1000),
DAGasPriceDeviationPPB: ccipocr3.NewBigIntFromInt64(0),
FinalityDepth: 10,
OptimisticConfirmations: 1,
})
require.NoError(t, err)
chainConfig := integrationhelpers.SetupConfigInfo(chainSelector, p2pIDs, f, encodedExtraChainConfig)
inputConfig := []ccip_config.CCIPConfigTypesChainConfigInfo{
chainConfig,
}
_, err := h.ccipConfig.ApplyChainConfigUpdates(h.owner, nil, inputConfig)
_, err = h.ccipConfig.ApplyChainConfigUpdates(h.owner, nil, inputConfig)
require.NoError(t, err)
h.backend.Commit()
return chainConfig
Expand All @@ -437,49 +465,71 @@ func (h *homeChain) AddDON(
for range oracles {
schedule = append(schedule, 1)
}
signers, transmitters, f, _, offchainConfigVersion, offchainConfig, err := ocr3confighelper.ContractSetConfigArgsForTests(
30*time.Second, // deltaProgress
10*time.Second, // deltaResend
20*time.Second, // deltaInitial
2*time.Second, // deltaRound
2*time.Second, // deltaGrace
10*time.Second, // deltaCertifiedCommitRequest TODO: whats a good value for this?
10*time.Second, // deltaStage
3, // rmax
schedule,
oracles,
[]byte{}, // empty offchain config
50*time.Millisecond, // maxDurationQuery
5*time.Second, // maxDurationObservation
10*time.Second, // maxDurationShouldAcceptAttestedReport
10*time.Second, // maxDurationShouldTransmitAcceptedReport
int(f),
[]byte{}) // empty OnChainConfig
require.NoError(t, err, "failed to create contract config")

tabi, err := ocr3_config_encoder.IOCR3ConfigEncoderMetaData.GetAbi()
require.NoError(t, err)

signersBytes := make([][]byte, len(signers))
for i, signer := range signers {
signersBytes[i] = signer
}

transmittersBytes := make([][]byte, len(transmitters))
for i, transmitter := range transmitters {
// anotherErr because linting doesn't want to shadow err
parsed, anotherErr := common.ParseHexOrString(string(transmitter))
require.NoError(t, anotherErr)
transmittersBytes[i] = parsed
}

// Add DON on capability registry contract
var ocr3Configs []ocr3_config_encoder.CCIPConfigTypesOCR3Config
for _, pluginType := range []cctypes.PluginType{cctypes.PluginTypeCCIPCommit, cctypes.PluginTypeCCIPExec} {
var encodedOffchainConfig []byte
var err2 error
if pluginType == cctypes.PluginTypeCCIPCommit {
encodedOffchainConfig, err2 = pluginconfig.EncodeCommitOffchainConfig(pluginconfig.CommitOffchainConfig{
RemoteGasPriceBatchWriteFrequency: *commonconfig.MustNewDuration(RemoteGasPriceBatchWriteFrequency),
// TODO: implement token price writes
// TokenPriceBatchWriteFrequency: *commonconfig.MustNewDuration(tokenPriceBatchWriteFrequency),
})
require.NoError(t, err2)
} else {
encodedOffchainConfig, err2 = pluginconfig.EncodeExecuteOffchainConfig(pluginconfig.ExecuteOffchainConfig{
BatchGasLimit: BatchGasLimit,
RelativeBoostPerWaitHour: RelativeBoostPerWaitHour,
MessageVisibilityInterval: *commonconfig.MustNewDuration(FirstBlockAge),
InflightCacheExpiry: *commonconfig.MustNewDuration(InflightCacheExpiry),
RootSnoozeTime: *commonconfig.MustNewDuration(RootSnoozeTime),
BatchingStrategyID: BatchingStrategyID,
})
require.NoError(t, err2)
}
signers, transmitters, configF, _, offchainConfigVersion, offchainConfig, err2 := ocr3confighelper.ContractSetConfigArgsForTests(
DeltaProgress,
DeltaResend,
DeltaInitial,
DeltaRound,
DeltaGrace,
DeltaCertifiedCommitRequest,
DeltaStage,
Rmax,
schedule,
oracles,
encodedOffchainConfig,
MaxDurationQuery,
MaxDurationObservation,
MaxDurationShouldAcceptAttestedReport,
MaxDurationShouldTransmitAcceptedReport,
int(f),
[]byte{}, // empty OnChainConfig
)
require.NoError(t, err2, "failed to create contract config")

signersBytes := make([][]byte, len(signers))
for i, signer := range signers {
signersBytes[i] = signer
}

transmittersBytes := make([][]byte, len(transmitters))
for i, transmitter := range transmitters {
// anotherErr because linting doesn't want to shadow err
parsed, anotherErr := common.ParseHexOrString(string(transmitter))
require.NoError(t, anotherErr)
transmittersBytes[i] = parsed
}

ocr3Configs = append(ocr3Configs, ocr3_config_encoder.CCIPConfigTypesOCR3Config{
PluginType: uint8(pluginType),
ChainSelector: chainSelector,
F: f,
F: configF,
OffchainConfigVersion: offchainConfigVersion,
OfframpAddress: uni.offramp.Address().Bytes(),
BootstrapP2PIds: [][32]byte{bootstrapP2PID},
Expand Down Expand Up @@ -522,13 +572,13 @@ func (h *homeChain) AddDON(
require.NotZero(t, donID, "failed to get donID from config set event")

var signerAddresses []common.Address
for _, signer := range signers {
signerAddresses = append(signerAddresses, common.BytesToAddress(signer))
for _, oracle := range oracles {
signerAddresses = append(signerAddresses, common.BytesToAddress(oracle.OnchainPublicKey))
}

var transmitterAddresses []common.Address
for _, transmitter := range transmitters {
transmitterAddresses = append(transmitterAddresses, common.HexToAddress(string(transmitter)))
for _, oracle := range oracles {
transmitterAddresses = append(transmitterAddresses, common.HexToAddress(string(oracle.TransmitAccount)))
}

// get the config digest from the ccip config contract and set config on the offramp.
Expand Down
23 changes: 19 additions & 4 deletions core/capabilities/ccip/ccip_integration_tests/home_chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

libocrtypes "github.com/smartcontractkit/libocr/ragep2p/types"

"github.com/smartcontractkit/chainlink-ccip/chainconfig"
ccipreader "github.com/smartcontractkit/chainlink-ccip/pkg/reader"

cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3"
Expand All @@ -35,15 +36,22 @@ func TestHomeChainReader(t *testing.T) {
p2pIDs := integrationhelpers.P2pIDsFromInts(arr)
uni.AddCapability(p2pIDs)
//==============================Apply configs to Capability Contract=================================
chainAConf := integrationhelpers.SetupConfigInfo(integrationhelpers.ChainA, p2pIDs, integrationhelpers.FChainA, []byte("ChainA"))
chainBConf := integrationhelpers.SetupConfigInfo(integrationhelpers.ChainB, p2pIDs[1:], integrationhelpers.FChainB, []byte("ChainB"))
chainCConf := integrationhelpers.SetupConfigInfo(integrationhelpers.ChainC, p2pIDs[2:], integrationhelpers.FChainC, []byte("ChainC"))
encodedChainConfig, err := chainconfig.EncodeChainConfig(chainconfig.ChainConfig{
GasPriceDeviationPPB: cciptypes.NewBigIntFromInt64(1000),
DAGasPriceDeviationPPB: cciptypes.NewBigIntFromInt64(1_000_000),
FinalityDepth: -1,
OptimisticConfirmations: 1,
})
require.NoError(t, err)
chainAConf := integrationhelpers.SetupConfigInfo(integrationhelpers.ChainA, p2pIDs, integrationhelpers.FChainA, encodedChainConfig)
chainBConf := integrationhelpers.SetupConfigInfo(integrationhelpers.ChainB, p2pIDs[1:], integrationhelpers.FChainB, encodedChainConfig)
chainCConf := integrationhelpers.SetupConfigInfo(integrationhelpers.ChainC, p2pIDs[2:], integrationhelpers.FChainC, encodedChainConfig)
inputConfig := []capcfg.CCIPConfigTypesChainConfigInfo{
chainAConf,
chainBConf,
chainCConf,
}
_, err := uni.CcipCfg.ApplyChainConfigUpdates(uni.Transactor, nil, inputConfig)
_, err = uni.CcipCfg.ApplyChainConfigUpdates(uni.Transactor, nil, inputConfig)
require.NoError(t, err)
uni.Backend.Commit()
//================================Setup HomeChainReader===============================
Expand All @@ -63,6 +71,7 @@ func TestHomeChainReader(t *testing.T) {
expectedChainConfigs[cciptypes.ChainSelector(c.ChainSelector)] = ccipreader.ChainConfig{
FChain: int(c.ChainConfig.FChain),
SupportedNodes: toPeerIDs(c.ChainConfig.Readers),
Config: mustDecodeChainConfig(t, c.ChainConfig.Config),
}
}
configs, err := homeChain.GetAllChainConfigs()
Expand All @@ -86,3 +95,9 @@ func toPeerIDs(readers [][32]byte) mapset.Set[libocrtypes.PeerID] {
}
return peerIDs
}

func mustDecodeChainConfig(t *testing.T, encodedChainConfig []byte) chainconfig.ChainConfig {
chainConfig, err := chainconfig.DecodeChainConfig(encodedChainConfig)
require.NoError(t, err)
return chainConfig
}
22 changes: 15 additions & 7 deletions core/capabilities/ccip/configs/evm/chain_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,13 @@ var (
offrampABI = evmtypes.MustGetABI(evm_2_evm_multi_offramp.EVM2EVMMultiOffRampABI)
)

func MustChainWriterConfig(fromAddress common.Address, maxGasPrice *assets.Wei) []byte {
rawConfig := ChainWriterConfigRaw(fromAddress, maxGasPrice)
func MustChainWriterConfig(
fromAddress common.Address,
maxGasPrice *assets.Wei,
commitGasLimit,
execBatchGasLimit uint64,
) []byte {
rawConfig := ChainWriterConfigRaw(fromAddress, maxGasPrice, commitGasLimit, execBatchGasLimit)
encoded, err := json.Marshal(rawConfig)
if err != nil {
panic(fmt.Errorf("failed to marshal ChainWriterConfig: %w", err))
Expand All @@ -31,7 +36,12 @@ func MustChainWriterConfig(fromAddress common.Address, maxGasPrice *assets.Wei)
}

// ChainWriterConfigRaw returns a ChainWriterConfig that can be used to transmit commit and execute reports.
func ChainWriterConfigRaw(fromAddress common.Address, maxGasPrice *assets.Wei) evmrelaytypes.ChainWriterConfig {
func ChainWriterConfigRaw(
fromAddress common.Address,
maxGasPrice *assets.Wei,
commitGasLimit,
execBatchGasLimit uint64,
) evmrelaytypes.ChainWriterConfig {
return evmrelaytypes.ChainWriterConfig{
Contracts: map[string]*evmrelaytypes.ContractConfig{
consts.ContractNameOffRamp: {
Expand All @@ -40,14 +50,12 @@ func ChainWriterConfigRaw(fromAddress common.Address, maxGasPrice *assets.Wei) e
consts.MethodCommit: {
ChainSpecificName: mustGetMethodName("commit", offrampABI),
FromAddress: fromAddress,
// TODO: inject this into the method, should be fetched from the OCR config.
GasLimit: 500_000,
GasLimit: commitGasLimit,
},
consts.MethodExecute: {
ChainSpecificName: mustGetMethodName("execute", offrampABI),
FromAddress: fromAddress,
// TODO: inject this into the method, should be fetched from the OCR config.
GasLimit: 6_500_000,
GasLimit: execBatchGasLimit,
},
},
},
Expand Down
29 changes: 20 additions & 9 deletions core/capabilities/ccip/ocrimpls/config_tracker.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types"

gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper"
"github.com/smartcontractkit/libocr/offchainreporting2plus/types"
)

Expand All @@ -24,6 +25,20 @@ func (c *configTracker) LatestBlockHeight(ctx context.Context) (blockHeight uint

// LatestConfig implements types.ContractConfigTracker.
func (c *configTracker) LatestConfig(ctx context.Context, changedInBlock uint64) (types.ContractConfig, error) {
return c.contractConfig(), nil
}

// LatestConfigDetails implements types.ContractConfigTracker.
func (c *configTracker) LatestConfigDetails(ctx context.Context) (changedInBlock uint64, configDigest types.ConfigDigest, err error) {
return 0, c.cfg.ConfigDigest, nil
}

// Notify implements types.ContractConfigTracker.
func (c *configTracker) Notify() <-chan struct{} {
return nil
}

func (c *configTracker) contractConfig() types.ContractConfig {
return types.ContractConfig{
ConfigDigest: c.cfg.ConfigDigest,
ConfigCount: c.cfg.ConfigCount,
Expand All @@ -33,17 +48,13 @@ func (c *configTracker) LatestConfig(ctx context.Context, changedInBlock uint64)
OnchainConfig: []byte{},
OffchainConfigVersion: c.cfg.Config.OffchainConfigVersion,
OffchainConfig: c.cfg.Config.OffchainConfig,
}, nil
}

// LatestConfigDetails implements types.ContractConfigTracker.
func (c *configTracker) LatestConfigDetails(ctx context.Context) (changedInBlock uint64, configDigest types.ConfigDigest, err error) {
return 0, c.cfg.ConfigDigest, nil
}
}

// Notify implements types.ContractConfigTracker.
func (c *configTracker) Notify() <-chan struct{} {
return nil
// PublicConfig returns the OCR configuration as a PublicConfig so that we can
// access ReportingPluginConfig and other fields prior to launching the plugins.
func (c *configTracker) PublicConfig() (ocr3confighelper.PublicConfig, error) {
return ocr3confighelper.PublicConfigFromContractConfig(false, c.contractConfig())
}

func toOnchainPublicKeys(signers [][]byte) []types.OnchainPublicKey {
Expand Down
Loading

0 comments on commit 3fcaec9

Please sign in to comment.