diff --git a/internal/testutil/solanatest/datastore.go b/internal/testutil/solanatest/datastore.go new file mode 100644 index 0000000..574f545 --- /dev/null +++ b/internal/testutil/solanatest/datastore.go @@ -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() +} diff --git a/mcms/changesets/set-config/changeset_test.go b/mcms/changesets/set-config/changeset_test.go index a74a0f8..6d41ea1 100644 --- a/mcms/changesets/set-config/changeset_test.go +++ b/mcms/changesets/set-config/changeset_test.go @@ -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 @@ -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{ { @@ -257,7 +256,7 @@ 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) @@ -265,7 +264,7 @@ func TestChangeset_EVM(t *testing.T) { 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) @@ -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) @@ -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) @@ -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( @@ -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) @@ -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 { diff --git a/mcms/changesets/set-config/helpers_test.go b/mcms/changesets/set-config/helpers_test.go index f04468f..d51cdf4 100644 --- a/mcms/changesets/set-config/helpers_test.go +++ b/mcms/changesets/set-config/helpers_test.go @@ -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 { @@ -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) } @@ -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}), ) @@ -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) diff --git a/mcms/evm/firedrill/changeset_test.go b/mcms/evm/firedrill/changeset_test.go index 6f7f31b..0ca3594 100644 --- a/mcms/evm/firedrill/changeset_test.go +++ b/mcms/evm/firedrill/changeset_test.go @@ -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" ) @@ -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) diff --git a/mcms/evm/set-config/operation_test.go b/mcms/evm/set-config/operation_test.go index 6cb68f8..06cbe8b 100644 --- a/mcms/evm/set-config/operation_test.go +++ b/mcms/evm/set-config/operation_test.go @@ -1,11 +1,9 @@ -package evmsetconfig +package evmsetconfig_test import ( "crypto/ecdsa" - "math/big" "testing" - "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" @@ -18,6 +16,8 @@ import ( "github.com/smartcontractkit/chainlink-deployments-framework/operations/optest" mcmsevm "github.com/smartcontractkit/mcms/sdk/evm" mcmstypes "github.com/smartcontractkit/mcms/types" + + evmsetconfig "github.com/smartcontractkit/cld-changesets/mcms/evm/set-config" ) func TestOpEVMSetConfigMCM_missingDeployerKey(t *testing.T) { @@ -25,38 +25,23 @@ func TestOpEVMSetConfigMCM_missingDeployerKey(t *testing.T) { _, err := operations.ExecuteOperation( optest.NewBundle(t), - OpEVMSetConfigMCM, + evmsetconfig.OpEVMSetConfigMCM, cldf_evm.Chain{Selector: chainselectors.TEST_90000001.Selector}, - OpEVMSetConfigInput{ + evmsetconfig.OpEVMSetConfigInput{ NoSend: false, - Target: MCMSetConfigTarget{ - Address: common.Address{1}, + Target: evmsetconfig.MCMSetConfigTarget{ + Address: common.Address{1}, + ContractType: mcmscontracts.ProposerManyChainMultisig, }, }, ) require.ErrorContains(t, err, "missing deployer key") } -func TestCloneTransactOptsWithGas(t *testing.T) { - t.Parallel() - - require.Nil(t, cloneTransactOptsWithGas(nil, 100, 200)) - - opts := &bind.TransactOpts{GasLimit: 1, GasPrice: big.NewInt(1)} - got := cloneTransactOptsWithGas(opts, 0, 0) - require.Equal(t, uint64(1), got.GasLimit) - require.Equal(t, int64(1), got.GasPrice.Int64()) - require.NotSame(t, opts, got) - - got = cloneTransactOptsWithGas(opts, 500_000, 30_000_000_000) - require.Equal(t, uint64(500_000), got.GasLimit) - require.Equal(t, uint64(30_000_000_000), got.GasPrice.Uint64()) -} - func TestOpEVMSetConfigInputGasOverridable(t *testing.T) { t.Parallel() - in := OpEVMSetConfigInput{GasLimit: 100, GasPrice: 200} + in := evmsetconfig.OpEVMSetConfigInput{GasLimit: 100, GasPrice: 200} gotLimit, gotPrice := in.GasBoostValues() require.Equal(t, uint64(100), gotLimit) require.Equal(t, uint64(200), gotPrice) @@ -96,10 +81,10 @@ func TestOpEVMSetConfigMCM(t *testing.T) { report, err := operations.ExecuteOperation( rt.Environment().OperationsBundle, - OpEVMSetConfigMCM, + evmsetconfig.OpEVMSetConfigMCM, chain, - OpEVMSetConfigInput{ - Target: MCMSetConfigTarget{ + evmsetconfig.OpEVMSetConfigInput{ + Target: evmsetconfig.MCMSetConfigTarget{ Address: refs.Canceller, Config: cfg, ContractType: mcmscontracts.CancellerManyChainMultisig, @@ -113,7 +98,7 @@ func TestOpEVMSetConfigMCM(t *testing.T) { require.Equal(t, !tt.noSend, report.Output.Confirmed) if tt.noSend { - batch, err := evmCallOutputsToBatch(selector, []EVMCallOutput{report.Output}) + batch, err := evmsetconfig.EVMCallOutputsToBatch(selector, []evmsetconfig.EVMCallOutput{report.Output}) require.NoError(t, err) require.Len(t, batch.Transactions, 1) require.NoError(t, rt.Exec( diff --git a/mcms/evm/set-config/register_test.go b/mcms/evm/set-config/register_test.go index 279c919..a01ecc8 100644 --- a/mcms/evm/set-config/register_test.go +++ b/mcms/evm/set-config/register_test.go @@ -1,4 +1,4 @@ -package evmsetconfig +package evmsetconfig_test import ( "fmt" @@ -12,15 +12,22 @@ import ( cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment" setconfig "github.com/smartcontractkit/cld-changesets/mcms/changesets/set-config" + evmsetconfig "github.com/smartcontractkit/cld-changesets/mcms/evm/set-config" + + _ "github.com/smartcontractkit/cld-changesets/mcms/evm/set-config" ) func TestRegistration(t *testing.T) { t.Parallel() - reg := Registration() + reg := evmsetconfig.Registration() require.Equal(t, chainselectors.FamilyEVM, reg.Family) - require.Equal(t, seqSetConfig, reg.Sequence) + require.NotNil(t, reg.Sequence) require.NotNil(t, reg.Verify) + + registered, err := setconfig.Registry.SequenceForFamily(chainselectors.FamilyEVM) + require.NoError(t, err) + require.Equal(t, reg.Sequence, registered) } func TestInitRegistersEVM(t *testing.T) { @@ -36,7 +43,7 @@ func TestInitRegistersEVM(t *testing.T) { t.Helper() seq, err := setconfig.Registry.SequenceForFamily(chainselectors.FamilyEVM) require.NoError(t, err) - require.Equal(t, seqSetConfig, seq) + require.NotNil(t, seq) }, }, { @@ -45,7 +52,7 @@ func TestInitRegistersEVM(t *testing.T) { t.Helper() got, err := setconfig.Registry.SequenceForChainSelector(chainselectors.TEST_90000001.Selector) require.NoError(t, err) - require.Equal(t, seqSetConfig, got) + require.NotNil(t, got) }, }, } @@ -62,7 +69,7 @@ func TestVerifyEVMChains(t *testing.T) { t.Parallel() selector := chainselectors.TEST_90000001.Selector - reg := Registration() + reg := evmsetconfig.Registration() tests := []struct { name string diff --git a/mcms/evm/set-config/sequence.go b/mcms/evm/set-config/sequence.go index 16c5715..4ddd221 100644 --- a/mcms/evm/set-config/sequence.go +++ b/mcms/evm/set-config/sequence.go @@ -76,7 +76,7 @@ func runEVMSetConfig( return out, nil } - batch, err := evmCallOutputsToBatch(in.ChainSelector, outs) + batch, err := EVMCallOutputsToBatch(in.ChainSelector, outs) if err != nil { return sequenceutils.OnChainOutput{}, err } @@ -109,7 +109,8 @@ func setConfigTargets(e cldf.Environment, configs []setconfig.ContractSetConfig) return targets, nil } -func evmCallOutputsToBatch(chainSelector uint64, outs []EVMCallOutput) (mcmstypes.BatchOperation, error) { +// EVMCallOutputsToBatch converts no-send set-config outputs into a single MCMS batch operation. +func EVMCallOutputsToBatch(chainSelector uint64, outs []EVMCallOutput) (mcmstypes.BatchOperation, error) { result := mcmstypes.BatchOperation{ ChainSelector: mcmstypes.ChainSelector(chainSelector), Transactions: []mcmstypes.Transaction{}, diff --git a/mcms/evm/set-config/sequence_test.go b/mcms/evm/set-config/sequence_test.go index 4d6f979..62bd71d 100644 --- a/mcms/evm/set-config/sequence_test.go +++ b/mcms/evm/set-config/sequence_test.go @@ -1,7 +1,6 @@ -package evmsetconfig +package evmsetconfig_test import ( - "context" "crypto/ecdsa" "testing" "time" @@ -14,6 +13,7 @@ import ( chainselectors "github.com/smartcontractkit/chain-selectors" "github.com/smartcontractkit/chainlink-deployments-framework/chain" cldf_evm "github.com/smartcontractkit/chainlink-deployments-framework/chain/evm" + "github.com/smartcontractkit/chainlink-deployments-framework/changeset/sequenceutils" "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" @@ -21,6 +21,7 @@ import ( cldftesthelpers "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/mcms/proposalutils/testhelpers" "github.com/smartcontractkit/chainlink-deployments-framework/engine/test/environment" "github.com/smartcontractkit/chainlink-deployments-framework/engine/test/runtime" + "github.com/smartcontractkit/chainlink-deployments-framework/operations" "github.com/smartcontractkit/chainlink-deployments-framework/operations/optest" "github.com/smartcontractkit/chainlink-deployments-framework/pkg/logger" mcmsevm "github.com/smartcontractkit/mcms/sdk/evm" @@ -29,11 +30,16 @@ 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" + "github.com/smartcontractkit/cld-changesets/mcms/changesets/deploy" setconfig "github.com/smartcontractkit/cld-changesets/mcms/changesets/set-config" + transfertotimelock "github.com/smartcontractkit/cld-changesets/mcms/changesets/transfer-to-timelock" _ "github.com/smartcontractkit/cld-changesets/mcms/evm/readers" evmreaders "github.com/smartcontractkit/cld-changesets/mcms/evm/readers" + + _ "github.com/smartcontractkit/cld-changesets/mcms/changesets/set-config/all" + _ "github.com/smartcontractkit/cld-changesets/mcms/evm/deploy" + _ "github.com/smartcontractkit/cld-changesets/mcms/evm/set-config" + _ "github.com/smartcontractkit/cld-changesets/mcms/evm/transfer-to-timelock" ) func TestRunEVMSetConfig(t *testing.T) { @@ -142,11 +148,11 @@ func newEVMSetConfigRuntime(t *testing.T, selector uint64) *runtime.Runtime { )) require.NoError(t, err) - err = rt.Exec( - runtime.ChangesetTask(cldf.CreateLegacyChangeSet(legacymcms.DeployMCMSWithTimelockV2), map[uint64]cldfproposalutils.MCMSWithTimelockConfig{ + err = rt.Exec(runtime.ChangesetTask(deploy.Changeset{}, deploy.Input{ + ConfigByChain: map[uint64]cldfproposalutils.MCMSWithTimelockConfig{ selector: cldftesthelpers.SingleGroupTimelockConfig(t), - }), - ) + }, + })) require.NoError(t, err) return rt @@ -190,14 +196,22 @@ func transferEVMMCMSToTimelock(t *testing.T, rt *runtime.Runtime, selector uint6 t.Helper() err := rt.Exec( - runtime.ChangesetTask(cldf.CreateLegacyChangeSet(legacymcms.TransferToMCMSWithTimelockV2), legacymcms.TransferToMCMSWithTimelockConfig{ - ContractsByChain: map[uint64][]common.Address{ - selector: { - refs.Proposer, - refs.Bypasser, - refs.Canceller, + runtime.ChangesetTask(transfertotimelock.Changeset{}, transfertotimelock.Input{ + Cfg: transfertotimelock.Config{ + ContractsByChain: map[uint64][]common.Address{ + selector: { + refs.Proposer, + refs.Bypasser, + refs.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}), ) @@ -247,7 +261,25 @@ func assertEVMConfigEquals(t *testing.T, inspector *mcmsevm.Inspector, address c require.Equal(t, want.Quorum, got.Quorum) } -func TestSetConfigTargets(t *testing.T) { +func runEVMSetConfig( + b operations.Bundle, + deps setconfig.Deps, + in setconfig.ChainInput, +) (sequenceutils.OnChainOutput, error) { + seq, err := setconfig.Registry.SequenceForFamily(chainselectors.FamilyEVM) + if err != nil { + return sequenceutils.OnChainOutput{}, err + } + + report, err := operations.ExecuteSequence(b, seq, deps, in) + if err != nil { + return sequenceutils.OnChainOutput{}, err + } + + return report.Output, nil +} + +func TestSetConfigTargets_errors(t *testing.T) { t.Parallel() const selector uint64 = 90000001 @@ -261,23 +293,28 @@ func TestSetConfigTargets(t *testing.T) { Type: datastore.ContractType(mcmscontracts.ProposerManyChainMultisig), Version: semver.MustParse("1.0.0"), })) - env := cldf.Environment{ - DataStore: ds.Seal(), - GetContext: context.Background, + deps := setconfig.Deps{ + BlockChains: chain.NewBlockChains(map[uint64]chain.BlockChain{ + selector: cldf_evm.Chain{Selector: selector}, + }), + DataStore: ds.Seal(), } - got, err := setConfigTargets(env, []setconfig.ContractSetConfig{ - {Ref: validRef, Config: cfg}, - }) - require.NoError(t, err) - require.Len(t, got, 1) - require.Equal(t, "0x0000000000000000000000000000000000000100", got[0].Address.Hex()) - require.Equal(t, cfg, got[0].Config) - - _, err = setConfigTargets(env, []setconfig.ContractSetConfig{ - {Ref: refkey.New(selector, datastore.ContractType(mcmscontracts.CancellerManyChainMultisig), &semvers.V1_0_0, ""), Config: cfg}, - }) - require.ErrorContains(t, err, "targets[0]:") + _, err := runEVMSetConfig( + optest.NewBundle(t), + deps, + setconfig.ChainInput{ + ChainSelector: selector, + Targets: []setconfig.ContractSetConfig{ + {Ref: validRef, Config: cfg}, + { + Ref: refkey.New(selector, datastore.ContractType(mcmscontracts.CancellerManyChainMultisig), &semvers.V1_0_0, ""), + Config: cfg, + }, + }, + }, + ) + require.ErrorContains(t, err, "targets[1]:") invalidDS := datastore.NewMemoryDataStore() require.NoError(t, invalidDS.Addresses().Add(datastore.AddressRef{ @@ -286,9 +323,17 @@ func TestSetConfigTargets(t *testing.T) { Type: datastore.ContractType(mcmscontracts.ProposerManyChainMultisig), Version: semver.MustParse("1.0.0"), })) - _, err = setConfigTargets(cldf.Environment{DataStore: invalidDS.Seal(), GetContext: context.Background}, []setconfig.ContractSetConfig{ - {Ref: validRef, Config: cfg}, - }) + _, err = runEVMSetConfig( + optest.NewBundle(t), + setconfig.Deps{ + BlockChains: deps.BlockChains, + DataStore: invalidDS.Seal(), + }, + setconfig.ChainInput{ + ChainSelector: selector, + Targets: []setconfig.ContractSetConfig{{Ref: validRef, Config: cfg}}, + }, + ) require.ErrorContains(t, err, `invalid EVM address "not-an-address"`) } diff --git a/mcms/evm/set-config/validate_test.go b/mcms/evm/set-config/validate_test.go index a65d387..c36e60e 100644 --- a/mcms/evm/set-config/validate_test.go +++ b/mcms/evm/set-config/validate_test.go @@ -1,4 +1,4 @@ -package evmsetconfig +package evmsetconfig_test import ( "context" @@ -9,6 +9,8 @@ import ( "github.com/stretchr/testify/require" chainselectors "github.com/smartcontractkit/chain-selectors" + "github.com/smartcontractkit/chainlink-deployments-framework/chain" + 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" @@ -16,8 +18,10 @@ import ( mcmstypes "github.com/smartcontractkit/mcms/types" setconfig "github.com/smartcontractkit/cld-changesets/mcms/changesets/set-config" + evmsetconfig "github.com/smartcontractkit/cld-changesets/mcms/evm/set-config" _ "github.com/smartcontractkit/cld-changesets/mcms/evm/readers" + _ "github.com/smartcontractkit/cld-changesets/mcms/evm/set-config" ) type validateRefSpec struct { @@ -35,6 +39,7 @@ func TestValidateMCMSIfPresent(t *testing.T) { ValidUntil: uint32(time.Now().Add(2 * time.Hour).UTC().Unix()), //nolint:gosec // test timestamp TimelockDelay: mcmstypes.NewDuration(time.Second), } + reg := evmsetconfig.Registration() tests := []struct { name string @@ -80,12 +85,12 @@ func TestValidateMCMSIfPresent(t *testing.T) { addValidateRef(t, ds, selector, ref.contractType, ref.address, version, "") } - err := validateMCMSIfPresent( - validateTestEnv(ds.Seal()), - setconfig.ChainInput{ + err := reg.Verify( + validateTestEnv(ds.Seal(), selector), + []setconfig.ChainInput{{ ChainSelector: selector, MCMS: tt.mcms, - }, + }}, ) if tt.wantErr == "" { require.NoError(t, err) @@ -117,10 +122,13 @@ func addValidateRef( })) } -func validateTestEnv(ds datastore.DataStore) cldf.Environment { +func validateTestEnv(ds datastore.DataStore, selector uint64) cldf.Environment { return cldf.Environment{ - Logger: logger.Nop(), - DataStore: ds, + Logger: logger.Nop(), + DataStore: ds, + BlockChains: chain.NewBlockChains(map[uint64]chain.BlockChain{ + selector: cldf_evm.Chain{Selector: selector}, + }), GetContext: context.Background, } } diff --git a/mcms/solana/deploy/changeset_test.go b/mcms/solana/deploy/changeset_test.go index d0f81a8..78b9ef8 100644 --- a/mcms/solana/deploy/changeset_test.go +++ b/mcms/solana/deploy/changeset_test.go @@ -18,6 +18,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/cld-changesets/internal/semvers" + "github.com/smartcontractkit/cld-changesets/internal/testutil/solanatest" legacysolana "github.com/smartcontractkit/cld-changesets/legacy/pkg/family/solana" solutils "github.com/smartcontractkit/cld-changesets/legacy/pkg/family/solana/solutils" soltestutils "github.com/smartcontractkit/cld-changesets/legacy/pkg/family/solana/testutils" @@ -38,34 +39,6 @@ type solanaMCMSTimelockRefs struct { Timelock string } -// datastoreWithMCMSPrograms seeds the datastore with canonical MCMS program IDs. -// The test validator preloads the same programs via WithSolanaContainer; program -// deploy is skipped because artifacts lack -keypair.json files required by -// solana program deploy for fixed program IDs. -func datastoreWithMCMSPrograms(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() -} - func newSolanaDeployRuntime(t *testing.T, selector uint64, ds datastore.DataStore) *runtime.Runtime { t.Helper() @@ -234,7 +207,7 @@ func TestDeployMCMSWithTimelock_Solana_FreshDeploy(t *testing.T) { selector := chainselectors.TEST_22222222222222222222222222222222222222222222.Selector cfg := cldftesthelpers.SingleGroupTimelockConfig(t) - rt := newSolanaDeployRuntime(t, selector, datastoreWithMCMSPrograms(t, selector)) + rt := newSolanaDeployRuntime(t, selector, solanatest.PreloadDatastoreWithMCMSPrograms(t, selector)) execSolanaDeployChangeset(t, rt, selector, cfg) assertSolanaDeployDatastoreRefs(t, rt, selector) @@ -246,7 +219,7 @@ func TestDeployMCMSWithTimelock_Solana_PartialDeploy(t *testing.T) { selector := chainselectors.TEST_22222222222222222222222222222222222222222222.Selector cfg := cldftesthelpers.SingleGroupTimelockConfig(t) - rt := newSolanaDeployRuntime(t, selector, datastoreWithMCMSPrograms(t, selector)) + rt := newSolanaDeployRuntime(t, selector, solanatest.PreloadDatastoreWithMCMSPrograms(t, selector)) execSolanaDeployChangeset(t, rt, selector, cfg) byType := assertSolanaDeployDatastoreRefs(t, rt, selector) @@ -267,7 +240,7 @@ func TestDeployMCMSWithTimelock_Solana_IdempotentReRun(t *testing.T) { selector := chainselectors.TEST_22222222222222222222222222222222222222222222.Selector cfg := cldftesthelpers.SingleGroupTimelockConfig(t) - rt := newSolanaDeployRuntime(t, selector, datastoreWithMCMSPrograms(t, selector)) + rt := newSolanaDeployRuntime(t, selector, solanatest.PreloadDatastoreWithMCMSPrograms(t, selector)) execSolanaDeployChangeset(t, rt, selector, cfg) afterFirst := assertSolanaDeployDatastoreRefs(t, rt, selector) diff --git a/mcms/solana/firedrill/changeset_test.go b/mcms/solana/firedrill/changeset_test.go index fa4c29f..a64e779 100644 --- a/mcms/solana/firedrill/changeset_test.go +++ b/mcms/solana/firedrill/changeset_test.go @@ -15,10 +15,12 @@ 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/internal/testutil/solanatest" soltestutils "github.com/smartcontractkit/cld-changesets/legacy/pkg/family/solana/testutils" + "github.com/smartcontractkit/cld-changesets/mcms/changesets/deploy" firedrill "github.com/smartcontractkit/cld-changesets/mcms/changesets/firedrill" + _ "github.com/smartcontractkit/cld-changesets/mcms/solana/deploy" _ "github.com/smartcontractkit/cld-changesets/mcms/solana/firedrill" _ "github.com/smartcontractkit/cld-changesets/mcms/solana/readers" ) @@ -59,18 +61,20 @@ func TestChangeset_Apply_solanaProposal(t *testing.T) { func newSolanaFireDrillRuntime(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) diff --git a/mcms/solana/set-config/sequence_test.go b/mcms/solana/set-config/sequence_test.go index 7896e9f..09dd798 100644 --- a/mcms/solana/set-config/sequence_test.go +++ b/mcms/solana/set-config/sequence_test.go @@ -30,12 +30,13 @@ 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" + "github.com/smartcontractkit/cld-changesets/internal/testutil/solanatest" solchangesets "github.com/smartcontractkit/cld-changesets/legacy/pkg/family/solana/changesets" "github.com/smartcontractkit/cld-changesets/legacy/pkg/family/solana/solutils" 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/solana/deploy" _ "github.com/smartcontractkit/cld-changesets/mcms/solana/readers" solreaders "github.com/smartcontractkit/cld-changesets/mcms/solana/readers" ) @@ -135,18 +136,20 @@ func testRunSolanaSetConfig(t *testing.T) { func newSolanaSetConfigRuntime(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)