Skip to content
Closed
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
@@ -0,0 +1,41 @@
package adapters

import (
"fmt"

"github.com/smartcontractkit/chainlink-ccip/ccv/chains/evm/deployment/v1_7_0/operations/committee_verifier"
ccvadapters "github.com/smartcontractkit/chainlink-ccip/deployment/v1_7_0/adapters"
"github.com/smartcontractkit/chainlink-deployments-framework/datastore"
)

type EVMCommitteeVerifierContractAdapter struct{}

var _ ccvadapters.CommitteeVerifierContractAdapter = (*EVMCommitteeVerifierContractAdapter)(nil)

func (a *EVMCommitteeVerifierContractAdapter) ResolveCommitteeVerifierContracts(
ds datastore.DataStore,
chainSelector uint64,
qualifier string,
) ([]datastore.AddressRef, error) {
verifier, err := ds.Addresses().Get(datastore.NewAddressRefKey(
chainSelector,
datastore.ContractType(committee_verifier.ContractType),
committee_verifier.Version,
qualifier,
))
if err != nil {
return nil, fmt.Errorf("committee verifier not found for chain %d qualifier %q: %w", chainSelector, qualifier, err)
}

resolver, err := ds.Addresses().Get(datastore.NewAddressRefKey(
chainSelector,
datastore.ContractType(committee_verifier.ResolverType),
committee_verifier.Version,
qualifier,
))
if err != nil {
return nil, fmt.Errorf("committee verifier resolver not found for chain %d qualifier %q: %w", chainSelector, qualifier, err)
}

return []datastore.AddressRef{verifier, resolver}, nil
}
3 changes: 3 additions & 0 deletions ccv/chains/evm/deployment/v1_7_0/adapters/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
adapters1_6 "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_6_0/adapters"

"github.com/smartcontractkit/chainlink-ccip/deployment/deploy"
ccvadapters "github.com/smartcontractkit/chainlink-ccip/deployment/v1_7_0/adapters"
)

func init() {
Expand All @@ -22,4 +23,6 @@ func init() {
laneMigratorReg := deploy.GetLaneMigratorRegistry()
laneMigratorReg.RegisterRampUpdater(chainsel.FamilyEVM, semver.MustParse("1.7.0"), &LaneMigrator{})
laneMigratorReg.RegisterRouterUpdater(chainsel.FamilyEVM, semver.MustParse("1.2.0"), &adapters1_2.RouterUpdater{})

ccvadapters.GetCommitteeVerifierContractRegistry().Register(chainsel.FamilyEVM, &EVMCommitteeVerifierContractAdapter{})
}
67 changes: 67 additions & 0 deletions deployment/v1_7_0/adapters/committee_verifier_contract.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package adapters

import (
"fmt"
"sync"

chainsel "github.com/smartcontractkit/chain-selectors"
"github.com/smartcontractkit/chainlink-deployments-framework/datastore"
)

type CommitteeVerifierContractAdapter interface {
ResolveCommitteeVerifierContracts(
ds datastore.DataStore,
chainSelector uint64,
qualifier string,
) ([]datastore.AddressRef, error)
}

type CommitteeVerifierContractRegistry struct {
mu sync.Mutex
adapters map[string]CommitteeVerifierContractAdapter
}

var (
singletonCommitteeVerifierContractRegistry *CommitteeVerifierContractRegistry
committeeVerifierContractRegistryOnce sync.Once
)

func NewCommitteeVerifierContractRegistry() *CommitteeVerifierContractRegistry {
return &CommitteeVerifierContractRegistry{
adapters: make(map[string]CommitteeVerifierContractAdapter),
}
}

func GetCommitteeVerifierContractRegistry() *CommitteeVerifierContractRegistry {
committeeVerifierContractRegistryOnce.Do(func() {
singletonCommitteeVerifierContractRegistry = NewCommitteeVerifierContractRegistry()
})
return singletonCommitteeVerifierContractRegistry
}

func (r *CommitteeVerifierContractRegistry) Register(family string, a CommitteeVerifierContractAdapter) {
r.mu.Lock()
defer r.mu.Unlock()
if _, exists := r.adapters[family]; !exists {
r.adapters[family] = a
}
}

func (r *CommitteeVerifierContractRegistry) Get(family string) (CommitteeVerifierContractAdapter, bool) {
r.mu.Lock()
defer r.mu.Unlock()
a, ok := r.adapters[family]
return a, ok
}

func (r *CommitteeVerifierContractRegistry) GetByChain(chainSelector uint64) (CommitteeVerifierContractAdapter, error) {
family, err := chainsel.GetSelectorFamily(chainSelector)
if err != nil {
return nil, fmt.Errorf("failed to get chain family for selector %d: %w", chainSelector, err)
}
adapter, ok := r.Get(family)
if !ok {
return nil, fmt.Errorf("no committee verifier contract adapter registered for chain family %q", family)
}
return adapter, nil
}
213 changes: 213 additions & 0 deletions deployment/v1_7_0/changesets/configure_committee_verifiers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
package changesets

import (
"fmt"
"slices"
"strconv"

chainsel "github.com/smartcontractkit/chain-selectors"
changesetscore "github.com/smartcontractkit/chainlink-ccip/deployment/utils/changesets"
"github.com/smartcontractkit/chainlink-ccip/deployment/utils/mcms"
"github.com/smartcontractkit/chainlink-ccip/deployment/v1_7_0/adapters"
"github.com/smartcontractkit/chainlink-ccip/deployment/v1_7_0/offchain"
"github.com/smartcontractkit/chainlink-ccip/deployment/v1_7_0/offchain/operations/fetch_signing_keys"
"github.com/smartcontractkit/chainlink-deployments-framework/datastore"
"github.com/smartcontractkit/chainlink-deployments-framework/deployment"
)

type CommitteeVerifierRemoteChainConfig struct {
AllowlistEnabled bool
AddedAllowlistedSenders []string
RemovedAllowlistedSenders []string
FeeUSDCents uint16
GasForVerification uint32
PayloadSizeBytes uint32
}

type CommitteeVerifierInputConfig struct {
CommitteeQualifier string
RemoteChains map[uint64]CommitteeVerifierRemoteChainConfig
}

type PartialChainConfig struct {
ChainSelector uint64
Router datastore.AddressRef
OnRamp datastore.AddressRef
CommitteeVerifiers []CommitteeVerifierInputConfig
FeeQuoter datastore.AddressRef
OffRamp datastore.AddressRef
RemoteChains map[uint64]adapters.RemoteChainConfig[datastore.AddressRef, datastore.AddressRef]
}

type ConfigureChainsForLanesFromTopologyConfig struct {
Topology *offchain.EnvironmentTopology
Chains []PartialChainConfig
MCMS mcms.Input
}

func ConfigureChainsForLanesFromTopology(
committeeVerifierContractRegistry *adapters.CommitteeVerifierContractRegistry,
chainFamilyRegistry *adapters.ChainFamilyRegistry,
mcmsRegistry *changesetscore.MCMSReaderRegistry,
) deployment.ChangeSetV2[ConfigureChainsForLanesFromTopologyConfig] {
validate := func(e deployment.Environment, cfg ConfigureChainsForLanesFromTopologyConfig) error {
if cfg.Topology == nil {
return fmt.Errorf("topology is required")
}

if len(cfg.Topology.NOPTopology.Committees) == 0 {
return fmt.Errorf("no committees defined in topology")
}

for _, chain := range cfg.Chains {
if !slices.Contains(e.BlockChains.ListChainSelectors(), chain.ChainSelector) {
return fmt.Errorf("chain selector %d is not available in environment", chain.ChainSelector)
}
}

return nil
}

apply := func(e deployment.Environment, cfg ConfigureChainsForLanesFromTopologyConfig) (deployment.ChangesetOutput, error) {
if cfg.Topology == nil {
return deployment.ChangesetOutput{}, fmt.Errorf("topology is required")
}

signingKeysByNOP := fetchSigningKeysForNOPs(e, cfg.Topology.NOPTopology.NOPs)

chains := make([]ChainConfig, 0, len(cfg.Chains))
for _, chain := range cfg.Chains {
committeeVerifiers := make([]adapters.CommitteeVerifierConfig[datastore.AddressRef], 0, len(chain.CommitteeVerifiers))
for _, cv := range chain.CommitteeVerifiers {
remoteChains := make(map[uint64]adapters.CommitteeVerifierRemoteChainConfig, len(cv.RemoteChains))
for remoteChainSelector, remoteChainConfig := range cv.RemoteChains {
signatureConfig, err := getSignatureConfigForLane(e, cfg.Topology, cv.CommitteeQualifier, chain.ChainSelector, remoteChainSelector, signingKeysByNOP)
if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to get signature config for source chain %d: %w", remoteChainSelector, err)
}
remoteChains[remoteChainSelector] = adapters.CommitteeVerifierRemoteChainConfig{
AllowlistEnabled: remoteChainConfig.AllowlistEnabled,
AddedAllowlistedSenders: remoteChainConfig.AddedAllowlistedSenders,
RemovedAllowlistedSenders: remoteChainConfig.RemovedAllowlistedSenders,
FeeUSDCents: remoteChainConfig.FeeUSDCents,
GasForVerification: remoteChainConfig.GasForVerification,
PayloadSizeBytes: remoteChainConfig.PayloadSizeBytes,
SignatureConfig: *signatureConfig,
}
}

adapter, err := committeeVerifierContractRegistry.GetByChain(chain.ChainSelector)
if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("no committee verifier contract adapter for chain %d: %w", chain.ChainSelector, err)
}

contracts, err := adapter.ResolveCommitteeVerifierContracts(e.DataStore, chain.ChainSelector, cv.CommitteeQualifier)
if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to resolve committee verifier contracts for chain %d qualifier %q: %w", chain.ChainSelector, cv.CommitteeQualifier, err)
}

committeeVerifiers = append(committeeVerifiers, adapters.CommitteeVerifierConfig[datastore.AddressRef]{
CommitteeVerifier: contracts,
RemoteChains: remoteChains,
})
}
chains = append(chains, ChainConfig{
ChainSelector: chain.ChainSelector,
RemoteChains: chain.RemoteChains,
FeeQuoter: chain.FeeQuoter,
OnRamp: chain.OnRamp,
OffRamp: chain.OffRamp,
Router: chain.Router,
CommitteeVerifiers: committeeVerifiers,
})
}

return ConfigureChainsForLanes(chainFamilyRegistry, mcmsRegistry).Apply(e, ConfigureChainsForLanesConfig{
Chains: chains,
MCMS: cfg.MCMS,
})
}

return deployment.CreateChangeSet(apply, validate)
}

func getSignatureConfigForLane(
e deployment.Environment,
topology *offchain.EnvironmentTopology,
committeeQualifier string,
localSelector uint64,
remoteSelector uint64,
signingKeysByNOP fetch_signing_keys.SigningKeysByNOP,
) (*adapters.CommitteeVerifierSignatureQuorumConfig, error) {
committee, ok := topology.NOPTopology.Committees[committeeQualifier]
if !ok {
return nil, fmt.Errorf("committee %q not found", committeeQualifier)
}

chainCfg, ok := committee.ChainConfigs[strconv.FormatUint(remoteSelector, 10)]
if !ok {
return nil, fmt.Errorf("chain selector %d not found in committee %q", remoteSelector, committeeQualifier)
}

localFamily, err := chainsel.GetSelectorFamily(localSelector)
if err != nil {
return nil, fmt.Errorf("failed to get selector family for selector %d: %w", localSelector, err)
}

signers := make([]string, 0, len(chainCfg.NOPAliases))
for _, alias := range chainCfg.NOPAliases {
signer, err := signerAddressForNOPAlias(e, topology, alias, localFamily, committeeQualifier, remoteSelector, signingKeysByNOP)
if err != nil {
return nil, err
}
signers = append(signers, signer)
}

return &adapters.CommitteeVerifierSignatureQuorumConfig{
Threshold: chainCfg.Threshold,
Signers: signers,
}, nil
}

func signerAddressForNOPAlias(
e deployment.Environment,
topology *offchain.EnvironmentTopology,
alias string,
localFamily string,
committeeQualifier string,
remoteSelector uint64,
signingKeysByNOP fetch_signing_keys.SigningKeysByNOP,
) (string, error) {
nop, ok := topology.NOPTopology.GetNOP(alias)
if !ok {
return "", fmt.Errorf(
"NOP alias %q not found for committee %q chain %d",
alias, committeeQualifier, remoteSelector,
)
}

if nop.SignerAddressByFamily != nil {
if addr := nop.SignerAddressByFamily[localFamily]; addr != "" {
return addr, nil
}
}

if signer, ok := signerFromJDIfMissing(
nop.SignerAddressByFamily,
alias,
localFamily,
signingKeysByNOP,
); ok {
e.Logger.Debugw("Using signing address from JD",
"nopAlias", alias,
"chainFamily", localFamily,
"signerAddress", signer,
)
return signer, nil
}

return "", fmt.Errorf(
"NOP %q missing signer_address for family %s on committee %q chain %d",
alias, localFamily, committeeQualifier, remoteSelector,
)
}
Loading
Loading