diff --git a/legacy/pkg/family/solana/testutils/artifacts.go b/legacy/pkg/family/solana/testutils/artifacts.go index 55540ec..c8978b0 100644 --- a/legacy/pkg/family/solana/testutils/artifacts.go +++ b/legacy/pkg/family/solana/testutils/artifacts.go @@ -3,34 +3,8 @@ package soltestutils import ( "os" "path/filepath" - "sync" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/smartcontractkit/cld-changesets/legacy/pkg/family/solana/solutils" ) -var ( - onceCCIP = &sync.Once{} -) - -type downloadFunc func(t *testing.T) string - -// downloadChainlinkCCIPProgramArtifacts downloads CCIP Solana artifacts (includes MCMS programs). -func downloadChainlinkCCIPProgramArtifacts(t *testing.T) string { - t.Helper() - - cachePath := programsCacheDir() - - onceCCIP.Do(func() { - err := solutils.DownloadChainlinkCCIPProgramArtifacts(t.Context(), cachePath, "", nil) - require.NoError(t, err) - }) - - return cachePath -} - // programsCacheDir returns where to store downloaded .so files. Leaf dir is solana_programs // (under UserCacheDir/TempDir, so "cache" is implied; avoids read-only pkg/mod paths). func programsCacheDir() string { diff --git a/legacy/pkg/family/solana/testutils/preload.go b/legacy/pkg/family/solana/testutils/preload.go index 7568fae..85255fe 100644 --- a/legacy/pkg/family/solana/testutils/preload.go +++ b/legacy/pkg/family/solana/testutils/preload.go @@ -1,9 +1,9 @@ package soltestutils import ( - "io" - "os" + "maps" "path/filepath" + "sync" "testing" "github.com/stretchr/testify/require" @@ -13,71 +13,76 @@ import ( "github.com/smartcontractkit/cld-changesets/legacy/pkg/family/solana/solutils" ) -// LoadMCMSPrograms loads the MCMS program artifacts into the given directory. -// -// Returns the path to the temporary test directory and a map of program names to IDs. -func LoadMCMSPrograms(t *testing.T, dir string) (string, map[string]string) { - t.Helper() +// solTestExclusive serializes top-level Solana integration tests that mutate global +// gobinding program IDs via SetProgramID. solTestDepth allows nested subtests in +// the same test to re-enter without deadlocking. +var ( + solTestExclusive sync.Mutex + solTestCountMu sync.Mutex + solTestDepth int +) - progIDs := loadProgramArtifacts(t, - solutils.MCMSProgramNames, downloadChainlinkCCIPProgramArtifacts, dir, - ) +func acquireSolanaTestIsolation(t *testing.T) { + t.Helper() - return dir, progIDs + solTestCountMu.Lock() + if solTestDepth == 0 { + solTestExclusive.Lock() + } + solTestDepth++ + solTestCountMu.Unlock() + + t.Cleanup(func() { + solTestCountMu.Lock() + solTestDepth-- + release := solTestDepth == 0 + solTestCountMu.Unlock() + if release { + solTestExclusive.Unlock() + } + }) } -// PreloadMCMS provides a convenience function to preload the MCMS program artifacts and address -// book for a given selector. -func PreloadMCMS(t *testing.T, selector uint64) (string, map[string]string, *cldf.AddressBookMap) { +var ( + mcmsProgramsOnce sync.Once + mcmsProgramsPath string + mcmsProgramIDs map[string]string +) + +// sharedMCMSPrograms downloads MCMS Solana program artifacts once per test process +// and returns the shared cache directory plus program IDs. +func sharedMCMSPrograms(t *testing.T) (string, map[string]string) { t.Helper() - dir := t.TempDir() + mcmsProgramsOnce.Do(func() { + mcmsProgramsPath = programsCacheDir() + err := solutils.DownloadChainlinkCCIPProgramArtifacts(t.Context(), mcmsProgramsPath, "", nil) + require.NoError(t, err) - _, programIDs := LoadMCMSPrograms(t, dir) + mcmsProgramIDs = make(map[string]string, len(solutils.MCMSProgramNames)) + for _, name := range solutils.MCMSProgramNames { + id := solutils.GetProgramID(name) + require.NotEmpty(t, id, "program id not found for program name: %s", name) + require.FileExists(t, filepath.Join(mcmsProgramsPath, name+".so")) + mcmsProgramIDs[name] = id + } + }) - ab := PreloadAddressBookWithMCMSPrograms(t, selector) + programIDs := make(map[string]string, len(mcmsProgramIDs)) + maps.Copy(programIDs, mcmsProgramIDs) - return dir, programIDs, ab + return mcmsProgramsPath, programIDs } -// loadProgramArtifacts is a helper function that loads program artifacts into a temporary test directory. -// It downloads artifacts using the provided download function and copies the specified programs. -// -// Returns the map of program names to IDs. -func loadProgramArtifacts(t *testing.T, programNames []string, downloadFn downloadFunc, targetDir string) map[string]string { +// PreloadMCMS provides a convenience function to preload the MCMS program artifacts and address +// book for a given selector. +func PreloadMCMS(t *testing.T, selector uint64) (string, map[string]string, *cldf.AddressBookMap) { t.Helper() - // Download the program artifacts using the provided download function - cachePath := downloadFn(t) - - progIDs := make(map[string]string, len(programNames)) - - // Copy the specific artifacts to the target directory and add the program ID to the map - for _, name := range programNames { - id := solutils.GetProgramID(name) - require.NotEmpty(t, id, "program id not found for program name: %s", name) - - src := filepath.Join(cachePath, name+".so") - dst := filepath.Join(targetDir, name+".so") + acquireSolanaTestIsolation(t) - func() { - srcFile, err := os.Open(src) - require.NoError(t, err) - defer srcFile.Close() - - dstFile, err := os.Create(dst) - require.NoError(t, err) - defer dstFile.Close() - - _, err = io.Copy(dstFile, srcFile) - require.NoError(t, err) - }() - - // Add the program ID to the map - progIDs[name] = id - t.Logf("copied solana program %s to %s", name, dst) - } + programsPath, programIDs := sharedMCMSPrograms(t) + ab := PreloadAddressBookWithMCMSPrograms(t, selector) - // Return the path to the cached artifacts and the map of program IDs - return progIDs + return programsPath, programIDs, ab } diff --git a/mcms/changesets/set-config/changeset_test.go b/mcms/changesets/set-config/changeset_test.go index a74a0f8..4dbb5cf 100644 --- a/mcms/changesets/set-config/changeset_test.go +++ b/mcms/changesets/set-config/changeset_test.go @@ -176,11 +176,11 @@ func TestChangeset_VerifyPreconditions(t *testing.T) { } } -//nolint:paralleltest // global mcm.SetProgramID state and shared Solana CTF container setup func TestChangeset_VerifyPreconditions_Solana(t *testing.T) { + t.Parallel() + selector := chain_selectors.TEST_22222222222222222222222222222222222222222222.Selector - rt := newSolanaRuntimeWithDeploy(t, selector) - env := rt.Environment() + env := newSolanaVerifyPreconditionsEnv(t, selector) validCfg := cldftesthelpers.SingleGroupMCMS(t) validTargets := mcmsTargets(selector, validCfg, validCfg, validCfg) @@ -200,6 +200,7 @@ func TestChangeset_VerifyPreconditions_Solana(t *testing.T) { }, } { t.Run(tt.name, func(t *testing.T) { + t.Parallel() require.NoError(t, cs.VerifyPreconditions(env, tt.input)) }) } diff --git a/mcms/changesets/set-config/helpers_test.go b/mcms/changesets/set-config/helpers_test.go index f04468f..d7714c3 100644 --- a/mcms/changesets/set-config/helpers_test.go +++ b/mcms/changesets/set-config/helpers_test.go @@ -1,14 +1,18 @@ package setconfig_test import ( + "context" "crypto/ecdsa" "testing" "time" + "github.com/Masterminds/semver/v3" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" + cldf_chain "github.com/smartcontractkit/chainlink-deployments-framework/chain" cldf_evm "github.com/smartcontractkit/chainlink-deployments-framework/chain/evm" + cldfsol "github.com/smartcontractkit/chainlink-deployments-framework/chain/solana" cldfdatastore "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" @@ -146,6 +150,42 @@ func evmMCMSChainState(t *testing.T, rt *runtime.Runtime, selector uint64) (*evm return mcmsState, chain } +// newSolanaVerifyPreconditionsEnv builds a mock Solana environment for VerifyPreconditions +// only — no CTF container or on-chain deploy. +func newSolanaVerifyPreconditionsEnv(t *testing.T, selector uint64) cldf.Environment { + t.Helper() + + ds := cldfdatastore.NewMemoryDataStore() + version := semver.MustParse("1.0.0") + for _, ref := range []struct { + contractType cldf.ContractType + address string + }{ + {mcmscontracts.RBACTimelock, "timelock-address"}, + {mcmscontracts.ProposerManyChainMultisig, "proposer-address"}, + {mcmscontracts.CancellerManyChainMultisig, "canceller-address"}, + {mcmscontracts.BypasserManyChainMultisig, "bypasser-address"}, + } { + require.NoError(t, ds.Addresses().Add(cldfdatastore.AddressRef{ + Address: ref.address, + ChainSelector: selector, + Type: cldfdatastore.ContractType(ref.contractType), + Version: version, + })) + } + + return cldf.Environment{ + Logger: logger.Test(t), + DataStore: ds.Seal(), + GetContext: func() context.Context { + return t.Context() + }, + BlockChains: cldf_chain.NewBlockChains(map[uint64]cldf_chain.BlockChain{ + selector: cldfsol.Chain{Selector: selector}, + }), + } +} + func newSolanaRuntimeWithDeploy(t *testing.T, selector uint64) *runtime.Runtime { t.Helper() diff --git a/mcms/solana/changesets/fund-mcm-pdas/changeset_test.go b/mcms/solana/changesets/fund-mcm-pdas/changeset_test.go index 1cdb2b5..2a51f23 100644 --- a/mcms/solana/changesets/fund-mcm-pdas/changeset_test.go +++ b/mcms/solana/changesets/fund-mcm-pdas/changeset_test.go @@ -33,7 +33,7 @@ func TestChangeset(t *testing.T) { selector2 := chainselectors.TEST_33333333333333333333333333333333333333333333.Selector rt1 := testRuntime(t, selector1) - env1 := configureFundMCMSignersEnv(t, rt1.Environment(), selector1, rpcWithBalance(t, 1_000), true) + env1 := configureFundMCMSignersEnv(t, rt1.Environment(), selector1, rpcWithBalance(t, 1_000)) cs := Changeset{} t.Run("VerifyPreconditions", func(t *testing.T) { @@ -92,7 +92,7 @@ func TestChangeset(t *testing.T) { }, { name: "insufficient deployer balance", - env: configureFundMCMSignersEnv(t, rt1.Environment(), selector1, rpcWithBalance(t, 1), true), + env: configureFundMCMSignersEnv(t, rt1.Environment(), selector1, rpcWithBalance(t, 1)), config: Config{ FundingPerChain: map[uint64]FundingConfig{selector1: { ProposeMCM: 100, @@ -106,7 +106,7 @@ func TestChangeset(t *testing.T) { { name: "missing deployer key", env: func() cldf.Environment { - env := configureFundMCMSignersEnv(t, rt1.Environment(), selector1, rpcWithBalance(t, 1_000), true) + env := configureFundMCMSignersEnv(t, rt1.Environment(), selector1, rpcWithBalance(t, 1_000)) chain := env.BlockChains.SolanaChains()[selector1] chain.DeployerKey = nil env.BlockChains = cldf_chain.NewBlockChains(map[uint64]cldf_chain.BlockChain{selector1: chain}) @@ -138,8 +138,11 @@ func TestChangeset(t *testing.T) { } t.Run("mcms contracts not deployed", func(t *testing.T) { - rt2 := testRuntime(t, selector2) - env := configureFundMCMSignersEnv(t, rt2.Environment(), selector2, rpcWithBalance(t, 1_000), false) + chain := rt1.Environment().BlockChains.SolanaChains()[selector1] + chain.Client = rpcWithBalance(t, 1_000) + env := cldf.Environment{DataStore: newMCMSDataStore(t, selector2, false)} + env.BlockChains = cldf_chain.NewBlockChains(map[uint64]cldf_chain.BlockChain{selector2: chain}) + err := cs.VerifyPreconditions(env, Config{ FundingPerChain: map[uint64]FundingConfig{selector2: { ProposeMCM: 100, @@ -155,7 +158,7 @@ func TestChangeset(t *testing.T) { t.Run("Apply", func(t *testing.T) { var confirmed [][]solana.Instruction - env := configureFundMCMSignersEnv(t, rt1.Environment(), selector1, nil, true) + env := configureFundMCMSignersEnv(t, rt1.Environment(), selector1, nil) chain := env.BlockChains.SolanaChains()[selector1] require.NotNil(t, chain.DeployerKey) deployerKey := *chain.DeployerKey @@ -246,12 +249,11 @@ func configureFundMCMSignersEnv( base cldf.Environment, selector uint64, client *rpc.Client, - completeState bool, ) cldf.Environment { t.Helper() env := base - env.DataStore = newMCMSDataStore(t, selector, completeState) + env.DataStore = newMCMSDataStore(t, selector, true) chain := env.BlockChains.SolanaChains()[selector] if client != nil { diff --git a/mcms/solana/set-config/operation_test.go b/mcms/solana/set-config/operation_test.go index d346aac..21737a8 100644 --- a/mcms/solana/set-config/operation_test.go +++ b/mcms/solana/set-config/operation_test.go @@ -5,10 +5,10 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" - solanago "github.com/gagliardetto/solana-go" "github.com/stretchr/testify/require" chainselectors "github.com/smartcontractkit/chain-selectors" + cldfsol "github.com/smartcontractkit/chainlink-deployments-framework/chain/solana" mcmscontracts "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/contracts/mcms" cldftesthelpers "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/mcms/proposalutils/testhelpers" "github.com/smartcontractkit/chainlink-deployments-framework/engine/test/runtime" @@ -19,65 +19,95 @@ import ( //nolint:paralleltest // global mcm.SetProgramID state; serialized via soltestutils.PreloadMCMS lock func TestSolanaSetConfig(t *testing.T) { - t.Run("operation", testOpSolanaSetConfigMCM) - t.Run("sequence", testRunSolanaSetConfig) + selector := chainselectors.TEST_22222222222222222222222222222222222222222222.Selector + + // Direct-send cases must not mutate MCMS quorum before transfer runs in the MCMS group. + t.Run("direct send", func(t *testing.T) { //nolint:paralleltest // shared runtime state + rt := newSolanaSetConfigRuntime(t, selector) + chain := rt.Environment().BlockChains.SolanaChains()[selector] + refs := solanaSetConfigRefs(t, rt.Environment(), selector) + fundSolanaSignerPDAs(t, chain, refs) + + t.Run("operation", func(t *testing.T) { //nolint:paralleltest // shared runtime state + testOpSolanaSetConfigDirectSend(t, rt, chain, refs) + }) + t.Run("sequence", func(t *testing.T) { //nolint:paralleltest // shared runtime state + testRunSolanaSetConfigDirectSend(t, rt, chain, refs, selector) + }) + }) + + // MCMS cases share one fresh deploy; transfer runs once before any timelock proposals. + t.Run("MCMS proposal", func(t *testing.T) { //nolint:paralleltest // shared runtime state + rt := newSolanaSetConfigRuntime(t, selector) + chain := rt.Environment().BlockChains.SolanaChains()[selector] + refs := solanaSetConfigRefs(t, rt.Environment(), selector) + fundSolanaSignerPDAs(t, chain, refs) + + t.Run("operation", func(t *testing.T) { //nolint:paralleltest // shared runtime state + testOpSolanaSetConfigMCMSProposal(t, rt, chain, refs, selector) + }) + t.Run("sequence", func(t *testing.T) { //nolint:paralleltest // shared runtime state + testRunSolanaSetConfigMCMSProposal(t, rt, chain, refs, selector) + }) + }) } -func testOpSolanaSetConfigMCM(t *testing.T) { - tests := []struct { - name string - noSend bool - }{ - {name: "direct send", noSend: false}, - {name: "MCMS proposal", noSend: true}, - } +func testOpSolanaSetConfigDirectSend(t *testing.T, rt *runtime.Runtime, chain cldfsol.Chain, refs solanaMCMSRefs) { + t.Helper() - for _, tt := range tests { //nolint:paralleltest // global mcm.SetProgramID state - t.Run(tt.name, func(t *testing.T) { - selector := chainselectors.TEST_22222222222222222222222222222222222222222222.Selector - rt := newSolanaSetConfigRuntime(t, selector) - chain := rt.Environment().BlockChains.SolanaChains()[selector] - refs := solanaSetConfigRefs(t, rt.Environment(), selector) - fundSolanaSignerPDAs(t, chain, refs) + cfg := cldftesthelpers.SingleGroupMCMS(t) + cfg.Signers = append(cfg.Signers, common.HexToAddress("0x0000000000000000000000000000000000000909")) + cfg.Quorum = 2 - authorityAccount := solanago.PublicKey{} - if tt.noSend { - transferSolanaMCMSToTimelock(t, rt, selector) - fundSolanaSignerPDAs(t, chain, refs) - authorityAccount = refs.TimelockSigner - } + report, err := operations.ExecuteOperation( + rt.Environment().OperationsBundle, + OpSolanaSetConfigMCM, + chain, + OpSolanaSetConfigInput{ + Target: MCMSetConfigTarget{ + Address: refs.Canceller, + Config: cfg, + ContractType: string(mcmscontracts.CancellerManyChainMultisig), + }, + NoSend: false, + }, + ) + require.NoError(t, err) + require.True(t, report.Output.Confirmed) + assertSolanaConfigEquals(t, mcmssolana.NewInspector(chain.Client), refs.Canceller, cfg) +} - cfg := cldftesthelpers.SingleGroupMCMS(t) - cfg.Signers = append(cfg.Signers, common.HexToAddress("0x0000000000000000000000000000000000000909")) - cfg.Quorum = 2 +func testOpSolanaSetConfigMCMSProposal(t *testing.T, rt *runtime.Runtime, chain cldfsol.Chain, refs solanaMCMSRefs, selector uint64) { + t.Helper() - report, err := operations.ExecuteOperation( - rt.Environment().OperationsBundle, - OpSolanaSetConfigMCM, - chain, - OpSolanaSetConfigInput{ - Target: MCMSetConfigTarget{ - Address: refs.Canceller, - Config: cfg, - ContractType: string(mcmscontracts.CancellerManyChainMultisig), - }, - NoSend: tt.noSend, - AuthorityAccount: authorityAccount, - }, - ) - require.NoError(t, err) - require.Equal(t, !tt.noSend, report.Output.Confirmed) + transferSolanaMCMSToTimelock(t, rt, selector) + fundSolanaSignerPDAs(t, chain, refs) - if tt.noSend { - require.Equal(t, mcmstypes.ChainSelector(selector), report.Output.BatchOperation.ChainSelector) - require.NotEmpty(t, report.Output.BatchOperation.Transactions) - require.NoError(t, rt.Exec( - newTimelockProposalTask([]mcmstypes.BatchOperation{report.Output.BatchOperation}, "solana set config operation test"), - runtime.SignAndExecuteProposalsTask([]*ecdsa.PrivateKey{cldftesthelpers.TestXXXMCMSSigner}), - )) - } + cfg := cldftesthelpers.SingleGroupMCMS(t) + cfg.Signers = append(cfg.Signers, common.HexToAddress("0x0000000000000000000000000000000000000909")) + cfg.Quorum = 2 - assertSolanaConfigEquals(t, mcmssolana.NewInspector(chain.Client), refs.Canceller, cfg) - }) - } + report, err := operations.ExecuteOperation( + rt.Environment().OperationsBundle, + OpSolanaSetConfigMCM, + chain, + OpSolanaSetConfigInput{ + Target: MCMSetConfigTarget{ + Address: refs.Canceller, + Config: cfg, + ContractType: string(mcmscontracts.CancellerManyChainMultisig), + }, + NoSend: true, + AuthorityAccount: refs.TimelockSigner, + }, + ) + require.NoError(t, err) + require.False(t, report.Output.Confirmed) + require.Equal(t, mcmstypes.ChainSelector(selector), report.Output.BatchOperation.ChainSelector) + require.NotEmpty(t, report.Output.BatchOperation.Transactions) + require.NoError(t, rt.Exec( + newTimelockProposalTask([]mcmstypes.BatchOperation{report.Output.BatchOperation}, "solana set config operation test"), + runtime.SignAndExecuteProposalsTask([]*ecdsa.PrivateKey{cldftesthelpers.TestXXXMCMSSigner}), + )) + assertSolanaConfigEquals(t, mcmssolana.NewInspector(chain.Client), refs.Canceller, cfg) } diff --git a/mcms/solana/set-config/sequence_test.go b/mcms/solana/set-config/sequence_test.go index 7896e9f..743fa5f 100644 --- a/mcms/solana/set-config/sequence_test.go +++ b/mcms/solana/set-config/sequence_test.go @@ -41,95 +41,89 @@ import ( ) //nolint:paralleltest // global mcm.SetProgramID state; serialized via soltestutils.PreloadMCMS lock -func testRunSolanaSetConfig(t *testing.T) { - tests := []struct { - name string - noSend bool - }{ - {name: "direct send", noSend: false}, - {name: "MCMS proposal", noSend: true}, +func testRunSolanaSetConfigDirectSend(t *testing.T, rt *runtime.Runtime, chain cldfsol.Chain, refs solanaMCMSRefs, selector uint64) { + t.Helper() + + proposerCfg := cldftesthelpers.SingleGroupMCMS(t) + proposerCfg.Signers = append(proposerCfg.Signers, common.HexToAddress("0x0000000000000000000000000000000000000101")) + proposerCfg.Quorum = 2 + + cancellerCfg := cldftesthelpers.SingleGroupMCMS(t) + cancellerCfg.Signers = append(cancellerCfg.Signers, common.HexToAddress("0x0000000000000000000000000000000000000202")) + cancellerCfg.Quorum = 2 + + targets := []setconfig.ContractSetConfig{ + { + Ref: refkey.New(selector, datastore.ContractType(mcmscontracts.ProposerManyChainMultisig), &semvers.V1_0_0, ""), + Config: proposerCfg, + }, + { + Ref: refkey.New(selector, datastore.ContractType(mcmscontracts.CancellerManyChainMultisig), &semvers.V1_0_0, ""), + Config: cancellerCfg, + }, } - for _, tt := range tests { //nolint:paralleltest // global mcm.SetProgramID state - t.Run(tt.name, func(t *testing.T) { - selector := chainselectors.TEST_22222222222222222222222222222222222222222222.Selector - rt := newSolanaSetConfigRuntime(t, selector) - chain := rt.Environment().BlockChains.SolanaChains()[selector] - refs := solanaSetConfigRefs(t, rt.Environment(), selector) - fundSolanaSignerPDAs(t, chain, refs) + out, err := runSolanaSetConfig( + rt.Environment().OperationsBundle, + setconfig.Deps{ + BlockChains: rt.Environment().BlockChains, + DataStore: rt.Environment().DataStore, + }, + setconfig.ChainInput{ + ChainSelector: selector, + Targets: targets, + }, + ) + require.NoError(t, err) + require.Empty(t, out.BatchOps) - if tt.noSend { - transferSolanaMCMSToTimelock(t, rt, selector) - fundSolanaSignerPDAs(t, chain, refs) - } + inspector := mcmssolana.NewInspector(chain.Client) + assertSolanaConfigEquals(t, inspector, refs.Proposer, proposerCfg) + assertSolanaConfigEquals(t, inspector, refs.Canceller, cancellerCfg) +} - proposerCfg := cldftesthelpers.SingleGroupMCMS(t) - proposerCfg.Signers = append(proposerCfg.Signers, common.HexToAddress("0x0000000000000000000000000000000000000101")) - proposerCfg.Quorum = 2 +func testRunSolanaSetConfigMCMSProposal(t *testing.T, rt *runtime.Runtime, chain cldfsol.Chain, refs solanaMCMSRefs, selector uint64) { + t.Helper() - cancellerCfg := cldftesthelpers.SingleGroupMCMS(t) - cancellerCfg.Signers = append(cancellerCfg.Signers, common.HexToAddress("0x0000000000000000000000000000000000000202")) - cancellerCfg.Quorum = 2 + fundSolanaSignerPDAs(t, chain, refs) - targets := []setconfig.ContractSetConfig{ - { - Ref: refkey.New(selector, datastore.ContractType(mcmscontracts.ProposerManyChainMultisig), &semvers.V1_0_0, ""), - Config: proposerCfg, - }, - { - Ref: refkey.New(selector, datastore.ContractType(mcmscontracts.CancellerManyChainMultisig), &semvers.V1_0_0, ""), - Config: cancellerCfg, - }, - } - var mcmsInput *cldf.MCMSTimelockProposalInput - if tt.noSend { - targets = []setconfig.ContractSetConfig{ - { - Ref: refkey.New(selector, datastore.ContractType(mcmscontracts.CancellerManyChainMultisig), &semvers.V1_0_0, ""), - Config: cancellerCfg, - }, - } - mcmsInput = &cldf.MCMSTimelockProposalInput{ - TimelockAction: mcmstypes.TimelockActionSchedule, - ValidUntil: uint32(time.Now().Add(2 * time.Hour).UTC().Unix()), //nolint:gosec // test timestamp - TimelockDelay: mcmstypes.NewDuration(time.Second), - } - } - - out, err := runSolanaSetConfig( - rt.Environment().OperationsBundle, - setconfig.Deps{ - BlockChains: rt.Environment().BlockChains, - DataStore: rt.Environment().DataStore, - }, - setconfig.ChainInput{ - ChainSelector: selector, - Targets: targets, - MCMS: mcmsInput, - }, - ) - require.NoError(t, err) - - if tt.noSend { - require.Len(t, out.BatchOps, 1) - require.NotEmpty(t, out.BatchOps[0].Transactions) - require.NoError(t, rt.Exec( - newTimelockProposalTask(out.BatchOps, "solana set config sequence test"), - runtime.SignAndExecuteProposalsTask([]*ecdsa.PrivateKey{cldftesthelpers.TestXXXMCMSSigner}), - )) - } else { - require.Empty(t, out.BatchOps) - } - - inspector := mcmssolana.NewInspector(chain.Client) - if tt.noSend { - assertSolanaConfigEquals(t, inspector, refs.Canceller, cancellerCfg) - } else { - assertSolanaConfigEquals(t, inspector, refs.Proposer, proposerCfg) - assertSolanaConfigEquals(t, inspector, refs.Canceller, cancellerCfg) - } - }) + cancellerCfg := cldftesthelpers.SingleGroupMCMS(t) + cancellerCfg.Signers = append(cancellerCfg.Signers, common.HexToAddress("0x0000000000000000000000000000000000000202")) + cancellerCfg.Quorum = 2 + + mcmsInput := &cldf.MCMSTimelockProposalInput{ + TimelockAction: mcmstypes.TimelockActionSchedule, + ValidUntil: uint32(time.Now().Add(2 * time.Hour).UTC().Unix()), //nolint:gosec // test timestamp + TimelockDelay: mcmstypes.NewDuration(time.Second), } + targets := []setconfig.ContractSetConfig{ + { + Ref: refkey.New(selector, datastore.ContractType(mcmscontracts.CancellerManyChainMultisig), &semvers.V1_0_0, ""), + Config: cancellerCfg, + }, + } + + out, err := runSolanaSetConfig( + rt.Environment().OperationsBundle, + setconfig.Deps{ + BlockChains: rt.Environment().BlockChains, + DataStore: rt.Environment().DataStore, + }, + setconfig.ChainInput{ + ChainSelector: selector, + Targets: targets, + MCMS: mcmsInput, + }, + ) + require.NoError(t, err) + require.Len(t, out.BatchOps, 1) + require.NotEmpty(t, out.BatchOps[0].Transactions) + require.NoError(t, rt.Exec( + newTimelockProposalTask(out.BatchOps, "solana set config sequence test"), + runtime.SignAndExecuteProposalsTask([]*ecdsa.PrivateKey{cldftesthelpers.TestXXXMCMSSigner}), + )) + + assertSolanaConfigEquals(t, mcmssolana.NewInspector(chain.Client), refs.Canceller, cancellerCfg) } func newSolanaSetConfigRuntime(t *testing.T, selector uint64) *runtime.Runtime {