From b56aae7006cecca4d171c1831b369ed0952ea553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Wed, 25 Dec 2024 15:17:49 +0900 Subject: [PATCH 1/7] ccip/oraclecreator: inline a couple functions --- .../capabilities/ccip/oraclecreator/plugin.go | 23 ++++--------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/core/capabilities/ccip/oraclecreator/plugin.go b/core/capabilities/ccip/oraclecreator/plugin.go index d4c5596aeaf..fb7a48f9175 100644 --- a/core/capabilities/ccip/oraclecreator/plugin.go +++ b/core/capabilities/ccip/oraclecreator/plugin.go @@ -328,9 +328,9 @@ func (i *pluginOracleCreator) createReadersAndWriters( execBatchGasLimit = defaultExecGasLimit } - homeChainID, err := i.getChainID(i.homeChainSelector) + homeChainID, err := chainsel.GetChainIDFromSelector(uint64(i.homeChainSelector)) if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("failed to get chain ID from chain selector %d: %w", i.homeChainSelector, err) } contractReaders := make(map[cciptypes.ChainSelector]types.ContractReader) @@ -338,7 +338,8 @@ func (i *pluginOracleCreator) createReadersAndWriters( for relayID, relayer := range i.relayers { chainID := relayID.ChainID relayChainFamily := relayID.Network - chainSelector, err1 := i.getChainSelector(chainID, relayChainFamily) + chainDetails, err1 := chainsel.GetChainDetailsByChainIDAndFamily(chainID, relayChainFamily) + chainSelector := cciptypes.ChainSelector(chainDetails.ChainSelector) if err1 != nil { return nil, nil, fmt.Errorf("failed to get chain selector from chain ID %s: %w", chainID, err1) } @@ -421,22 +422,6 @@ func decodeAndValidateOffchainConfig( return ofc, nil } -func (i *pluginOracleCreator) getChainSelector(chainID string, chainFamily string) (cciptypes.ChainSelector, error) { - chainDetails, err := chainsel.GetChainDetailsByChainIDAndFamily(chainID, chainFamily) - if err != nil { - return 0, fmt.Errorf("failed to get chain selector from chain ID %s and family %s", chainID, chainFamily) - } - return cciptypes.ChainSelector(chainDetails.ChainSelector), nil -} - -func (i *pluginOracleCreator) getChainID(chainSelector cciptypes.ChainSelector) (string, error) { - chainID, err := chainsel.GetChainIDFromSelector(uint64(chainSelector)) - if err != nil { - return "", fmt.Errorf("failed to get chain ID from chain selector %d: %w", chainSelector, err) - } - return chainID, nil -} - func getChainReaderConfig( lggr logger.Logger, chainID string, From 5bf41bf954e82380d844471e3b3a13dd0c1872c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Wed, 25 Dec 2024 15:18:25 +0900 Subject: [PATCH 2/7] ccip/oraclecreator: getKeySpecificMaxGasPrice seems unused? --- .../capabilities/ccip/oraclecreator/plugin.go | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/core/capabilities/ccip/oraclecreator/plugin.go b/core/capabilities/ccip/oraclecreator/plugin.go index fb7a48f9175..41bced3ee51 100644 --- a/core/capabilities/ccip/oraclecreator/plugin.go +++ b/core/capabilities/ccip/oraclecreator/plugin.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "io" - "math/big" "time" "github.com/ethereum/go-ethereum/common" @@ -20,8 +19,6 @@ import ( evmconfig "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/configs/evm" "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/ocrimpls" cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/services/ocr3/promwrapper" "github.com/smartcontractkit/libocr/commontypes" @@ -505,33 +502,6 @@ func createChainWriter( return cw, nil } -func getKeySpecificMaxGasPrice(evmConfigs toml.EVMConfigs, chainID *big.Int, fromAddress common.Address) *assets.Wei { - var maxGasPrice *assets.Wei - - // If a chain is enabled it should have some configuration in the TOML config - // of the chainlink node. - for _, config := range evmConfigs { - if config.ChainID.ToInt().Cmp(chainID) != 0 { - continue - } - - // find the key-specific max gas price for the given fromAddress. - for _, keySpecific := range config.KeySpecific { - if keySpecific.Key.Address() == fromAddress { - maxGasPrice = keySpecific.GasEstimator.PriceMax - } - } - - // if we didn't find a key-specific max gas price, use the one specified - // in the gas estimator config, which should have a default value. - if maxGasPrice == nil { - maxGasPrice = config.GasEstimator.PriceMax - } - } - - return maxGasPrice -} - type offChainConfig struct { commitOffchainConfig *pluginconfig.CommitOffchainConfig execOffchainConfig *pluginconfig.ExecuteOffchainConfig From 27ce8db68e9cc415794c98b6162915a9f7d7fda8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Thu, 2 Jan 2025 10:35:30 +0900 Subject: [PATCH 3/7] keystore: Add OCR3 support to solana OCR keys --- .../keystore/keys/ocr2key/solana_keyring.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/core/services/keystore/keys/ocr2key/solana_keyring.go b/core/services/keystore/keys/ocr2key/solana_keyring.go index a5fa2f77dc0..e9a017c3ce5 100644 --- a/core/services/keystore/keys/ocr2key/solana_keyring.go +++ b/core/services/keystore/keys/ocr2key/solana_keyring.go @@ -7,7 +7,6 @@ import ( "io" "github.com/ethereum/go-ethereum/crypto" - "github.com/pkg/errors" "github.com/smartcontractkit/libocr/offchainreporting2/types" "github.com/smartcontractkit/libocr/offchainreporting2plus/chains/evmutil" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" @@ -49,7 +48,15 @@ func (skr *solanaKeyring) Sign(reportCtx ocrtypes.ReportContext, report ocrtypes } func (skr *solanaKeyring) Sign3(digest types.ConfigDigest, seqNr uint64, r ocrtypes.Report) (signature []byte, err error) { - return nil, errors.New("not implemented") + return skr.signBlob(skr.reportToSigData3(digest, seqNr, r)) +} + +func (skr *solanaKeyring) reportToSigData3(digest types.ConfigDigest, seqNr uint64, r ocrtypes.Report) []byte { + rawReportContext := RawReportContext3(digest, seqNr) + sigData := crypto.Keccak256(r) + sigData = append(sigData, rawReportContext[0][:]...) + sigData = append(sigData, rawReportContext[1][:]...) + return crypto.Keccak256(sigData) } func (skr *solanaKeyring) signBlob(b []byte) (sig []byte, err error) { @@ -62,7 +69,8 @@ func (skr *solanaKeyring) Verify(publicKey ocrtypes.OnchainPublicKey, reportCtx } func (skr *solanaKeyring) Verify3(publicKey ocrtypes.OnchainPublicKey, cd ocrtypes.ConfigDigest, seqNr uint64, r ocrtypes.Report, signature []byte) bool { - return false + hash := skr.reportToSigData3(cd, seqNr, r) + return skr.verifyBlob(publicKey, hash, signature) } func (skr *solanaKeyring) verifyBlob(pubkey types.OnchainPublicKey, b, sig []byte) bool { From bc0cda81a7c22c0692941095eedcc743f2a61288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Thu, 2 Jan 2025 14:39:24 +0900 Subject: [PATCH 4/7] deployment/ccip: Declare OCR key bundles for all available chain types --- core/capabilities/ccip/delegate.go | 5 ---- deployment/ccip/changeset/cs_jobspec.go | 34 ++++++++++++++++--------- deployment/environment.go | 7 ----- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/core/capabilities/ccip/delegate.go b/core/capabilities/ccip/delegate.go index 536622978f6..3673c46ff34 100644 --- a/core/capabilities/ccip/delegate.go +++ b/core/capabilities/ccip/delegate.go @@ -43,7 +43,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" - "github.com/smartcontractkit/chainlink/v2/core/services/relay" "github.com/smartcontractkit/chainlink/v2/core/services/telemetry" "github.com/smartcontractkit/chainlink/v2/plugins" ) @@ -251,10 +250,6 @@ func (d *Delegate) OnDeleteJob(ctx context.Context, spec job.Job) error { func (d *Delegate) getOCRKeys(ocrKeyBundleIDs job.JSONConfig) (map[string]ocr2key.KeyBundle, error) { ocrKeys := make(map[string]ocr2key.KeyBundle) for networkType, bundleIDRaw := range ocrKeyBundleIDs { - if networkType != relay.NetworkEVM { - return nil, fmt.Errorf("unsupported chain type: %s", networkType) - } - bundleID, ok := bundleIDRaw.(string) if !ok { return nil, fmt.Errorf("OCRKeyBundleIDs must be a map of chain types to OCR key bundle IDs, got: %T", bundleIDRaw) diff --git a/deployment/ccip/changeset/cs_jobspec.go b/deployment/ccip/changeset/cs_jobspec.go index 2551f193f47..19fb2e7ab0b 100644 --- a/deployment/ccip/changeset/cs_jobspec.go +++ b/deployment/ccip/changeset/cs_jobspec.go @@ -3,10 +3,11 @@ package changeset import ( "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock" + chainsel "github.com/smartcontractkit/chain-selectors" + "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/internal" "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/validate" - "github.com/smartcontractkit/chainlink/v2/core/services/relay" ) var _ deployment.ChangeSet[any] = CCIPCapabilityJobspec @@ -25,6 +26,19 @@ func CCIPCapabilityJobspec(env deployment.Environment, _ any) (deployment.Change // Find the bootstrap nodes nodesToJobSpecs := make(map[string][]string) for _, node := range nodes { + // pick first keybundle of each type (by default nodes will auto create one key of each type for each defined chain family) + keyBundles := map[string]string{} + for details, config := range node.SelToOCRConfig { + family, err := chainsel.GetSelectorFamily(details.ChainSelector) + if err != nil { + break + } + _, exists := keyBundles[family] + if !exists { + keyBundles[family] = config.KeyBundleID + } + } + var spec string var err error if !node.IsBootstrap { @@ -32,13 +46,10 @@ func CCIPCapabilityJobspec(env deployment.Environment, _ any) (deployment.Change P2PV2Bootstrappers: nodes.BootstrapLocators(), CapabilityVersion: internal.CapabilityVersion, CapabilityLabelledName: internal.CapabilityLabelledName, - OCRKeyBundleIDs: map[string]string{ - // TODO: Validate that that all EVM chains are using the same keybundle. - relay.NetworkEVM: node.FirstOCRKeybundle().KeyBundleID, - }, - P2PKeyID: node.PeerID.String(), - RelayConfigs: nil, - PluginConfig: map[string]any{}, + OCRKeyBundleIDs: keyBundles, + P2PKeyID: node.PeerID.String(), + RelayConfigs: nil, + PluginConfig: map[string]any{}, }) } else { spec, err = validate.NewCCIPSpecToml(validate.SpecArgs{ @@ -46,10 +57,9 @@ func CCIPCapabilityJobspec(env deployment.Environment, _ any) (deployment.Change CapabilityVersion: internal.CapabilityVersion, CapabilityLabelledName: internal.CapabilityLabelledName, OCRKeyBundleIDs: map[string]string{}, - // TODO: validate that all EVM chains are using the same keybundle - P2PKeyID: node.PeerID.String(), - RelayConfigs: nil, - PluginConfig: map[string]any{}, + P2PKeyID: node.PeerID.String(), + RelayConfigs: nil, + PluginConfig: map[string]any{}, }) } if err != nil { diff --git a/deployment/environment.go b/deployment/environment.go index bfbeac2f0c4..af30325d2ca 100644 --- a/deployment/environment.go +++ b/deployment/environment.go @@ -289,13 +289,6 @@ func (n Node) OCRConfigForChainSelector(chainSel uint64) (OCRConfig, bool) { return c, ok } -func (n Node) FirstOCRKeybundle() OCRConfig { - for _, ocrConfig := range n.SelToOCRConfig { - return ocrConfig - } - return OCRConfig{} -} - func MustPeerIDFromString(s string) p2pkey.PeerID { p := p2pkey.PeerID{} if err := p.UnmarshalString(s); err != nil { From 07ad42c662ff48493a21dc78a3e361b1c1efcf4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Thu, 2 Jan 2025 16:08:05 +0900 Subject: [PATCH 5/7] oraclecreator: Extract chain specific config out into a per-family declaration --- .../capabilities/ccip/oraclecreator/plugin.go | 58 ++++++++++++++++--- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/core/capabilities/ccip/oraclecreator/plugin.go b/core/capabilities/ccip/oraclecreator/plugin.go index 41bced3ee51..b8bc30aea8e 100644 --- a/core/capabilities/ccip/oraclecreator/plugin.go +++ b/core/capabilities/ccip/oraclecreator/plugin.go @@ -33,6 +33,7 @@ import ( execocr3 "github.com/smartcontractkit/chainlink-ccip/execute" "github.com/smartcontractkit/chainlink-ccip/pkg/consts" ccipreaderpkg "github.com/smartcontractkit/chainlink-ccip/pkg/reader" + "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" cciptypes "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" "github.com/smartcontractkit/chainlink-ccip/pluginconfig" @@ -215,6 +216,39 @@ func (i *pluginOracleCreator) Create(ctx context.Context, donID uint32, config c return newWrappedOracle(oracle, closers), nil } +// TODO: duplicated from chainlink-ccip since it's still internal +type EstimateProvider interface { + CalculateMerkleTreeGas(numRequests int) uint64 + CalculateMessageMaxGas(msg ccipocr3.Message) uint64 +} +type plugin struct { + CommitPluginCodec cciptypes.CommitPluginCodec + ExecutePluginCodec cciptypes.ExecutePluginCodec + MessageHasher func(lggr logger.Logger) cciptypes.MessageHasher + TokenDataEncoder cciptypes.TokenDataEncoder + GasEstimateProvider EstimateProvider + RMNCrypto func(lggr logger.Logger) cciptypes.RMNCrypto +} + +var plugins = map[string]plugin{ + chainsel.FamilyEVM: { + CommitPluginCodec: ccipevm.NewCommitPluginCodecV1(), + ExecutePluginCodec: ccipevm.NewExecutePluginCodecV1(), + MessageHasher: func(lggr logger.Logger) cciptypes.MessageHasher { return ccipevm.NewMessageHasherV1(lggr) }, + TokenDataEncoder: ccipevm.NewEVMTokenDataEncoder(), + GasEstimateProvider: ccipevm.NewGasEstimateProvider(), + RMNCrypto: func(lggr logger.Logger) cciptypes.RMNCrypto { return ccipevm.NewEVMRMNCrypto(lggr) }, + }, + chainsel.FamilySolana: { + CommitPluginCodec: nil, + ExecutePluginCodec: nil, + MessageHasher: func(lggr logger.Logger) cciptypes.MessageHasher { return nil }, + TokenDataEncoder: nil, + GasEstimateProvider: nil, + RMNCrypto: func(lggr logger.Logger) cciptypes.RMNCrypto { return nil }, + }, +} + func (i *pluginOracleCreator) createFactoryAndTransmitter( donID uint32, config cctypes.OCR3ConfigWithMeta, @@ -233,6 +267,16 @@ func (i *pluginOracleCreator) createFactoryAndTransmitter( return nil, nil, fmt.Errorf("unsupported chain selector %d %w", config.Config.ChainSelector, err) } + chainFamily, err := chainsel.GetSelectorFamily(uint64(config.Config.ChainSelector)) + if err != nil { + return nil, nil, fmt.Errorf("unsupported chain selector %d %w", config.Config.ChainSelector, err) + } + plugin, exists := plugins[chainFamily] + if !exists { + return nil, nil, fmt.Errorf("unsupported chain %v", chainFamily) + } + messageHasher := plugin.MessageHasher(i.lggr.Named("MessageHasherV1")) + if config.Config.PluginType == uint8(cctypes.PluginTypeCCIPCommit) { if !i.peerWrapper.IsStarted() { return nil, nil, fmt.Errorf("peer wrapper is not started") @@ -248,7 +292,7 @@ func (i *pluginOracleCreator) createFactoryAndTransmitter( publicConfig.DeltaRound, ) - rmnCrypto := ccipevm.NewEVMRMNCrypto(i.lggr.Named("EVMRMNCrypto")) + rmnCrypto := plugin.RMNCrypto(i.lggr.Named("RMNCrypto")) factory = commitocr3.NewPluginFactory( i.lggr. @@ -258,8 +302,8 @@ func (i *pluginOracleCreator) createFactoryAndTransmitter( Named(hexutil.Encode(config.Config.OfframpAddress)), donID, ccipreaderpkg.OCR3ConfigWithMeta(config), - ccipevm.NewCommitPluginCodecV1(), - ccipevm.NewMessageHasherV1(i.lggr.Named("MessageHasherV1")), + plugin.CommitPluginCodec, + messageHasher, i.homeChainReader, i.homeChainSelector, contractReaders, @@ -280,11 +324,11 @@ func (i *pluginOracleCreator) createFactoryAndTransmitter( Named(hexutil.Encode(config.Config.OfframpAddress)), donID, ccipreaderpkg.OCR3ConfigWithMeta(config), - ccipevm.NewExecutePluginCodecV1(), - ccipevm.NewMessageHasherV1(i.lggr.Named("MessageHasherV1")), + plugin.ExecutePluginCodec, + messageHasher, i.homeChainReader, - ccipevm.NewEVMTokenDataEncoder(), - ccipevm.NewGasEstimateProvider(), + plugin.TokenDataEncoder, + plugin.GasEstimateProvider, contractReaders, chainWriters, ) From 8ea3930b086f8ac560118b09216a95ee9285449c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Fri, 3 Jan 2025 20:35:22 +0900 Subject: [PATCH 6/7] Tweak logger names --- core/capabilities/ccip/oraclecreator/plugin.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/capabilities/ccip/oraclecreator/plugin.go b/core/capabilities/ccip/oraclecreator/plugin.go index b8bc30aea8e..aaf43dd994e 100644 --- a/core/capabilities/ccip/oraclecreator/plugin.go +++ b/core/capabilities/ccip/oraclecreator/plugin.go @@ -275,7 +275,7 @@ func (i *pluginOracleCreator) createFactoryAndTransmitter( if !exists { return nil, nil, fmt.Errorf("unsupported chain %v", chainFamily) } - messageHasher := plugin.MessageHasher(i.lggr.Named("MessageHasherV1")) + messageHasher := plugin.MessageHasher(i.lggr.Named(chainFamily + ".MessageHasherV1")) if config.Config.PluginType == uint8(cctypes.PluginTypeCCIPCommit) { if !i.peerWrapper.IsStarted() { @@ -292,7 +292,7 @@ func (i *pluginOracleCreator) createFactoryAndTransmitter( publicConfig.DeltaRound, ) - rmnCrypto := plugin.RMNCrypto(i.lggr.Named("RMNCrypto")) + rmnCrypto := plugin.RMNCrypto(i.lggr.Named(chainFamily + ".RMNCrypto")) factory = commitocr3.NewPluginFactory( i.lggr. From ec9d62bfba3f85a28a888b77b7cf7ce2d2be60ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Fri, 3 Jan 2025 20:35:43 +0900 Subject: [PATCH 7/7] Add in check for multiple keys --- deployment/ccip/changeset/cs_jobspec.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/deployment/ccip/changeset/cs_jobspec.go b/deployment/ccip/changeset/cs_jobspec.go index 19fb2e7ab0b..c311fdb9ff3 100644 --- a/deployment/ccip/changeset/cs_jobspec.go +++ b/deployment/ccip/changeset/cs_jobspec.go @@ -1,6 +1,8 @@ package changeset import ( + "fmt" + "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock" chainsel "github.com/smartcontractkit/chain-selectors" @@ -34,9 +36,13 @@ func CCIPCapabilityJobspec(env deployment.Environment, _ any) (deployment.Change break } _, exists := keyBundles[family] - if !exists { - keyBundles[family] = config.KeyBundleID + if exists { + if keyBundles[family] != config.KeyBundleID { + return deployment.ChangesetOutput{}, fmt.Errorf("multiple different %v OCR keys found for node %v", family, node.PeerID) + } + continue } + keyBundles[family] = config.KeyBundleID } var spec string