Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solana plugin #15820

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
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
5 changes: 0 additions & 5 deletions core/capabilities/ccip/delegate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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)
}
Comment on lines -254 to -256
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check can still be performed by the oracle creator using something like _, supported := oraclecreator.plugins[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)
Expand Down
111 changes: 55 additions & 56 deletions core/capabilities/ccip/oraclecreator/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"encoding/json"
"fmt"
"io"
"math/big"
"time"

"github.com/ethereum/go-ethereum/common"
Expand All @@ -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"
Expand All @@ -36,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"

Expand Down Expand Up @@ -218,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
}
Comment on lines +219 to +223
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can address this once #15860 is merged

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,
Expand All @@ -236,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(chainFamily + ".MessageHasherV1"))

if config.Config.PluginType == uint8(cctypes.PluginTypeCCIPCommit) {
if !i.peerWrapper.IsStarted() {
return nil, nil, fmt.Errorf("peer wrapper is not started")
Expand All @@ -251,7 +292,7 @@ func (i *pluginOracleCreator) createFactoryAndTransmitter(
publicConfig.DeltaRound,
)

rmnCrypto := ccipevm.NewEVMRMNCrypto(i.lggr.Named("EVMRMNCrypto"))
rmnCrypto := plugin.RMNCrypto(i.lggr.Named(chainFamily + ".RMNCrypto"))
Copy link
Contributor

@winder winder Jan 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm partial to using structured logging fields for this sort of thing. i.e. lggr.Named("RMNCrypto").With("chainFamily", chainFamily).

what do you think @mateusz-sekara @makramkd


factory = commitocr3.NewPluginFactory(
i.lggr.
Expand All @@ -261,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,
Expand All @@ -283,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,
)
Expand Down Expand Up @@ -328,17 +369,18 @@ 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)
chainWriters := make(map[cciptypes.ChainSelector]types.ContractWriter)
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)
}
Expand Down Expand Up @@ -421,22 +463,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,
Expand Down Expand Up @@ -520,33 +546,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
Expand Down
14 changes: 11 additions & 3 deletions core/services/keystore/keys/ocr2key/solana_keyring.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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) {
Expand All @@ -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 {
Expand Down
40 changes: 28 additions & 12 deletions deployment/ccip/changeset/cs_jobspec.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package changeset

import (
"fmt"

"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
Expand All @@ -25,31 +28,44 @@ 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 {
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
var err error
if !node.IsBootstrap {
spec, err = validate.NewCCIPSpecToml(validate.SpecArgs{
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{
P2PV2Bootstrappers: []string{}, // Intentionally empty for bootstraps.
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 {
Expand Down
7 changes: 0 additions & 7 deletions deployment/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading