Skip to content
Merged
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
93 changes: 75 additions & 18 deletions deployment/ccip/changeset/v1_6/cs_ccip_home.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import (
"errors"
"fmt"
"math/big"
"slices"
"strings"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/gagliardetto/solana-go"
"golang.org/x/exp/maps"

"github.com/Masterminds/semver/v3"
chain_selectors "github.com/smartcontractkit/chain-selectors"

mcmslib "github.com/smartcontractkit/mcms"
Expand All @@ -28,6 +30,7 @@ import (
"github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/tokens"
"github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3"
"github.com/smartcontractkit/chainlink-ccip/pluginconfig"
"github.com/smartcontractkit/chainlink-deployments-framework/datastore"

capabilities_registry "github.com/smartcontractkit/chainlink-evm/gethwrappers/keystone/generated/capabilities_registry_1_1_0"

Expand Down Expand Up @@ -185,29 +188,36 @@ func validateUSDCConfig(usdcConfig *pluginconfig.USDCCCTPObserverConfig, state s
if !ok {
return fmt.Errorf("chain %d does not exist in EVM chain state but provided in USDCCCTPObserverConfig", sel)
}
if onchainState.USDCTokenPools == nil && onchainState.USDCTokenPoolsV1_6 == nil && onchainState.USDCTokenPoolProxies == nil {
return fmt.Errorf("chain %d does not have any USDC token pools deployed", sel)
validSourcePools := make([]common.Address, 0, 3)
if pool, ok := onchainState.USDCTokenPools[deployment.Version1_5_1]; ok {
validSourcePools = append(validSourcePools, pool.Address())
}
if pool, ok := onchainState.USDCTokenPoolsV1_6[deployment.Version1_6_2]; ok {
validSourcePools = append(validSourcePools, pool.Address())
}

var sourcePoolAddress common.Address
if proxy, ok := onchainState.USDCTokenPoolProxies[deployment.Version1_7_0]; ok {
sourcePoolAddress = proxy
} else if pool, ok := onchainState.USDCTokenPoolsV1_6[deployment.Version1_6_2]; ok {
sourcePoolAddress = pool.Address()
} else if pool, ok := onchainState.USDCTokenPools[deployment.Version1_5_1]; ok {
sourcePoolAddress = pool.Address()
} else {
validSourcePools = append(validSourcePools, proxy)
}
if len(validSourcePools) == 0 {
return fmt.Errorf(
"chain %d does not have USDC token pool deployed with version %s, %s, or %s",
sel, deployment.Version1_5_1, deployment.Version1_6_2, deployment.Version1_7_0,
)
}

if common.HexToAddress(token.SourcePoolAddress) != sourcePoolAddress {
return fmt.Errorf("chain %d has latest USDC token pool deployed at %s, "+
"but SourcePoolAddress %s is provided in USDCCCTPObserverConfig",
sel, sourcePoolAddress.String(), token.SourcePoolAddress)
configuredSourcePool := common.HexToAddress(token.SourcePoolAddress)
if slices.Contains(validSourcePools, configuredSourcePool) {
break
}

expectedAddresses := make([]string, 0, len(validSourcePools))
for _, sourcePoolAddress := range validSourcePools {
expectedAddresses = append(expectedAddresses, sourcePoolAddress.String())
}
return fmt.Errorf(
"chain %d SourcePoolAddress %s is not one of the deployed USDC pools %v",
sel, token.SourcePoolAddress, expectedAddresses,
)
case chain_selectors.FamilySolana:
onchainState, ok := state.SolChains[uint64(sel)]
if !ok {
Expand Down Expand Up @@ -249,6 +259,53 @@ func validateUSDCConfig(usdcConfig *pluginconfig.USDCCCTPObserverConfig, state s
return nil
}

func loadOnchainStateForCandidateChangesets(e cldf.Environment) (stateview.CCIPOnChainState, error) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

there are methods which can already load both DS & Addressbook state merged, but for this purpose its okay

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

#21567 (comment)
GitHub made this suggestion which made sense, so I wrote my own

state, err := stateview.LoadOnchainState(e)
if err != nil {
return stateview.CCIPOnChainState{}, err
}
if e.DataStore == nil {
return state, nil
}

for chainSelector := range e.BlockChains.EVMChains() {
refs := e.DataStore.Addresses().Filter(
datastore.AddressRefByChainSelector(chainSelector),
datastore.AddressRefByType(datastore.ContractType(shared.USDCTokenPoolProxy)),
datastore.AddressRefByVersion(&deployment.Version1_7_0),
)
if len(refs) == 0 {
continue
}
if len(refs) > 1 {
return stateview.CCIPOnChainState{}, fmt.Errorf(
"multiple datastore entries found for %s %s on chain %d; qualifiers=%v",
shared.USDCTokenPoolProxy, deployment.Version1_7_0, chainSelector, maps.Keys(refsByQualifier(refs)),
)
}

chainState, ok := state.EVMChainState(chainSelector)
if !ok {
return stateview.CCIPOnChainState{}, fmt.Errorf("chain %d not found in state", chainSelector)
}
if chainState.USDCTokenPoolProxies == nil {
chainState.USDCTokenPoolProxies = make(map[semver.Version]common.Address)
}
chainState.USDCTokenPoolProxies[deployment.Version1_7_0] = common.HexToAddress(refs[0].Address)
state.WriteEVMChainState(chainSelector, chainState)
}

return state, state.Validate()
Comment thread
0xsuryansh marked this conversation as resolved.
}

func refsByQualifier(refs []datastore.AddressRef) map[string]struct{} {
out := make(map[string]struct{}, len(refs))
for _, ref := range refs {
out[ref.Qualifier] = struct{}{}
}
return out
}

type CCIPOCRParams struct {
// OCRParameters contains the parameters for the OCR plugin.
OCRParameters commontypes.OCRParameters `json:"ocrParameters"`
Expand Down Expand Up @@ -312,7 +369,7 @@ type PromoteCandidateChangesetConfig struct {
}

func (p PromoteCandidateChangesetConfig) Validate(e cldf.Environment) (map[uint64]uint32, error) {
state, err := stateview.LoadOnchainState(e)
state, err := loadOnchainStateForCandidateChangesets(e)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -398,7 +455,7 @@ func PromoteCandidateChangeset(
if err != nil {
return cldf.ChangesetOutput{}, fmt.Errorf("%w: %w", cldf.ErrInvalidConfig, err)
}
state, err := stateview.LoadOnchainState(e)
state, err := loadOnchainStateForCandidateChangesets(e)
if err != nil {
return cldf.ChangesetOutput{}, err
}
Expand Down Expand Up @@ -630,7 +687,7 @@ func AddDonAndSetCandidateChangeset(
e cldf.Environment,
cfg AddDonAndSetCandidateChangesetConfig,
) (cldf.ChangesetOutput, error) {
state, err := stateview.LoadOnchainState(e)
state, err := loadOnchainStateForCandidateChangesets(e)
if err != nil {
return cldf.ChangesetOutput{}, err
}
Expand Down Expand Up @@ -763,7 +820,7 @@ func SetCandidateChangeset(
e cldf.Environment,
cfg SetCandidateChangesetConfig,
) (cldf.ChangesetOutput, error) {
state, err := stateview.LoadOnchainState(e)
state, err := loadOnchainStateForCandidateChangesets(e)
if err != nil {
return cldf.ChangesetOutput{}, err
}
Expand Down
116 changes: 116 additions & 0 deletions deployment/ccip/changeset/v1_6/cs_ccip_home_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ import (
"time"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
chain_selectors "github.com/smartcontractkit/chain-selectors"
"github.com/stretchr/testify/assert"
"golang.org/x/exp/maps"

"github.com/smartcontractkit/chainlink-ccip/pluginconfig"
"github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3"
cldf_chain "github.com/smartcontractkit/chainlink-deployments-framework/chain"
"github.com/smartcontractkit/chainlink-deployments-framework/datastore"

"github.com/smartcontractkit/chainlink-ccip/chainconfig"
cciptypes "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3"
Expand Down Expand Up @@ -549,3 +553,115 @@ func Test_UpdateChainConfigs(t *testing.T) {
})
}
}

func Test_SetCandidateAcceptsUSDCTokenPoolProxyFromDataStore(t *testing.T) {
t.Parallel()

tenv, _ := testhelpers.NewMemoryEnvironment(t,
testhelpers.WithNumOfChains(2),
testhelpers.WithNumOfNodes(4))
dest := remoteChainSelector(
tenv.Env.BlockChains.ListChainSelectors(cldf_chain.WithFamily(chain_selectors.FamilyEVM)),
tenv.HomeChainSel,
)
proxyAddress := common.HexToAddress("0x1000000000000000000000000000000000000001")

ds := datastore.NewMemoryDataStore()
require.NoError(t, ds.Addresses().Add(datastore.AddressRef{
ChainSelector: dest,
Address: proxyAddress.Hex(),
Type: datastore.ContractType(shared.USDCTokenPoolProxy),
Version: &deployment.Version1_7_0,
Qualifier: "proxy-only-in-datastore",
}))
tenv.Env.DataStore = ds.Seal()

_, err := commonchangeset.Apply(t, tenv.Env,
commonchangeset.Configure(
cldf.CreateLegacyChangeSet(v1_6.SetCandidateChangeset),
setCandidateExecConfig(tenv.HomeChainSel, tenv.FeedChainSel, dest, proxyAddress.Hex()),
),
)
require.NoError(t, err)
}

func Test_SetCandidateErrorsOnDuplicateUSDCTokenPoolProxyInDataStore(t *testing.T) {
t.Parallel()

tenv, _ := testhelpers.NewMemoryEnvironment(t,
testhelpers.WithNumOfChains(2),
testhelpers.WithNumOfNodes(4))
dest := remoteChainSelector(
tenv.Env.BlockChains.ListChainSelectors(cldf_chain.WithFamily(chain_selectors.FamilyEVM)),
tenv.HomeChainSel,
)

ds := datastore.NewMemoryDataStore()
for i, ref := range []struct {
address string
qualifier string
}{
{address: "0x1000000000000000000000000000000000000001", qualifier: "proxy-a"},
{address: "0x2000000000000000000000000000000000000002", qualifier: "proxy-b"},
} {
require.NoError(t, ds.Addresses().Add(datastore.AddressRef{
ChainSelector: dest,
Address: common.HexToAddress(ref.address).Hex(),
Type: datastore.ContractType(shared.USDCTokenPoolProxy),
Version: &deployment.Version1_7_0,
Qualifier: ref.qualifier,
}), "add datastore ref %d", i)
}
tenv.Env.DataStore = ds.Seal()

_, err := commonchangeset.Apply(t, tenv.Env,
commonchangeset.Configure(
cldf.CreateLegacyChangeSet(v1_6.SetCandidateChangeset),
setCandidateExecConfig(tenv.HomeChainSel, tenv.FeedChainSel, dest, "0x1000000000000000000000000000000000000001"),
),
)
require.Error(t, err)
require.ErrorContains(t, err, "multiple datastore entries found for USDCTokenPoolProxy 1.7.0")
}

func setCandidateExecConfig(homeChainSel, feedChainSel, dest uint64, sourcePoolAddress string) v1_6.SetCandidateChangesetConfig {
return v1_6.SetCandidateChangesetConfig{
SetCandidateConfigBase: v1_6.SetCandidateConfigBase{
HomeChainSelector: homeChainSel,
FeedChainSelector: feedChainSel,
},
PluginInfo: []v1_6.SetCandidatePluginInfo{
{
PluginType: types.PluginTypeCCIPExec,
OCRConfigPerRemoteChainSelector: map[uint64]v1_6.CCIPOCRParams{
dest: v1_6.DeriveOCRParamsForExec(v1_6.SimulationTest, []pluginconfig.TokenDataObserverConfig{
{
Type: pluginconfig.USDCCCTPHandlerType,
Version: "1.0",
USDCCCTPObserverConfig: &pluginconfig.USDCCCTPObserverConfig{
AttestationConfig: pluginconfig.AttestationConfig{
AttestationAPI: "http://example.com",
},
Tokens: map[ccipocr3.ChainSelector]pluginconfig.USDCCCTPTokenConfig{
ccipocr3.ChainSelector(dest): {
SourcePoolAddress: sourcePoolAddress,
SourceMessageTransmitterAddr: common.HexToAddress("0x3000000000000000000000000000000000000003").Hex(),
},
},
},
},
}, nil),
},
},
},
}
}

func remoteChainSelector(selectors []uint64, exclude uint64) uint64 {
for _, selector := range selectors {
if selector != exclude {
return selector
}
}
return 0
}
Loading