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
39 changes: 39 additions & 0 deletions internal/testutil/solanatest/datastore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Package solanatest provides test utilities for Solana MCMS integration tests.
package solanatest

import (
"testing"

"github.com/smartcontractkit/chainlink-deployments-framework/datastore"
mcmscontracts "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/contracts/mcms"
"github.com/stretchr/testify/require"

"github.com/smartcontractkit/cld-changesets/internal/semvers"
"github.com/smartcontractkit/cld-changesets/legacy/pkg/family/solana/solutils"
)

// PreloadDatastoreWithMCMSPrograms seeds a datastore with canonical MCMS program IDs
// for the given chain selector. Use with the mcms/changesets/deploy changeset.
func PreloadDatastoreWithMCMSPrograms(t *testing.T, selector uint64) datastore.DataStore {
t.Helper()

v := semvers.V1_0_0
ds := datastore.NewMemoryDataStore()
for _, entry := range []struct {
addr string
ct datastore.ContractType
}{
{solutils.GetProgramID(solutils.ProgAccessController), datastore.ContractType(mcmscontracts.AccessControllerProgram)},
{solutils.GetProgramID(solutils.ProgMCM), datastore.ContractType(mcmscontracts.ManyChainMultisigProgram)},
{solutils.GetProgramID(solutils.ProgTimelock), datastore.ContractType(mcmscontracts.RBACTimelockProgram)},
} {
require.NoError(t, ds.Addresses().Add(datastore.AddressRef{
ChainSelector: selector,
Address: entry.addr,
Type: entry.ct,
Version: &v,
}))
}

return ds.Seal()
}
41 changes: 33 additions & 8 deletions legacy/pkg/family/solana/changesets/transfer_ownership.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,14 +377,7 @@ func (t TransferMCMSToTimelockSolana) VerifyPreconditions(
env cldf.Environment, config TransferMCMSToTimelockSolanaConfig,
) error {
for _, chainSelector := range config.Chains {
addressBook := env.ExistingAddresses //nolint:staticcheck // SA1019: legacy AddressBook until DataStore migration
err := addressBookContains(addressBook, chainSelector,
mcmscontracts.RBACTimelockProgram,
mcmscontracts.RBACTimelock,
mcmscontracts.ManyChainMultisigProgram,
mcmscontracts.ProposerManyChainMultisig,
)
if err != nil {
if err := envContainsMCMSBundle(env, chainSelector); err != nil {
return err
}
}
Expand Down Expand Up @@ -550,4 +543,36 @@ func addressBookContains(addressBook cldf.AddressBook, chainSelector uint64, cty
return nil
}

func envContainsMCMSBundle(env cldf.Environment, chainSelector uint64) error {
return envContainsContractTypes(env, chainSelector,
mcmscontracts.RBACTimelockProgram,
mcmscontracts.RBACTimelock,
mcmscontracts.ManyChainMultisigProgram,
mcmscontracts.ProposerManyChainMultisig,
)
}

func envContainsContractTypes(env cldf.Environment, chainSelector uint64, ctypes ...cldf.ContractType) error {
for _, ctype := range ctypes {
if env.ExistingAddresses != nil { //nolint:staticcheck // SA1019: legacy AddressBook until DataStore migration
if _, err := cldf.SearchAddressBook(env.ExistingAddresses, chainSelector, ctype); err == nil {
continue
}
}
if env.DataStore != nil {
refs := env.DataStore.Addresses().Filter(
datastore.AddressRefByChainSelector(chainSelector),
datastore.AddressRefByType(datastore.ContractType(ctype)),
)
if len(refs) > 0 {
continue
}
}

return fmt.Errorf("environment does not contain a %s contract for chain %d", ctype, chainSelector)
}

return nil
}

var solanaAddress = mcmssolanasdk.ContractAddress
20 changes: 3 additions & 17 deletions legacy/pkg/family/solana/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,27 +192,13 @@ type MCMSWithTimelockState struct {
*MCMSWithTimelockPrograms
}

// MaybeLoadMCMSWithTimelockState loads MCMSWithTimelockState for each provided chain selector from the environment's
// Solana chains and ExistingAddresses (address book).
// MaybeLoadMCMSWithTimelockState loads MCMSWithTimelockState for each provided chain selector from the environment.
func MaybeLoadMCMSWithTimelockState(env cldf.Environment, chainSelectors []uint64) (map[uint64]*MCMSWithTimelockState, error) {
result := map[uint64]*MCMSWithTimelockState{}
solChains := env.BlockChains.SolanaChains()
for _, chainSelector := range chainSelectors {
chain, ok := solChains[chainSelector]
if !ok {
return nil, fmt.Errorf("chain %d not found", chainSelector)
}
addressesChain, err := env.ExistingAddresses.AddressesForChain(chainSelector) //nolint:staticcheck // SA1019: AddressBook deprecated; Solana MCMS load merges with address book until full DataStore migration.
if err != nil {
if !errors.Is(err, cldf.ErrChainNotFound) {
return nil, fmt.Errorf("unable to get addresses for chain %v: %w", chainSelector, err)
}
// chain not found in address book, initialize empty
addressesChain = make(map[string]cldf.TypeAndVersion)
}
state, err := MaybeLoadMCMSWithTimelockChainState(chain, addressesChain)
state, err := GetState(env, chainSelector)
if err != nil {
return nil, fmt.Errorf("unable to load mcms and timelock solana chain state: %w", err)
return nil, fmt.Errorf("unable to load mcms and timelock solana chain state for chain %d: %w", chainSelector, err)
}
result[chainSelector] = state
Comment on lines +195 to 203
}
Expand Down
10 changes: 10 additions & 0 deletions legacy/pkg/family/solana/testutils/preload.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,13 @@ func PreloadMCMS(t *testing.T, selector uint64) (string, map[string]string, *cld

return programsPath, programIDs, ab
}

// LoadMCMSPrograms downloads MCMS Solana program artifacts once per test process
// and returns the shared cache directory plus program IDs.
func LoadMCMSPrograms(t *testing.T) (string, map[string]string) {
t.Helper()

acquireSolanaTestIsolation(t)

return sharedMCMSPrograms(t)
}
62 changes: 33 additions & 29 deletions mcms/changesets/set-config/changeset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (

chain_selectors "github.com/smartcontractkit/chain-selectors"
cldf_evm "github.com/smartcontractkit/chainlink-deployments-framework/chain/evm"
"github.com/smartcontractkit/chainlink-deployments-framework/datastore"
cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment"
mcmscontracts "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/contracts/mcms"
cldfproposalutils "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/mcms/proposalutils"
Expand All @@ -25,15 +26,14 @@ import (

"github.com/smartcontractkit/cld-changesets/datastore/refkey"
"github.com/smartcontractkit/cld-changesets/internal/semvers"

// TODO: remove legacymcms import once remaining MCMS changesets are migrated out of legacy/mcms/changesets.
legacymcms "github.com/smartcontractkit/cld-changesets/legacy/mcms/changesets"
evmstate "github.com/smartcontractkit/cld-changesets/legacy/pkg/family/evm"
solstate "github.com/smartcontractkit/cld-changesets/legacy/pkg/family/solana"
solchangesets "github.com/smartcontractkit/cld-changesets/legacy/pkg/family/solana/changesets"
soltestutils "github.com/smartcontractkit/cld-changesets/legacy/pkg/family/solana/testutils"
"github.com/smartcontractkit/cld-changesets/mcms/changesets/deploy"
setconfig "github.com/smartcontractkit/cld-changesets/mcms/changesets/set-config"
_ "github.com/smartcontractkit/cld-changesets/mcms/changesets/set-config/all"
_ "github.com/smartcontractkit/cld-changesets/mcms/evm/deploy"
evmreaders "github.com/smartcontractkit/cld-changesets/mcms/evm/readers"
)

//nolint:paralleltest // global mcm.SetProgramID state and shared Solana CTF container setup
Expand Down Expand Up @@ -241,14 +241,14 @@ func TestChangeset_EVM(t *testing.T) {
},
} {
t.Run(tt.name, func(t *testing.T) {
mcmsState, _ := evmMCMSChainState(t, rt, tt.selector)
mcmsRefs, _ := loadEVMMCMSRefs(t, rt, tt.selector)
originalCfg := cldftesthelpers.SingleGroupMCMS(t)

var targets []setconfig.ContractSetConfig
var mcmsInput *cldf.MCMSTimelockProposalInput
if tt.useMCMS {
cfgCanceller := cldftesthelpers.SingleGroupMCMS(t)
cfgCanceller.Signers = append(cfgCanceller.Signers, mcmsState.BypasserMcm.Address())
cfgCanceller.Signers = append(cfgCanceller.Signers, mcmsRefs.Bypasser)
cfgCanceller.Quorum = 2
targets = []setconfig.ContractSetConfig{
{
Expand All @@ -258,15 +258,15 @@ func TestChangeset_EVM(t *testing.T) {
}
mcmsInput = newMCMSInput(mcmstypes.TimelockActionBypass, "Set config proposal", "")
} else {
timelockAddress := mcmsState.Timelock.Address()
timelockAddress := mcmsRefs.Timelock

cfgProposer := cldftesthelpers.SingleGroupMCMS(t)
cfgProposer.Signers = append(cfgProposer.Signers, timelockAddress)
cfgProposer.Quorum = 2
cfgCanceller := cldftesthelpers.SingleGroupMCMS(t)
cfgBypasser := cldftesthelpers.SingleGroupMCMS(t)
cfgBypasser.Signers = append(cfgBypasser.Signers, timelockAddress)
cfgBypasser.Signers = append(cfgBypasser.Signers, mcmsState.ProposerMcm.Address())
cfgBypasser.Signers = append(cfgBypasser.Signers, mcmsRefs.Proposer)
cfgBypasser.Quorum = 3

targets = mcmsTargets(tt.selector, cfgProposer, cfgCanceller, cfgBypasser)
Expand All @@ -286,17 +286,17 @@ func TestChangeset_EVM(t *testing.T) {

if tt.useMCMS {
cfgCanceller := targets[0].Config
newConf, err := inspector.GetConfig(t.Context(), mcmsState.CancellerMcm.Address().Hex())
newConf, err := inspector.GetConfig(t.Context(), mcmsRefs.Canceller.Hex())
require.NoError(t, err)
require.ElementsMatch(t, cfgCanceller.Signers, newConf.Signers)
require.Equal(t, cfgCanceller.Quorum, newConf.Quorum)

proposerConf, err := inspector.GetConfig(t.Context(), mcmsState.ProposerMcm.Address().Hex())
proposerConf, err := inspector.GetConfig(t.Context(), mcmsRefs.Proposer.Hex())
require.NoError(t, err)
require.ElementsMatch(t, originalCfg.Signers, proposerConf.Signers)
require.Equal(t, originalCfg.Quorum, proposerConf.Quorum)

bypasserConf, err := inspector.GetConfig(t.Context(), mcmsState.BypasserMcm.Address().Hex())
bypasserConf, err := inspector.GetConfig(t.Context(), mcmsRefs.Bypasser.Hex())
require.NoError(t, err)
require.ElementsMatch(t, originalCfg.Signers, bypasserConf.Signers)
require.Equal(t, originalCfg.Quorum, bypasserConf.Quorum)
Expand All @@ -308,17 +308,17 @@ func TestChangeset_EVM(t *testing.T) {
cfgCanceller := targets[1].Config
cfgBypasser := targets[2].Config

newConf, err := inspector.GetConfig(t.Context(), mcmsState.ProposerMcm.Address().Hex())
newConf, err := inspector.GetConfig(t.Context(), mcmsRefs.Proposer.Hex())
require.NoError(t, err)
require.ElementsMatch(t, cfgProposer.Signers, newConf.Signers)
require.Equal(t, cfgProposer.Quorum, newConf.Quorum)

newConf, err = inspector.GetConfig(t.Context(), mcmsState.BypasserMcm.Address().Hex())
newConf, err = inspector.GetConfig(t.Context(), mcmsRefs.Bypasser.Hex())
require.NoError(t, err)
require.ElementsMatch(t, cfgBypasser.Signers, newConf.Signers)
require.Equal(t, cfgBypasser.Quorum, newConf.Quorum)

newConf, err = inspector.GetConfig(t.Context(), mcmsState.CancellerMcm.Address().Hex())
newConf, err = inspector.GetConfig(t.Context(), mcmsRefs.Canceller.Hex())
require.NoError(t, err)
require.ElementsMatch(t, cfgCanceller.Signers, newConf.Signers)
require.Equal(t, cfgCanceller.Quorum, newConf.Quorum)
Expand All @@ -332,10 +332,10 @@ func TestChangeset_EVM_PartialTargets(t *testing.T) {
selector := chain_selectors.TEST_90000001.Selector

rt := newEVMRuntimeWithDeploy(t, selector)
mcmsState, chain := evmMCMSChainState(t, rt, selector)
mcmsRefs, chain := loadEVMMCMSRefs(t, rt, selector)

cfgProposer := cldftesthelpers.SingleGroupMCMS(t)
cfgProposer.Signers = append(cfgProposer.Signers, mcmsState.Timelock.Address())
cfgProposer.Signers = append(cfgProposer.Signers, mcmsRefs.Timelock)
cfgProposer.Quorum = 2

err := rt.Exec(
Expand All @@ -354,17 +354,17 @@ func TestChangeset_EVM_PartialTargets(t *testing.T) {
inspector := evm.NewInspector(chain.Client)
originalCfg := cldftesthelpers.SingleGroupMCMS(t)

proposerConf, err := inspector.GetConfig(t.Context(), mcmsState.ProposerMcm.Address().Hex())
proposerConf, err := inspector.GetConfig(t.Context(), mcmsRefs.Proposer.Hex())
require.NoError(t, err)
require.ElementsMatch(t, cfgProposer.Signers, proposerConf.Signers)
require.Equal(t, cfgProposer.Quorum, proposerConf.Quorum)

cancellerConf, err := inspector.GetConfig(t.Context(), mcmsState.CancellerMcm.Address().Hex())
cancellerConf, err := inspector.GetConfig(t.Context(), mcmsRefs.Canceller.Hex())
require.NoError(t, err)
require.ElementsMatch(t, originalCfg.Signers, cancellerConf.Signers)
require.Equal(t, originalCfg.Quorum, cancellerConf.Quorum)

bypasserConf, err := inspector.GetConfig(t.Context(), mcmsState.BypasserMcm.Address().Hex())
bypasserConf, err := inspector.GetConfig(t.Context(), mcmsRefs.Bypasser.Hex())
require.NoError(t, err)
require.ElementsMatch(t, originalCfg.Signers, bypasserConf.Signers)
require.Equal(t, originalCfg.Quorum, bypasserConf.Quorum)
Expand All @@ -386,21 +386,26 @@ func TestChangeset_EVM_Qualifier(t *testing.T) {
rmnmcmsConfig.Qualifier = &rmnmcmsQualifier

err := rt.Exec(
runtime.ChangesetTask(cldf.CreateLegacyChangeSet(legacymcms.DeployMCMSWithTimelockV2), map[uint64]cldfproposalutils.MCMSWithTimelockConfig{
selector: cllccipConfig,
runtime.ChangesetTask(deploy.Changeset{}, deploy.Input{
ConfigByChain: map[uint64]cldfproposalutils.MCMSWithTimelockConfig{
selector: cllccipConfig,
},
}),
runtime.ChangesetTask(cldf.CreateLegacyChangeSet(legacymcms.DeployMCMSWithTimelockV2), map[uint64]cldfproposalutils.MCMSWithTimelockConfig{
selector: rmnmcmsConfig,
runtime.ChangesetTask(deploy.Changeset{}, deploy.Input{
ConfigByChain: map[uint64]cldfproposalutils.MCMSWithTimelockConfig{
selector: rmnmcmsConfig,
},
}),
)
require.NoError(t, err)

cllccipState, err := evmstate.MaybeLoadMCMSWithTimelockStateWithQualifier(rt.Environment(), []uint64{selector}, cllccipQualifier)
cllccipTimelock, err := evmreaders.Reader{}.GetTimelockRef(rt.Environment(), selector, cldf.MCMSTimelockProposalInput{
Qualifier: cllccipQualifier,
})
require.NoError(t, err)
require.NotNil(t, cllccipState[selector])

cfgProposer := cldftesthelpers.SingleGroupMCMS(t)
cfgProposer.Signers = append(cfgProposer.Signers, cllccipState[selector].Timelock.Address())
cfgProposer.Signers = append(cfgProposer.Signers, common.HexToAddress(cllccipTimelock.Address))
cfgProposer.Quorum = 2

for _, tt := range []struct {
Expand Down Expand Up @@ -479,9 +484,8 @@ func TestChangeset_Solana(t *testing.T) {
rt := newSolanaRuntimeWithDeploy(t, selector)
chain := rt.Environment().BlockChains.SolanaChains()[selector]

addrs, err := rt.State().AddressBook.AddressesForChain(selector)
require.NoError(t, err)
mcmsState, err := solstate.MaybeLoadMCMSWithTimelockChainState(chain, addrs)
refs := rt.State().DataStore.Addresses().Filter(datastore.AddressRefByChainSelector(selector))
mcmsState, err := solstate.MaybeLoadMCMSWithTimelockChainStateV2(refs)
require.NoError(t, err)
soltestutils.FundSignerPDAs(t, chain, mcmsState)

Expand Down
Loading
Loading