Skip to content
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
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()
}
56 changes: 30 additions & 26 deletions mcms/changesets/set-config/changeset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,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 @@ -240,14 +239,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 @@ -257,15 +256,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 @@ -285,17 +284,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 @@ -307,17 +306,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 @@ -331,10 +330,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 @@ -353,17 +352,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 @@ -385,21 +384,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
86 changes: 62 additions & 24 deletions mcms/changesets/set-config/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,18 @@ import (

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

// 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"
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"
transfertotimelock "github.com/smartcontractkit/cld-changesets/mcms/changesets/transfer-to-timelock"
_ "github.com/smartcontractkit/cld-changesets/mcms/evm/deploy"
_ "github.com/smartcontractkit/cld-changesets/mcms/evm/readers"
evmreaders "github.com/smartcontractkit/cld-changesets/mcms/evm/readers"
_ "github.com/smartcontractkit/cld-changesets/mcms/evm/transfer-to-timelock"
_ "github.com/smartcontractkit/cld-changesets/mcms/solana/deploy"
)

func contractRef(chainSelector uint64, contractType cldf.ContractType, qualifier string) refkey.RefKey {
Expand Down Expand Up @@ -88,10 +93,9 @@ func deployEVMMCMSWithTimelock(t *testing.T, rt *runtime.Runtime, selectors ...u
configByChain[selector] = cfg
}

err := rt.Exec(
// TODO: replace with new deploy changeset when available
runtime.ChangesetTask(cldf.CreateLegacyChangeSet(legacymcms.DeployMCMSWithTimelockV2), configByChain),
)
err := rt.Exec(runtime.ChangesetTask(deploy.Changeset{}, deploy.Input{
ConfigByChain: configByChain,
}))
require.NoError(t, err)
}

Expand All @@ -107,17 +111,25 @@ func newEVMRuntimeWithDeploy(t *testing.T, selectors ...uint64) *runtime.Runtime
func transferEVMMCMSToTimelock(t *testing.T, rt *runtime.Runtime, selector uint64) {
t.Helper()

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

err := rt.Exec(
runtime.ChangesetTask(cldf.CreateLegacyChangeSet(legacymcms.TransferToMCMSWithTimelockV2), legacymcms.TransferToMCMSWithTimelockConfig{
ContractsByChain: map[uint64][]common.Address{
selector: {
mcmsState.ProposerMcm.Address(),
mcmsState.BypasserMcm.Address(),
mcmsState.CancellerMcm.Address(),
runtime.ChangesetTask(transfertotimelock.Changeset{}, transfertotimelock.Input{
Cfg: transfertotimelock.Config{
ContractsByChain: map[uint64][]common.Address{
selector: {
mcmsRefs.Proposer,
mcmsRefs.Bypasser,
mcmsRefs.Canceller,
},
},
},
MCMS: &cldf.MCMSTimelockProposalInput{
TimelockAction: mcmstypes.TimelockActionBypass,
ValidUntil: uint32(time.Now().Add(2 * time.Hour).UTC().Unix()), //nolint:gosec // test timestamp
TimelockDelay: mcmstypes.NewDuration(0),
Description: "transfer MCM to timelock",
},
}),
runtime.SignAndExecuteProposalsTask([]*ecdsa.PrivateKey{cldftesthelpers.TestXXXMCMSSigner}),
)
Expand All @@ -133,34 +145,60 @@ func newEVMRuntimeWithDeployAndTransfer(t *testing.T, selector uint64) *runtime.
return rt
}

func evmMCMSChainState(t *testing.T, rt *runtime.Runtime, selector uint64) (*evmstate.MCMSWithTimelockState, cldf_evm.Chain) {
type evmMCMSRefs struct {
Timelock common.Address
Proposer common.Address
Canceller common.Address
Bypasser common.Address
}

func loadEVMMCMSRefs(t *testing.T, rt *runtime.Runtime, selector uint64) (evmMCMSRefs, cldf_evm.Chain) {
t.Helper()

chain := rt.Environment().BlockChains.EVMChains()[selector]
addrs, err := rt.State().AddressBook.AddressesForChain(selector)
require.NoError(t, err)
env := rt.Environment()
chain := env.BlockChains.EVMChains()[selector]

mcmsState, err := evmstate.MaybeLoadMCMSWithTimelockChainState(chain, addrs)
reader := evmreaders.Reader{}
timelock, err := reader.GetTimelockRef(env, selector, cldf.MCMSTimelockProposalInput{})
require.NoError(t, err)
proposer, err := reader.GetMCMSRef(env, selector, cldf.MCMSTimelockProposalInput{
TimelockAction: mcmstypes.TimelockActionSchedule,
})
require.NoError(t, err)
canceller, err := reader.GetMCMSRef(env, selector, cldf.MCMSTimelockProposalInput{
TimelockAction: mcmstypes.TimelockActionCancel,
})
require.NoError(t, err)
bypasser, err := reader.GetMCMSRef(env, selector, cldf.MCMSTimelockProposalInput{
TimelockAction: mcmstypes.TimelockActionBypass,
})
require.NoError(t, err)

return mcmsState, chain
return evmMCMSRefs{
Timelock: common.HexToAddress(timelock.Address),
Proposer: common.HexToAddress(proposer.Address),
Canceller: common.HexToAddress(canceller.Address),
Bypasser: common.HexToAddress(bypasser.Address),
}, chain
}

func newSolanaRuntimeWithDeploy(t *testing.T, selector uint64) *runtime.Runtime {
t.Helper()

programsPath, programIDs, ab := soltestutils.PreloadMCMS(t, selector)
programsPath, programIDs := soltestutils.LoadMCMSPrograms(t, t.TempDir())
rt, err := runtime.New(t.Context(), runtime.WithEnvOpts(
environment.WithSolanaContainer(t, []uint64{selector}, programsPath, programIDs),
environment.WithAddressBook(ab),
environment.WithDatastore(solanatest.PreloadDatastoreWithMCMSPrograms(t, selector)),
environment.WithLogger(logger.Test(t)),
))
require.NoError(t, err)
require.Contains(t, rt.Environment().BlockChains.SolanaChains(), selector)

err = rt.Exec(
runtime.ChangesetTask(cldf.CreateLegacyChangeSet(legacymcms.DeployMCMSWithTimelockV2), map[uint64]cldfproposalutils.MCMSWithTimelockConfig{
selector: cldftesthelpers.SingleGroupTimelockConfig(t),
runtime.ChangesetTask(deploy.Changeset{}, deploy.Input{
ConfigByChain: map[uint64]cldfproposalutils.MCMSWithTimelockConfig{
selector: cldftesthelpers.SingleGroupTimelockConfig(t),
},
}),
)
require.NoError(t, err)
Expand Down
9 changes: 6 additions & 3 deletions mcms/evm/firedrill/changeset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ import (
mcmstypes "github.com/smartcontractkit/mcms/types"
"github.com/stretchr/testify/require"

legacymcms "github.com/smartcontractkit/cld-changesets/legacy/mcms/changesets"
"github.com/smartcontractkit/cld-changesets/mcms/changesets/deploy"
firedrill "github.com/smartcontractkit/cld-changesets/mcms/changesets/firedrill"

_ "github.com/smartcontractkit/cld-changesets/mcms/evm/deploy"
_ "github.com/smartcontractkit/cld-changesets/mcms/evm/firedrill"
_ "github.com/smartcontractkit/cld-changesets/mcms/evm/readers"
)
Expand Down Expand Up @@ -66,8 +67,10 @@ func newEVMFireDrillRuntime(t *testing.T, selector uint64) *runtime.Runtime {
require.NoError(t, err)

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