From 72eea707f66dc8cbcd308a55ae110db52b7e09a9 Mon Sep 17 00:00:00 2001 From: Jade Park Date: Thu, 19 Dec 2024 11:51:16 +0000 Subject: [PATCH] chore: mcm::set_config sync changes --- .../mcm/src/instructions/set_config.rs | 28 ++ .../programs/mcm/src/instructions/set_root.rs | 2 + chains/solana/contracts/target/idl/mcm.json | 10 + .../contracts/tests/config/mcm_config.go | 3 + chains/solana/contracts/tests/mcms/mcm.go | 86 +++++ .../tests/mcms/mcm_multiple_multisigs_test.go | 72 +--- .../tests/mcms/mcm_set_config_test.go | 67 +--- .../tests/mcms/mcm_set_root_execute_test.go | 319 +++++++++++++----- .../contracts/tests/mcms/mcm_timelock_test.go | 162 ++------- .../solana/contracts/tests/mcms/timelock.go | 2 +- .../mcms/timelock_bypasser_execute_test.go | 2 +- .../tests/mcms/timelock_rbac_test.go | 4 +- .../mcms/timelock_schedule_execute_test.go | 6 +- chains/solana/gobindings/mcm/SetConfig.go | 64 +++- 14 files changed, 467 insertions(+), 360 deletions(-) diff --git a/chains/solana/contracts/programs/mcm/src/instructions/set_config.rs b/chains/solana/contracts/programs/mcm/src/instructions/set_config.rs index 1d6d7fe4..8f5cf1ab 100644 --- a/chains/solana/contracts/programs/mcm/src/instructions/set_config.rs +++ b/chains/solana/contracts/programs/mcm/src/instructions/set_config.rs @@ -5,6 +5,7 @@ use crate::constant::*; use crate::error::*; use crate::eth_utils::*; use crate::event::*; +use crate::state::root::*; /// Set the configuration for the multisig instance after validating the input pub fn set_config( @@ -110,6 +111,27 @@ pub fn set_config( config.group_quorums = group_quorums; config.group_parents = group_parents; + // clear_root is equivalent to overriding with a completely empty root + if clear_root { + let expiring_root = &mut ctx.accounts.expiring_root_and_op_count; + let root_metadata = &mut ctx.accounts.root_metadata; + + // preserve the current op_count + let current_op_count = expiring_root.op_count; + + // clear the expiring root while preserving op_count + expiring_root.root = [0u8; 32]; // clear root (equivalent to bytes32(0) in Solidity) + expiring_root.valid_until = 0; // clear timestamp + expiring_root.op_count = current_op_count; + + // set root metadata to a cleared state + root_metadata.chain_id = ctx.accounts.multisig_config.chain_id; + root_metadata.multisig = ctx.accounts.multisig_config.key(); + root_metadata.pre_op_count = current_op_count; + root_metadata.post_op_count = current_op_count; + root_metadata.override_previous_root = true; + } + emit!(ConfigSet { group_parents, group_quorums, @@ -218,6 +240,12 @@ pub struct SetConfig<'info> { )] pub config_signers: Account<'info, ConfigSigners>, // preloaded signers account + #[account(mut, seeds = [ROOT_METADATA_SEED, multisig_name.as_ref()], bump)] + pub root_metadata: Account<'info, RootMetadata>, + + #[account(mut, seeds = [EXPIRING_ROOT_AND_OP_COUNT_SEED, multisig_name.as_ref()], bump)] + pub expiring_root_and_op_count: Account<'info, ExpiringRootAndOpCount>, + #[account(mut, address = multisig_config.owner @ AuthError::Unauthorized)] pub authority: Signer<'info>, diff --git a/chains/solana/contracts/programs/mcm/src/instructions/set_root.rs b/chains/solana/contracts/programs/mcm/src/instructions/set_root.rs index 999c4639..1f2e4023 100644 --- a/chains/solana/contracts/programs/mcm/src/instructions/set_root.rs +++ b/chains/solana/contracts/programs/mcm/src/instructions/set_root.rs @@ -56,6 +56,8 @@ pub fn set_root( ); let current_op_count = ctx.accounts.expiring_root_and_op_count.op_count; + // don't allow a new root to be set if there are still outstanding ops that have not been + // executed, unless override_previous_root is set require!( current_op_count == ctx.accounts.root_metadata.post_op_count || metadata.override_previous_root, diff --git a/chains/solana/contracts/target/idl/mcm.json b/chains/solana/contracts/target/idl/mcm.json index 63128902..e53283e1 100644 --- a/chains/solana/contracts/target/idl/mcm.json +++ b/chains/solana/contracts/target/idl/mcm.json @@ -134,6 +134,16 @@ "isMut": true, "isSigner": false }, + { + "name": "rootMetadata", + "isMut": true, + "isSigner": false + }, + { + "name": "expiringRootAndOpCount", + "isMut": true, + "isSigner": false + }, { "name": "authority", "isMut": true, diff --git a/chains/solana/contracts/tests/config/mcm_config.go b/chains/solana/contracts/tests/config/mcm_config.go index 693975bf..7fe7541b 100644 --- a/chains/solana/contracts/tests/config/mcm_config.go +++ b/chains/solana/contracts/tests/config/mcm_config.go @@ -26,6 +26,9 @@ var ( MaxNumSigners = 200 MaxAppendSignerBatchSize = 45 MaxAppendSignatureBatchSize = 13 + + McmEmptyRoot = [32]byte{} + McmEmptyTimestamp = uint32(0) // root related configs // the following diagram shows the structure of the signers and groups: // ref: https://github.com/smartcontractkit/ccip-owner-contracts/blob/56f1a8d2cd4ba5ef2b99d2185ffded53957dd410/src/ManyChainMultiSig.sol#L65 diff --git a/chains/solana/contracts/tests/mcms/mcm.go b/chains/solana/contracts/tests/mcms/mcm.go index d0b8eeec..a7d33c67 100644 --- a/chains/solana/contracts/tests/mcms/mcm.go +++ b/chains/solana/contracts/tests/mcms/mcm.go @@ -98,6 +98,47 @@ func NewMcmMultisig(name [32]byte) mcmsUtils.Multisig { } } +// instructions builder for preloading signers +func McmPreloadSignersIxs(signerAddresses [][20]uint8, msigName [32]byte, multisigCfgPDA solana.PublicKey, cfgSignersPDA solana.PublicKey, authority solana.PublicKey, appendChunkSize int) ([]solana.Instruction, error) { + ixs := make([]solana.Instruction, 0) + + parsedTotalSigners, pErr := mcmsUtils.SafeToUint8(len(signerAddresses)) + if pErr != nil { + return nil, pErr + } + initSignersIx, isErr := mcm.NewInitSignersInstruction( + msigName, + parsedTotalSigners, + multisigCfgPDA, + cfgSignersPDA, + authority, + solana.SystemProgramID, + ).ValidateAndBuild() + if isErr != nil { + return nil, isErr + } + ixs = append(ixs, initSignersIx) + + appendSignersIxs, asErr := AppendSignersIxs(signerAddresses, msigName, multisigCfgPDA, cfgSignersPDA, authority, appendChunkSize) + if asErr != nil { + return nil, asErr + } + ixs = append(ixs, appendSignersIxs...) + + finalizeSignersIx, fsErr := mcm.NewFinalizeSignersInstruction( + msigName, + multisigCfgPDA, + cfgSignersPDA, + authority, + ).ValidateAndBuild() + if fsErr != nil { + return nil, fsErr + } + ixs = append(ixs, finalizeSignersIx) + + return ixs, nil +} + // get chunked append instructions to preload signers to pda, required before set_config func AppendSignersIxs(signerAddresses [][20]uint8, msigName [32]byte, multisigCfgPDA solana.PublicKey, cfgSignersPDA solana.PublicKey, authority solana.PublicKey, chunkSize int) ([]solana.Instruction, error) { if chunkSize > config.MaxAppendSignerBatchSize { @@ -124,6 +165,51 @@ func AppendSignersIxs(signerAddresses [][20]uint8, msigName [32]byte, multisigCf return ixs, nil } +// instructions builder for preloading signatures +func McmPreloadSignaturesIxs(signatures []mcm.Signature, msigName [32]byte, root [32]uint8, validUntil uint32, signaturesPDA solana.PublicKey, authority solana.PublicKey, appendChunkSize int) ([]solana.Instruction, error) { + ixs := make([]solana.Instruction, 0) + + parsedTotalSigs, pErr := mcmsUtils.SafeToUint8(len(signatures)) + if pErr != nil { + return nil, pErr + } + + initSigsIx, isErr := mcm.NewInitSignaturesInstruction( + msigName, + root, + validUntil, + parsedTotalSigs, + signaturesPDA, + authority, + solana.SystemProgramID, + ).ValidateAndBuild() + if isErr != nil { + return nil, isErr + } + ixs = append(ixs, initSigsIx) + + appendSigsIxs, asErr := AppendSignaturesIxs(signatures, msigName, root, validUntil, signaturesPDA, authority, appendChunkSize) + if asErr != nil { + return nil, asErr + } + + ixs = append(ixs, appendSigsIxs...) + + finalizeSigsIx, fsErr := mcm.NewFinalizeSignaturesInstruction( + msigName, + root, + validUntil, + signaturesPDA, + authority, + ).ValidateAndBuild() + if fsErr != nil { + return nil, fsErr + } + ixs = append(ixs, finalizeSigsIx) + + return ixs, nil +} + // get chunked append instructions to preload signatures to pda, required before set_root func AppendSignaturesIxs(signatures []mcm.Signature, msigName [32]byte, root [32]uint8, validUntil uint32, signaturesPDA solana.PublicKey, authority solana.PublicKey, chunkSize int) ([]solana.Instruction, error) { if chunkSize > config.MaxAppendSignatureBatchSize { diff --git a/chains/solana/contracts/tests/mcms/mcm_multiple_multisigs_test.go b/chains/solana/contracts/tests/mcms/mcm_multiple_multisigs_test.go index 8e609417..703e70ca 100644 --- a/chains/solana/contracts/tests/mcms/mcm_multiple_multisigs_test.go +++ b/chains/solana/contracts/tests/mcms/mcm_multiple_multisigs_test.go @@ -114,37 +114,10 @@ func TestMcmMultipleMultisigs(t *testing.T) { signerAddresses := mcmConfig.SignerAddresses t.Run("mcm:set_config: preload signers on PDA", func(t *testing.T) { - ixs := make([]solana.Instruction, 0) + preloadIxs, pierr := McmPreloadSignersIxs(signerAddresses, testMsigName1, multisigConfigPDA1, configSignersPDA1, admin.PublicKey(), config.MaxAppendSignerBatchSize) + require.NoError(t, pierr) - parsedTotalSigners, err := mcmsUtils.SafeToUint8(len(signerAddresses)) - require.NoError(t, err) - - initSignersIx, err := mcm.NewInitSignersInstruction( - testMsigName1, - parsedTotalSigners, - multisigConfigPDA1, - configSignersPDA1, - admin.PublicKey(), - solana.SystemProgramID, - ).ValidateAndBuild() - - require.NoError(t, err) - ixs = append(ixs, initSignersIx) - - appendSignersIxs, err := AppendSignersIxs(signerAddresses, testMsigName1, multisigConfigPDA1, configSignersPDA1, admin.PublicKey(), config.MaxAppendSignerBatchSize) - require.NoError(t, err) - ixs = append(ixs, appendSignersIxs...) - - finalizeSignersIx, err := mcm.NewFinalizeSignersInstruction( - testMsigName1, - multisigConfigPDA1, - configSignersPDA1, - admin.PublicKey(), - ).ValidateAndBuild() - require.NoError(t, err) - ixs = append(ixs, finalizeSignersIx) - - for _, ix := range ixs { + for _, ix := range preloadIxs { utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } @@ -169,6 +142,8 @@ func TestMcmMultipleMultisigs(t *testing.T) { mcmConfig.ClearRoot, multisigConfigPDA1, configSignersPDA1, + rootMetadataPDA1, + expiringRootAndOpCountPDA1, admin.PublicKey(), solana.SystemProgramID, ).ValidateAndBuild() @@ -262,37 +237,10 @@ func TestMcmMultipleMultisigs(t *testing.T) { signerAddresses := mcmConfig.SignerAddresses t.Run("mcm:set_config: preload signers on PDA", func(t *testing.T) { - ixs := make([]solana.Instruction, 0) - - parsedTotalSigners, err := mcmsUtils.SafeToUint8(len(signerAddresses)) - require.NoError(t, err) - - initSignersIx, err := mcm.NewInitSignersInstruction( - testMsigName2, - parsedTotalSigners, - multisigConfigPDA2, - configSignersPDA2, - admin.PublicKey(), - solana.SystemProgramID, - ).ValidateAndBuild() - - require.NoError(t, err) - ixs = append(ixs, initSignersIx) - - appendSignersIxs, err := AppendSignersIxs(signerAddresses, testMsigName2, multisigConfigPDA2, configSignersPDA2, admin.PublicKey(), config.MaxAppendSignerBatchSize) - require.NoError(t, err) - ixs = append(ixs, appendSignersIxs...) - - finalizeSignersIx, err := mcm.NewFinalizeSignersInstruction( - testMsigName2, - multisigConfigPDA2, - configSignersPDA2, - admin.PublicKey(), - ).ValidateAndBuild() - require.NoError(t, err) - ixs = append(ixs, finalizeSignersIx) + preloadIxs, pierr := McmPreloadSignersIxs(signerAddresses, testMsigName2, multisigConfigPDA2, configSignersPDA2, admin.PublicKey(), config.MaxAppendSignerBatchSize) + require.NoError(t, pierr) - for _, ix := range ixs { + for _, ix := range preloadIxs { utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } @@ -317,6 +265,8 @@ func TestMcmMultipleMultisigs(t *testing.T) { mcmConfig.ClearRoot, multisigConfigPDA2, configSignersPDA2, + rootMetadataPDA2, + expiringRootAndOpCountPDA2, admin.PublicKey(), solana.SystemProgramID, ).ValidateAndBuild() @@ -336,6 +286,8 @@ func TestMcmMultipleMultisigs(t *testing.T) { mcmConfig.ClearRoot, multisigConfigPDA2, configSignersPDA2, + rootMetadataPDA2, + expiringRootAndOpCountPDA2, admin.PublicKey(), solana.SystemProgramID, ).ValidateAndBuild() diff --git a/chains/solana/contracts/tests/mcms/mcm_set_config_test.go b/chains/solana/contracts/tests/mcms/mcm_set_config_test.go index 2237f94c..eef6c6a4 100644 --- a/chains/solana/contracts/tests/mcms/mcm_set_config_test.go +++ b/chains/solana/contracts/tests/mcms/mcm_set_config_test.go @@ -207,36 +207,10 @@ func TestMcmSetConfig(t *testing.T) { signerAddresses := mcmConfig.SignerAddresses t.Run("mcm:set_config: preload signers on PDA", func(t *testing.T) { - ixs := make([]solana.Instruction, 0) - parsedTotalSigners, err := mcmsUtils.SafeToUint8(len(signerAddresses)) - require.NoError(t, err) - - initSignersIx, err := mcm.NewInitSignersInstruction( - testMsigName, - parsedTotalSigners, - multisigConfigPDA, - configSignersPDA, - admin.PublicKey(), - solana.SystemProgramID, - ).ValidateAndBuild() - - require.NoError(t, err) - ixs = append(ixs, initSignersIx) - - appendSignersIxs, err := AppendSignersIxs(signerAddresses, testMsigName, multisigConfigPDA, configSignersPDA, admin.PublicKey(), config.MaxAppendSignerBatchSize) - require.NoError(t, err) - ixs = append(ixs, appendSignersIxs...) + preloadIxs, pierr := McmPreloadSignersIxs(signerAddresses, testMsigName, multisigConfigPDA, configSignersPDA, admin.PublicKey(), config.MaxAppendSignerBatchSize) + require.NoError(t, pierr) - finalizeSignersIx, err := mcm.NewFinalizeSignersInstruction( - testMsigName, - multisigConfigPDA, - configSignersPDA, - admin.PublicKey(), - ).ValidateAndBuild() - require.NoError(t, err) - ixs = append(ixs, finalizeSignersIx) - - for _, ix := range ixs { + for _, ix := range preloadIxs { utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } @@ -262,6 +236,8 @@ func TestMcmSetConfig(t *testing.T) { mcmConfig.ClearRoot, multisigConfigPDA, configSignersPDA, + rootMetadataPDA, + expiringRootAndOpCountPDA, user.PublicKey(), // unauthorized user solana.SystemProgramID, ).ValidateAndBuild() @@ -281,6 +257,8 @@ func TestMcmSetConfig(t *testing.T) { mcmConfig.ClearRoot, multisigConfigPDA, configSignersPDA, + rootMetadataPDA, + expiringRootAndOpCountPDA, admin.PublicKey(), solana.SystemProgramID, ).ValidateAndBuild() @@ -388,33 +366,14 @@ func TestMcmSetConfig(t *testing.T) { utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{clearIx}, admin, config.DefaultCommitment) utils.AssertClosedAccount(ctx, t, solanaGoClient, configSignersPDA, config.DefaultCommitment) - reInitSignersIx, err := mcm.NewInitSignersInstruction( - testMsigName, - parsedTotalSigners, - multisigConfigPDA, - configSignersPDA, - admin.PublicKey(), - solana.SystemProgramID, - ).ValidateAndBuild() + // preload signers again + preloadIxs, pierr := McmPreloadSignersIxs(signerAddresses, testMsigName, multisigConfigPDA, configSignersPDA, admin.PublicKey(), config.MaxAppendSignerBatchSize) + require.NoError(t, pierr) - require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{reInitSignersIx}, admin, config.DefaultCommitment) - // register all signers - for _, ix := range appendSignersIxs { + for _, ix := range preloadIxs { utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } - // finalize registration - finalizeSignersIx, err := mcm.NewFinalizeSignersInstruction( - testMsigName, - multisigConfigPDA, - configSignersPDA, - admin.PublicKey(), - ).ValidateAndBuild() - - require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{finalizeSignersIx}, admin, config.DefaultCommitment) - var cfgSignersAccount mcm.ConfigSigners err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, configSignersPDA, config.DefaultCommitment, &cfgSignersAccount) require.NoError(t, err, "failed to get account info") @@ -436,6 +395,8 @@ func TestMcmSetConfig(t *testing.T) { mcmConfig.ClearRoot, multisigConfigPDA, configSignersPDA, + rootMetadataPDA, + expiringRootAndOpCountPDA, admin.PublicKey(), solana.SystemProgramID, ).ValidateAndBuild() @@ -655,6 +616,8 @@ func TestMcmSetConfig(t *testing.T) { cfg.ClearRoot, failMultisigConfigPDA, failConfigSignersPDA, + failRootMetadataPDA, + failExpiringRootAndOpCountPDA, admin.PublicKey(), solana.SystemProgramID, ).ValidateAndBuild() diff --git a/chains/solana/contracts/tests/mcms/mcm_set_root_execute_test.go b/chains/solana/contracts/tests/mcms/mcm_set_root_execute_test.go index 2a187352..a5080130 100644 --- a/chains/solana/contracts/tests/mcms/mcm_set_root_execute_test.go +++ b/chains/solana/contracts/tests/mcms/mcm_set_root_execute_test.go @@ -8,6 +8,7 @@ import ( "slices" "strings" "testing" + "time" bin "github.com/gagliardetto/binary" "github.com/gagliardetto/solana-go" @@ -262,36 +263,10 @@ func TestMcmSetRootAndExecute(t *testing.T) { signerAddresses := mcmConfig.SignerAddresses t.Run("mcm:preload signers", func(t *testing.T) { - ixs := make([]solana.Instruction, 0) + preloadIxs, pierr := McmPreloadSignersIxs(signerAddresses, testMsigName, multisigConfigPDA, configSignersPDA, admin.PublicKey(), config.MaxAppendSignerBatchSize) + require.NoError(t, pierr) - parsedTotalSigners, pErr := mcmsUtils.SafeToUint8(len(signerAddresses)) - require.NoError(t, pErr) - initSignersIx, isErr := mcm.NewInitSignersInstruction( - testMsigName, - parsedTotalSigners, - multisigConfigPDA, - configSignersPDA, - admin.PublicKey(), - solana.SystemProgramID, - ).ValidateAndBuild() - - require.NoError(t, isErr) - ixs = append(ixs, initSignersIx) - - appendSignersIxs, asErr := AppendSignersIxs(signerAddresses, testMsigName, multisigConfigPDA, configSignersPDA, admin.PublicKey(), config.MaxAppendSignerBatchSize) - require.NoError(t, asErr) - ixs = append(ixs, appendSignersIxs...) - - finalizeSignersIx, fsErr := mcm.NewFinalizeSignersInstruction( - testMsigName, - multisigConfigPDA, - configSignersPDA, - admin.PublicKey(), - ).ValidateAndBuild() - require.NoError(t, fsErr) - ixs = append(ixs, finalizeSignersIx) - - for _, ix := range ixs { + for _, ix := range preloadIxs { utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } @@ -316,6 +291,8 @@ func TestMcmSetRootAndExecute(t *testing.T) { mcmConfig.ClearRoot, multisigConfigPDA, configSignersPDA, + rootMetadataPDA, + expiringRootAndOpCountPDA, admin.PublicKey(), solana.SystemProgramID, ).ValidateAndBuild() @@ -342,9 +319,9 @@ func TestMcmSetRootAndExecute(t *testing.T) { } }) - var opNodes []mcmsUtils.McmOpNode + t.Run("mcm set_root", func(t *testing.T) { + var opNodes []mcmsUtils.McmOpNode - t.Run("mcm set_root happy path", func(t *testing.T) { for i, op := range stupProgramTestMcmOps { node := mcmsUtils.McmOpNode{ Nonce: uint64(i), @@ -414,35 +391,14 @@ func TestMcmSetRootAndExecute(t *testing.T) { utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{clearIx}, admin, config.DefaultCommitment) utils.AssertClosedAccount(ctx, t, solanaGoClient, signaturesPDA, config.DefaultCommitment) - reInitSigsIx, rIsErr := mcm.NewInitSignaturesInstruction( - testMsigName, - rootValidationData.Root, - validUntil, - parsedTotalSigs, - signaturesPDA, - admin.PublicKey(), - solana.SystemProgramID, - ).ValidateAndBuild() - - require.NoError(t, rIsErr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{reInitSigsIx}, admin, config.DefaultCommitment) + // preload again + preloadIxs, plerr := McmPreloadSignaturesIxs(signatures, testMsigName, rootValidationData.Root, validUntil, signaturesPDA, admin.PublicKey(), config.MaxAppendSignatureBatchSize) + require.NoError(t, plerr) - // register all signatures again - for _, ix := range appendSigsIxs { + for _, ix := range preloadIxs { utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } - finalizeSigsIx, fsErr := mcm.NewFinalizeSignaturesInstruction( - testMsigName, - rootValidationData.Root, - validUntil, - signaturesPDA, - admin.PublicKey(), - ).ValidateAndBuild() - - require.NoError(t, fsErr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{finalizeSigsIx}, admin, config.DefaultCommitment) - var sigAccount mcm.RootSignatures queryErr := utils.GetAccountDataBorshInto(ctx, solanaGoClient, signaturesPDA, config.DefaultCommitment, &sigAccount) require.NoError(t, queryErr, "failed to get account info") @@ -510,6 +466,223 @@ func TestMcmSetRootAndExecute(t *testing.T) { require.Equal(t, rootValidationData.Metadata.OverridePreviousRoot, newRootMetadata.OverridePreviousRoot) }) + var opNodes []mcmsUtils.McmOpNode + + t.Run("change config scenario, clear root and set a new root", func(t *testing.T) { + // keep current op count + var prevRootAndOpCount mcm.ExpiringRootAndOpCount + err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, expiringRootAndOpCountPDA, config.DefaultCommitment, &prevRootAndOpCount) + require.NoError(t, err, "failed to get account info") + currentOpCount := prevRootAndOpCount.OpCount + + // new config with clear_root + newMcmConfig, configErr := mcmsUtils.NewValidMcmConfig( + testMsigName, + config.SignerPrivateKeys, + config.SignerGroups, + config.GroupQuorums, + config.GroupParents, + true, // clear_root + ) + require.NoError(t, configErr) + + signerAddresses := newMcmConfig.SignerAddresses + + t.Run("preload signers", func(t *testing.T) { + preloadIxs, pierr := McmPreloadSignersIxs(signerAddresses, testMsigName, multisigConfigPDA, configSignersPDA, admin.PublicKey(), config.MaxAppendSignerBatchSize) + require.NoError(t, pierr) + + for _, ix := range preloadIxs { + utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + } + + var cfgSignersAccount mcm.ConfigSigners + queryErr := utils.GetAccountDataBorshInto(ctx, solanaGoClient, configSignersPDA, config.DefaultCommitment, &cfgSignersAccount) + require.NoError(t, queryErr, "failed to get account info") + require.Equal(t, true, cfgSignersAccount.IsFinalized) + // check if the addresses are registered correctly + for i, signer := range cfgSignersAccount.SignerAddresses { + require.Equal(t, signerAddresses[i], signer) + } + }) + + t.Run("set_config with clear_root", func(t *testing.T) { + ix, configErr := mcm.NewSetConfigInstruction( + newMcmConfig.MultisigName, + newMcmConfig.SignerGroups, + newMcmConfig.GroupQuorums, + newMcmConfig.GroupParents, + newMcmConfig.ClearRoot, + multisigConfigPDA, + configSignersPDA, + rootMetadataPDA, + expiringRootAndOpCountPDA, + admin.PublicKey(), + solana.SystemProgramID, + ).ValidateAndBuild() + + require.NoError(t, configErr) + + tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + require.NotNil(t, tx) + + parsedLogs := utils.ParseLogMessages(tx.Meta.LogMessages, + []utils.EventMapping{ + utils.EventMappingFor[ConfigSet]("ConfigSet"), + }, + ) + + event := parsedLogs[0].EventData[0].Data.(*ConfigSet) + require.Equal(t, newMcmConfig.GroupParents, event.GroupParents) + require.Equal(t, newMcmConfig.GroupQuorums, event.GroupQuorums) + require.Equal(t, true, event.IsRootCleared) + + // get config and validate + var configAccount mcm.MultisigConfig + configErr = utils.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA, config.DefaultCommitment, &configAccount) + require.NoError(t, configErr, "failed to get account info") + + require.Equal(t, config.TestChainID, configAccount.ChainId) + require.Equal(t, reflect.DeepEqual(configAccount.GroupParents, newMcmConfig.GroupParents), true) + require.Equal(t, reflect.DeepEqual(configAccount.GroupQuorums, newMcmConfig.GroupQuorums), true) + + // check if the McmSigner struct is correct + for i, signer := range configAccount.Signers { + require.Equal(t, signer.EvmAddress, newMcmConfig.SignerAddresses[i]) + require.Equal(t, signer.Index, uint8(i)) + require.Equal(t, signer.Group, newMcmConfig.SignerGroups[i]) + } + + // get root, metadata and validate + var clearedRootAndOpCount mcm.ExpiringRootAndOpCount + + err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, expiringRootAndOpCountPDA, config.DefaultCommitment, &clearedRootAndOpCount) + require.NoError(t, err, "failed to get account info") + + require.Equal(t, config.McmEmptyRoot, clearedRootAndOpCount.Root) + require.Equal(t, config.McmEmptyTimestamp, clearedRootAndOpCount.ValidUntil) + require.Equal(t, currentOpCount, clearedRootAndOpCount.OpCount) + + var clearedRootMetadata mcm.RootMetadata + err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, rootMetadataPDA, config.DefaultCommitment, &clearedRootMetadata) + require.NoError(t, err, "failed to get account info") + + require.Equal(t, configAccount.ChainId, clearedRootMetadata.ChainId) // reset to config.chainid + require.Equal(t, multisigConfigPDA, clearedRootMetadata.Multisig) // should be the same multisig + require.Equal(t, currentOpCount, clearedRootMetadata.PreOpCount) // preserve op count + require.Equal(t, currentOpCount, clearedRootMetadata.PostOpCount) // preserve op count + require.Equal(t, true, clearedRootMetadata.OverridePreviousRoot) // should be true + }) + + t.Run("mcm set_root on new config", func(t *testing.T) { + for i, op := range stupProgramTestMcmOps { + node := mcmsUtils.McmOpNode{ + Nonce: uint64(i), + Multisig: multisigConfigPDA, + To: config.ExternalCpiStubProgram, + Data: op.Data, + RemainingAccounts: op.RemainingAccounts, + } + opNodes = append(opNodes, node) + } + // should be different timestamp from previous root(seen_signed_hash) + validUntil := uint32(time.Now().Add(1 * time.Hour).Unix()) + + rootValidationData, rvErr := CreateMcmRootData( + McmRootInput{ + Multisig: multisigConfigPDA, + Operations: opNodes, + PreOpCount: 0, + PostOpCount: uint64(len(opNodes)), + ValidUntil: validUntil, + OverridePreviousRoot: false, + }, + ) + require.NoError(t, rvErr) + signaturesPDA := RootSignaturesAddress(testMsigName, rootValidationData.Root, validUntil) + + t.Run("preload signatures", func(t *testing.T) { + signers, getSignerErr := eth.GetEvmSigners(config.SignerPrivateKeys) + require.NoError(t, getSignerErr, "Failed to get signers") + + signatures, sigsErr := BulkSignOnMsgHash(signers, rootValidationData.EthMsgHash) + require.NoError(t, sigsErr) + + preloadIxs, plerr := McmPreloadSignaturesIxs(signatures, testMsigName, rootValidationData.Root, validUntil, signaturesPDA, admin.PublicKey(), config.MaxAppendSignatureBatchSize) + require.NoError(t, plerr) + + for _, ix := range preloadIxs { + utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + } + + var sigAccount mcm.RootSignatures + queryErr := utils.GetAccountDataBorshInto(ctx, solanaGoClient, signaturesPDA, config.DefaultCommitment, &sigAccount) + require.NoError(t, queryErr, "failed to get account info") + + require.Equal(t, true, sigAccount.IsFinalized) + require.Equal(t, true, sigAccount.TotalSignatures == uint8(len(signatures))) + + // check if the sigs are registered correctly + for i, sig := range sigAccount.Signatures { + require.Equal(t, signatures[i], sig) + } + }) + + newIx, setRootIxErr := mcm.NewSetRootInstruction( + testMsigName, + rootValidationData.Root, + validUntil, + rootValidationData.Metadata, + rootValidationData.MetadataProof, + signaturesPDA, + rootMetadataPDA, + SeenSignedHashesAddress(testMsigName, rootValidationData.Root, validUntil), + expiringRootAndOpCountPDA, + multisigConfigPDA, + admin.PublicKey(), + solana.SystemProgramID, + ).ValidateAndBuild() + require.NoError(t, setRootIxErr) + + tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{newIx}, admin, config.DefaultCommitment, utils.AddComputeUnitLimit(1_400_000)) + require.NotNil(t, tx) + + parsedLogs := utils.ParseLogMessages(tx.Meta.LogMessages, + []utils.EventMapping{ + utils.EventMappingFor[NewRoot]("NewRoot"), + }, + ) + event := parsedLogs[0].EventData[0].Data.(*NewRoot) + require.Equal(t, rootValidationData.Root, event.Root) + require.Equal(t, validUntil, event.ValidUntil) + require.Equal(t, rootValidationData.Metadata.ChainId, event.MetadataChainID) + require.Equal(t, multisigConfigPDA, event.MetadataMultisig) + require.Equal(t, rootValidationData.Metadata.PreOpCount, event.MetadataPreOpCount) + require.Equal(t, rootValidationData.Metadata.PostOpCount, event.MetadataPostOpCount) + require.Equal(t, rootValidationData.Metadata.OverridePreviousRoot, event.MetadataOverridePreviousRoot) + + var newRootAndOpCount mcm.ExpiringRootAndOpCount + + err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, expiringRootAndOpCountPDA, config.DefaultCommitment, &newRootAndOpCount) + require.NoError(t, err, "failed to get account info") + + require.Equal(t, rootValidationData.Root, newRootAndOpCount.Root) + require.Equal(t, validUntil, newRootAndOpCount.ValidUntil) + require.Equal(t, rootValidationData.Metadata.PreOpCount, newRootAndOpCount.OpCount) + + // get config and validate + var newRootMetadata mcm.RootMetadata + err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, rootMetadataPDA, config.DefaultCommitment, &newRootMetadata) + require.NoError(t, err, "failed to get account info") + + require.Equal(t, rootValidationData.Metadata.ChainId, newRootMetadata.ChainId) + require.Equal(t, rootValidationData.Metadata.Multisig, newRootMetadata.Multisig) + require.Equal(t, rootValidationData.Metadata.PreOpCount, newRootMetadata.PreOpCount) + require.Equal(t, rootValidationData.Metadata.PostOpCount, newRootMetadata.PostOpCount) + require.Equal(t, rootValidationData.Metadata.OverridePreviousRoot, newRootMetadata.OverridePreviousRoot) + }) + }) + t.Run("mcm execute happy path", func(t *testing.T) { for i, op := range opNodes { proofs, proofsErr := op.Proofs() @@ -786,36 +959,10 @@ func TestMcmSetRootAndExecute(t *testing.T) { require.NoError(t, configErr) t.Run("setup: load signers and set_config", func(t *testing.T) { - ixs := make([]solana.Instruction, 0) - - parsedTotalSigners, pErr := mcmsUtils.SafeToUint8(len(mcmConfig.SignerAddresses)) - require.NoError(t, pErr) - initSignersIx, isErr := mcm.NewInitSignersInstruction( - testMsigName, - parsedTotalSigners, - multisigConfigPDA, - configSignersPDA, - admin.PublicKey(), - solana.SystemProgramID, - ).ValidateAndBuild() - - require.NoError(t, isErr) - ixs = append(ixs, initSignersIx) + preloadIxs, pierr := McmPreloadSignersIxs(mcmConfig.SignerAddresses, testMsigName, multisigConfigPDA, configSignersPDA, admin.PublicKey(), config.MaxAppendSignerBatchSize) + require.NoError(t, pierr) - appendSignersIxs, asErr := AppendSignersIxs(mcmConfig.SignerAddresses, testMsigName, multisigConfigPDA, configSignersPDA, admin.PublicKey(), config.MaxAppendSignerBatchSize) - require.NoError(t, asErr) - ixs = append(ixs, appendSignersIxs...) - - finalizeSignersIx, fsErr := mcm.NewFinalizeSignersInstruction( - testMsigName, - multisigConfigPDA, - configSignersPDA, - admin.PublicKey(), - ).ValidateAndBuild() - require.NoError(t, fsErr) - ixs = append(ixs, finalizeSignersIx) - - for _, ix := range ixs { + for _, ix := range preloadIxs { utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } @@ -839,6 +986,8 @@ func TestMcmSetRootAndExecute(t *testing.T) { mcmConfig.ClearRoot, multisigConfigPDA, configSignersPDA, + rootMetadataPDA, + expiringRootAndOpCountPDA, admin.PublicKey(), solana.SystemProgramID, ).ValidateAndBuild() diff --git a/chains/solana/contracts/tests/mcms/mcm_timelock_test.go b/chains/solana/contracts/tests/mcms/mcm_timelock_test.go index 9043caea..d62a8cf7 100644 --- a/chains/solana/contracts/tests/mcms/mcm_timelock_test.go +++ b/chains/solana/contracts/tests/mcms/mcm_timelock_test.go @@ -175,6 +175,8 @@ func TestMcmWithTimelock(t *testing.T) { msig.RawConfig.ClearRoot, msig.ConfigPDA, msig.ConfigSignersPDA, + msig.RootMetadataPDA, + msig.ExpiringRootAndOpCountPDA, admin.PublicKey(), solana.SystemProgramID, ).ValidateAndBuild() @@ -347,7 +349,7 @@ func TestMcmWithTimelock(t *testing.T) { id := acceptOwnershipOp.OperationID() operationPDA := acceptOwnershipOp.OperationPDA() - ixs, ierr := TimelockPreloadOperationIxs(ctx, acceptOwnershipOp, admin.PublicKey(), solanaGoClient) + ixs, ierr := TimelockPreloadOperationIxs(acceptOwnershipOp, admin.PublicKey()) require.NoError(t, ierr) for _, ix := range ixs { utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) @@ -515,7 +517,7 @@ func TestMcmWithTimelock(t *testing.T) { opToSchedule.AddInstruction(ix, []solana.PublicKey{v.tokenProgram}) } - ixs, ierr := TimelockPreloadOperationIxs(ctx, opToSchedule, admin.PublicKey(), solanaGoClient) + ixs, ierr := TimelockPreloadOperationIxs(opToSchedule, admin.PublicKey()) require.NoError(t, ierr) for _, ix := range ixs { utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) @@ -554,39 +556,10 @@ func TestMcmWithTimelock(t *testing.T) { signaturesPDA := proposerMsig.RootSignaturesPDA(rootValidationData.Root, validUntil) t.Run("mcm:preload signatures", func(t *testing.T) { - parsedTotalSigs, pErr := mcmsUtils.SafeToUint8(len(signatures)) - require.NoError(t, pErr) + preloadIxs, plerr := McmPreloadSignaturesIxs(signatures, proposerMsig.PaddedName, rootValidationData.Root, validUntil, signaturesPDA, admin.PublicKey(), config.MaxAppendSignatureBatchSize) + require.NoError(t, plerr) - ixs := make([]solana.Instruction, 0) - - initSigsIx, isErr := mcm.NewInitSignaturesInstruction( - proposerMsig.PaddedName, - rootValidationData.Root, - validUntil, - parsedTotalSigs, - signaturesPDA, - admin.PublicKey(), // auth from someone who call set_root - solana.SystemProgramID, - ).ValidateAndBuild() - - require.NoError(t, isErr) - ixs = append(ixs, initSigsIx) - - appendSigsIxs, asErr := AppendSignaturesIxs(signatures, proposerMsig.PaddedName, rootValidationData.Root, validUntil, signaturesPDA, admin.PublicKey(), config.MaxAppendSignatureBatchSize) - require.NoError(t, asErr) - ixs = append(ixs, appendSigsIxs...) - - finalizeSigsIx, fsErr := mcm.NewFinalizeSignaturesInstruction( - proposerMsig.PaddedName, - rootValidationData.Root, - validUntil, - signaturesPDA, - admin.PublicKey(), - ).ValidateAndBuild() - require.NoError(t, fsErr) - ixs = append(ixs, finalizeSigsIx) - - for _, ix := range ixs { + for _, ix := range preloadIxs { utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } @@ -646,7 +619,6 @@ func TestMcmWithTimelock(t *testing.T) { require.Equal(t, validUntil, newRootAndOpCount.ValidUntil) require.Equal(t, rootValidationData.Metadata.PreOpCount, newRootAndOpCount.OpCount) - // get config and validate var newRootMetadata mcm.RootMetadata err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, proposerMsig.RootMetadataPDA, config.DefaultCommitment, &newRootMetadata) require.NoError(t, err, "failed to get account info") @@ -933,7 +905,7 @@ func TestMcmWithTimelock(t *testing.T) { for i, op := range timelockOps { t.Run(fmt.Sprintf("prepare mcm op node %d with timelock::schedule_batch ix", i), func(t *testing.T) { - ixs, ierr := TimelockPreloadOperationIxs(ctx, op, admin.PublicKey(), solanaGoClient) + ixs, ierr := TimelockPreloadOperationIxs(op, admin.PublicKey()) require.NoError(t, ierr) for _, ix := range ixs { utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) @@ -982,39 +954,10 @@ func TestMcmWithTimelock(t *testing.T) { //////////////////////////////////////////////// // mcm::set_root - with preloading signatures // //////////////////////////////////////////////// - parsedTotalSigs, pErr := mcmsUtils.SafeToUint8(len(signatures)) - require.NoError(t, pErr) + preloadIxs, plerr := McmPreloadSignaturesIxs(signatures, proposerMsig.PaddedName, rootValidationData.Root, validUntil, proposerMsig.RootSignaturesPDA(rootValidationData.Root, validUntil), admin.PublicKey(), config.MaxAppendSignatureBatchSize) + require.NoError(t, plerr) - ixs := make([]solana.Instruction, 0) - - initSigsIx, isErr := mcm.NewInitSignaturesInstruction( - proposerMsig.PaddedName, - rootValidationData.Root, - validUntil, - parsedTotalSigs, - proposerMsig.RootSignaturesPDA(rootValidationData.Root, validUntil), - admin.PublicKey(), // auth from someone who call set_root - solana.SystemProgramID, - ).ValidateAndBuild() - - require.NoError(t, isErr) - ixs = append(ixs, initSigsIx) - - appendSigsIxs, asErr := AppendSignaturesIxs(signatures, proposerMsig.PaddedName, rootValidationData.Root, validUntil, proposerMsig.RootSignaturesPDA(rootValidationData.Root, validUntil), admin.PublicKey(), config.MaxAppendSignatureBatchSize) - require.NoError(t, asErr) - ixs = append(ixs, appendSigsIxs...) - - finalizeSigsIx, fsErr := mcm.NewFinalizeSignaturesInstruction( - proposerMsig.PaddedName, - rootValidationData.Root, - validUntil, - proposerMsig.RootSignaturesPDA(rootValidationData.Root, validUntil), - admin.PublicKey(), - ).ValidateAndBuild() - require.NoError(t, fsErr) - ixs = append(ixs, finalizeSigsIx) - - for _, ix := range ixs { + for _, ix := range preloadIxs { utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } @@ -1183,45 +1126,13 @@ func TestMcmWithTimelock(t *testing.T) { signaturesPDA := canceller.RootSignaturesPDA(rootValidationData.Root, validUntil) - parsedTotalSigs, err := mcmsUtils.SafeToUint8(len(signatures)) - require.NoError(t, err) + preloadIxs, plerr := McmPreloadSignaturesIxs(signatures, canceller.PaddedName, rootValidationData.Root, validUntil, signaturesPDA, admin.PublicKey(), config.MaxAppendSignatureBatchSize) + require.NoError(t, plerr) - initSigsIx, err := mcm.NewInitSignaturesInstruction( - canceller.PaddedName, - rootValidationData.Root, - validUntil, - parsedTotalSigs, - signaturesPDA, - admin.PublicKey(), - solana.SystemProgramID, - ).ValidateAndBuild() - require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{initSigsIx}, admin, config.DefaultCommitment) - - appendSigsIxs, err := AppendSignaturesIxs( - signatures, - canceller.PaddedName, - rootValidationData.Root, - validUntil, - signaturesPDA, - admin.PublicKey(), - config.MaxAppendSignatureBatchSize, - ) - require.NoError(t, err) - for _, ix := range appendSigsIxs { + for _, ix := range preloadIxs { utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } - finalizeSigsIx, err := mcm.NewFinalizeSignaturesInstruction( - canceller.PaddedName, - rootValidationData.Root, - validUntil, - signaturesPDA, - admin.PublicKey(), - ).ValidateAndBuild() - require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{finalizeSigsIx}, admin, config.DefaultCommitment) - setRootIx, err := mcm.NewSetRootInstruction( canceller.PaddedName, rootValidationData.Root, @@ -1323,7 +1234,7 @@ func TestMcmWithTimelock(t *testing.T) { newOp3.AddInstruction(ix2, []solana.PublicKey{tokenProgram}) newOp3.AddInstruction(ix3, []solana.PublicKey{tokenProgram}) - ixs, err := TimelockPreloadOperationIxs(ctx, newOp3, admin.PublicKey(), solanaGoClient) + ixs, err := TimelockPreloadOperationIxs(newOp3, admin.PublicKey()) require.NoError(t, err) for _, ix := range ixs { utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) @@ -1365,48 +1276,13 @@ func TestMcmWithTimelock(t *testing.T) { signaturesPDA := proposerMsig.RootSignaturesPDA(rootValidationData.Root, validUntil) - // Initialize signatures - parsedTotalSigs, err := mcmsUtils.SafeToUint8(len(signatures)) - require.NoError(t, err) - - initSigsIx, err := mcm.NewInitSignaturesInstruction( - proposerMsig.PaddedName, - rootValidationData.Root, - validUntil, - parsedTotalSigs, - signaturesPDA, - admin.PublicKey(), - solana.SystemProgramID, - ).ValidateAndBuild() - require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{initSigsIx}, admin, config.DefaultCommitment) - - // Append signatures - appendSigsIxs, err := AppendSignaturesIxs( - signatures, - proposerMsig.PaddedName, - rootValidationData.Root, - validUntil, - signaturesPDA, - admin.PublicKey(), - config.MaxAppendSignatureBatchSize, - ) - require.NoError(t, err) - for _, ix := range appendSigsIxs { + // preload signatures + preloadIxs, plerr := McmPreloadSignaturesIxs(signatures, proposerMsig.PaddedName, rootValidationData.Root, validUntil, signaturesPDA, admin.PublicKey(), config.MaxAppendSignatureBatchSize) + require.NoError(t, plerr) + for _, ix := range preloadIxs { utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } - // Finalize signatures - finalizeSigsIx, err := mcm.NewFinalizeSignaturesInstruction( - proposerMsig.PaddedName, - rootValidationData.Root, - validUntil, - signaturesPDA, - admin.PublicKey(), - ).ValidateAndBuild() - require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{finalizeSigsIx}, admin, config.DefaultCommitment) - // Set root setRootIx, err := mcm.NewSetRootInstruction( proposerMsig.PaddedName, diff --git a/chains/solana/contracts/tests/mcms/timelock.go b/chains/solana/contracts/tests/mcms/timelock.go index 764e722b..22b8c72c 100644 --- a/chains/solana/contracts/tests/mcms/timelock.go +++ b/chains/solana/contracts/tests/mcms/timelock.go @@ -90,7 +90,7 @@ func TimelockBatchAddAccessIxs(ctx context.Context, roleAcAccount solana.PublicK } // instructions builder for preloading instructions to timelock operation -func TimelockPreloadOperationIxs(ctx context.Context, op TimelockOperation, authority solana.PublicKey, client *rpc.Client) ([]solana.Instruction, error) { +func TimelockPreloadOperationIxs(op TimelockOperation, authority solana.PublicKey) ([]solana.Instruction, error) { ixs := []solana.Instruction{} initOpIx, ioErr := timelock.NewInitializeOperationInstruction( op.OperationID(), diff --git a/chains/solana/contracts/tests/mcms/timelock_bypasser_execute_test.go b/chains/solana/contracts/tests/mcms/timelock_bypasser_execute_test.go index 328088af..322e782e 100644 --- a/chains/solana/contracts/tests/mcms/timelock_bypasser_execute_test.go +++ b/chains/solana/contracts/tests/mcms/timelock_bypasser_execute_test.go @@ -241,7 +241,7 @@ func TestTimelockBypasserExecute(t *testing.T) { operationPDA := op.OperationPDA() signer := roleMap[timelock.Proposer_Role].RandomPick() - ixs, err := TimelockPreloadOperationIxs(ctx, op, signer.PublicKey(), solanaGoClient) + ixs, err := TimelockPreloadOperationIxs(op, signer.PublicKey()) require.NoError(t, err) for _, ix := range ixs { utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, signer, config.DefaultCommitment) diff --git a/chains/solana/contracts/tests/mcms/timelock_rbac_test.go b/chains/solana/contracts/tests/mcms/timelock_rbac_test.go index 7cd7a743..a7994d67 100644 --- a/chains/solana/contracts/tests/mcms/timelock_rbac_test.go +++ b/chains/solana/contracts/tests/mcms/timelock_rbac_test.go @@ -271,7 +271,7 @@ func TestTimelockRBAC(t *testing.T) { nonProposer := roleMap[timelock.Executor_Role].RandomPick() ac := roleMap[timelock.Proposer_Role].AccessController - ixs, prierr := TimelockPreloadOperationIxs(ctx, nonExecutableOp, nonProposer.PublicKey(), solanaGoClient) + ixs, prierr := TimelockPreloadOperationIxs(nonExecutableOp, nonProposer.PublicKey()) require.NoError(t, prierr) for _, ix := range ixs { utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, nonProposer, config.DefaultCommitment) @@ -340,7 +340,7 @@ func TestTimelockRBAC(t *testing.T) { ix := system.NewTransferInstruction(1*solana.LAMPORTS_PER_SOL, admin.PublicKey(), config.TimelockSignerPDA).Build() nonExecutableOp2.AddInstruction(ix, []solana.PublicKey{}) - ixs, prerr := TimelockPreloadOperationIxs(ctx, nonExecutableOp2, proposer.PublicKey(), solanaGoClient) + ixs, prerr := TimelockPreloadOperationIxs(nonExecutableOp2, proposer.PublicKey()) require.NoError(t, prerr) for _, ix := range ixs { utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, proposer, config.DefaultCommitment) diff --git a/chains/solana/contracts/tests/mcms/timelock_schedule_execute_test.go b/chains/solana/contracts/tests/mcms/timelock_schedule_execute_test.go index 0a2f47aa..8f07cb25 100644 --- a/chains/solana/contracts/tests/mcms/timelock_schedule_execute_test.go +++ b/chains/solana/contracts/tests/mcms/timelock_schedule_execute_test.go @@ -289,7 +289,7 @@ func TestTimelockScheduleAndExecute(t *testing.T) { t.Run("success: schedule all operations", func(t *testing.T) { for _, op := range []TimelockOperation{op1, op2, op3} { - invalidIxs, ierr := TimelockPreloadOperationIxs(ctx, op, proposer.PublicKey(), solanaGoClient) + invalidIxs, ierr := TimelockPreloadOperationIxs(op, proposer.PublicKey()) require.NoError(t, ierr) for _, ix := range invalidIxs { utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, proposer, config.DefaultCommitment) @@ -311,7 +311,7 @@ func TestTimelockScheduleAndExecute(t *testing.T) { }) // re-preload instructions - ixs, err := TimelockPreloadOperationIxs(ctx, op, proposer.PublicKey(), solanaGoClient) + ixs, err := TimelockPreloadOperationIxs(op, proposer.PublicKey()) require.NoError(t, err) for _, ix := range ixs { utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, proposer, config.DefaultCommitment) @@ -584,7 +584,7 @@ func TestTimelockScheduleAndExecute(t *testing.T) { id := op.OperationID() operationPDA := op.OperationPDA() - ixs, err := TimelockPreloadOperationIxs(ctx, op, proposer.PublicKey(), solanaGoClient) + ixs, err := TimelockPreloadOperationIxs(op, proposer.PublicKey()) require.NoError(t, err) for _, ix := range ixs { utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, proposer, config.DefaultCommitment) diff --git a/chains/solana/gobindings/mcm/SetConfig.go b/chains/solana/gobindings/mcm/SetConfig.go index 722f72da..7fd44d5d 100644 --- a/chains/solana/gobindings/mcm/SetConfig.go +++ b/chains/solana/gobindings/mcm/SetConfig.go @@ -22,16 +22,20 @@ type SetConfig struct { // // [1] = [WRITE] configSigners // - // [2] = [WRITE, SIGNER] authority + // [2] = [WRITE] rootMetadata // - // [3] = [] systemProgram + // [3] = [WRITE] expiringRootAndOpCount + // + // [4] = [WRITE, SIGNER] authority + // + // [5] = [] systemProgram ag_solanago.AccountMetaSlice `bin:"-" borsh_skip:"true"` } // NewSetConfigInstructionBuilder creates a new `SetConfig` instruction builder. func NewSetConfigInstructionBuilder() *SetConfig { nd := &SetConfig{ - AccountMetaSlice: make(ag_solanago.AccountMetaSlice, 4), + AccountMetaSlice: make(ag_solanago.AccountMetaSlice, 6), } return nd } @@ -88,26 +92,48 @@ func (inst *SetConfig) GetConfigSignersAccount() *ag_solanago.AccountMeta { return inst.AccountMetaSlice[1] } +// SetRootMetadataAccount sets the "rootMetadata" account. +func (inst *SetConfig) SetRootMetadataAccount(rootMetadata ag_solanago.PublicKey) *SetConfig { + inst.AccountMetaSlice[2] = ag_solanago.Meta(rootMetadata).WRITE() + return inst +} + +// GetRootMetadataAccount gets the "rootMetadata" account. +func (inst *SetConfig) GetRootMetadataAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[2] +} + +// SetExpiringRootAndOpCountAccount sets the "expiringRootAndOpCount" account. +func (inst *SetConfig) SetExpiringRootAndOpCountAccount(expiringRootAndOpCount ag_solanago.PublicKey) *SetConfig { + inst.AccountMetaSlice[3] = ag_solanago.Meta(expiringRootAndOpCount).WRITE() + return inst +} + +// GetExpiringRootAndOpCountAccount gets the "expiringRootAndOpCount" account. +func (inst *SetConfig) GetExpiringRootAndOpCountAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[3] +} + // SetAuthorityAccount sets the "authority" account. func (inst *SetConfig) SetAuthorityAccount(authority ag_solanago.PublicKey) *SetConfig { - inst.AccountMetaSlice[2] = ag_solanago.Meta(authority).WRITE().SIGNER() + inst.AccountMetaSlice[4] = ag_solanago.Meta(authority).WRITE().SIGNER() return inst } // GetAuthorityAccount gets the "authority" account. func (inst *SetConfig) GetAuthorityAccount() *ag_solanago.AccountMeta { - return inst.AccountMetaSlice[2] + return inst.AccountMetaSlice[4] } // SetSystemProgramAccount sets the "systemProgram" account. func (inst *SetConfig) SetSystemProgramAccount(systemProgram ag_solanago.PublicKey) *SetConfig { - inst.AccountMetaSlice[3] = ag_solanago.Meta(systemProgram) + inst.AccountMetaSlice[5] = ag_solanago.Meta(systemProgram) return inst } // GetSystemProgramAccount gets the "systemProgram" account. func (inst *SetConfig) GetSystemProgramAccount() *ag_solanago.AccountMeta { - return inst.AccountMetaSlice[3] + return inst.AccountMetaSlice[5] } func (inst SetConfig) Build() *Instruction { @@ -156,9 +182,15 @@ func (inst *SetConfig) Validate() error { return errors.New("accounts.ConfigSigners is not set") } if inst.AccountMetaSlice[2] == nil { - return errors.New("accounts.Authority is not set") + return errors.New("accounts.RootMetadata is not set") } if inst.AccountMetaSlice[3] == nil { + return errors.New("accounts.ExpiringRootAndOpCount is not set") + } + if inst.AccountMetaSlice[4] == nil { + return errors.New("accounts.Authority is not set") + } + if inst.AccountMetaSlice[5] == nil { return errors.New("accounts.SystemProgram is not set") } } @@ -183,11 +215,13 @@ func (inst *SetConfig) EncodeToTree(parent ag_treeout.Branches) { }) // Accounts of the instruction: - instructionBranch.Child("Accounts[len=4]").ParentFunc(func(accountsBranch ag_treeout.Branches) { - accountsBranch.Child(ag_format.Meta("multisigConfig", inst.AccountMetaSlice[0])) - accountsBranch.Child(ag_format.Meta(" configSigners", inst.AccountMetaSlice[1])) - accountsBranch.Child(ag_format.Meta(" authority", inst.AccountMetaSlice[2])) - accountsBranch.Child(ag_format.Meta(" systemProgram", inst.AccountMetaSlice[3])) + instructionBranch.Child("Accounts[len=6]").ParentFunc(func(accountsBranch ag_treeout.Branches) { + accountsBranch.Child(ag_format.Meta(" multisigConfig", inst.AccountMetaSlice[0])) + accountsBranch.Child(ag_format.Meta(" configSigners", inst.AccountMetaSlice[1])) + accountsBranch.Child(ag_format.Meta(" rootMetadata", inst.AccountMetaSlice[2])) + accountsBranch.Child(ag_format.Meta("expiringRootAndOpCount", inst.AccountMetaSlice[3])) + accountsBranch.Child(ag_format.Meta(" authority", inst.AccountMetaSlice[4])) + accountsBranch.Child(ag_format.Meta(" systemProgram", inst.AccountMetaSlice[5])) }) }) }) @@ -261,6 +295,8 @@ func NewSetConfigInstruction( // Accounts: multisigConfig ag_solanago.PublicKey, configSigners ag_solanago.PublicKey, + rootMetadata ag_solanago.PublicKey, + expiringRootAndOpCount ag_solanago.PublicKey, authority ag_solanago.PublicKey, systemProgram ag_solanago.PublicKey) *SetConfig { return NewSetConfigInstructionBuilder(). @@ -271,6 +307,8 @@ func NewSetConfigInstruction( SetClearRoot(clearRoot). SetMultisigConfigAccount(multisigConfig). SetConfigSignersAccount(configSigners). + SetRootMetadataAccount(rootMetadata). + SetExpiringRootAndOpCountAccount(expiringRootAndOpCount). SetAuthorityAccount(authority). SetSystemProgramAccount(systemProgram) }