Skip to content

Commit

Permalink
solana: multiple timelock instances (#420)
Browse files Browse the repository at this point in the history
* feat: multiple timelock instances

* chore: timelock id in tests & clean up

* chore: updated function blocker note

* chore: mcms helpers clean up

* chore: timelock multiple instance test setup

* chore: tests for multiple timelock instances wip

* chore: timelock operation isolation tests

* chore: tests for instance collision check & util clean up

* chore: remove unused error code

* chore: clean up fn blocker with max

* chore: multisig name -> id for consistency

* fix: config field name
  • Loading branch information
jadepark-dev authored Jan 10, 2025
1 parent 6a38ffc commit 516f05d
Show file tree
Hide file tree
Showing 65 changed files with 2,304 additions and 948 deletions.
8 changes: 4 additions & 4 deletions chains/solana/contracts/programs/mcm/src/constant.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Business-logic constants
pub const MAX_NUM_SIGNERS: usize = 180; // maximum number of signers supported
pub const NUM_GROUPS: usize = 32; // Value copied from EVM reference contract
pub const NUM_GROUPS: usize = 32; // maximum number of signer groups supported

// fixed size msig name for distinguishing different multisig instances
pub const MULTISIG_NAME_PADDED: usize = 32;
// fixed size msig id for distinguishing different multisig instances
pub const MULTISIG_ID_PADDED: usize = 32;

// PDA seeds
// Note: These seeds are not full seed, for unique seeds, multisig_name should be appended
// Note: These seeds are not full seed, for unique seeds per instance, multisi_id should be appended
pub const SIGNER_SEED: &[u8] = b"multisig_signer"; // seed for dataless pda signing CPI
pub const CONFIG_SEED: &[u8] = b"multisig_config";
pub const CONFIG_SIGNERS_SEED: &[u8] = b"multisig_config_signers";
Expand Down
14 changes: 7 additions & 7 deletions chains/solana/contracts/programs/mcm/src/instructions/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::state::root::*;

pub fn execute<'info>(
ctx: Context<'_, '_, '_, 'info, Execute<'info>>,
multisig_name: [u8; MULTISIG_NAME_PADDED],
multisig_id: [u8; MULTISIG_ID_PADDED],
chain_id: u64,
nonce: u64,
data: Vec<u8>,
Expand Down Expand Up @@ -84,7 +84,7 @@ pub fn execute<'info>(

let seeds = &[
SIGNER_SEED,
multisig_name.as_ref(),
multisig_id.as_ref(),
&[ctx.bumps.multisig_signer],
];
let signer = &[&seeds[..]];
Expand All @@ -103,23 +103,23 @@ pub fn execute<'info>(
}

#[derive(Accounts)]
#[instruction(multisig_name: [u8; MULTISIG_NAME_PADDED])]
#[instruction(multisig_id: [u8; MULTISIG_ID_PADDED])]
pub struct Execute<'info> {
#[account(mut, seeds = [CONFIG_SEED, multisig_name.as_ref()], bump)]
#[account(mut, seeds = [CONFIG_SEED, multisig_id.as_ref()], bump)]
pub multisig_config: Account<'info, MultisigConfig>,

#[account(seeds = [ROOT_METADATA_SEED, multisig_name.as_ref()], bump)]
#[account(seeds = [ROOT_METADATA_SEED, multisig_id.as_ref()], bump)]
pub root_metadata: Account<'info, RootMetadata>,

#[account(mut, seeds = [EXPIRING_ROOT_AND_OP_COUNT_SEED, multisig_name.as_ref()], bump)]
#[account(mut, seeds = [EXPIRING_ROOT_AND_OP_COUNT_SEED, multisig_id.as_ref()], bump)]
pub expiring_root_and_op_count: Account<'info, ExpiringRootAndOpCount>,

/// CHECK: This is just used to invoke it through the CPI, it's value is not accessed directly
pub to: UncheckedAccount<'info>,

/// CHECK: program signer PDA that can hold balance
#[account(
seeds = [SIGNER_SEED, multisig_name.as_ref()],
seeds = [SIGNER_SEED, multisig_id.as_ref()],
bump
)]
pub multisig_signer: UncheckedAccount<'info>,
Expand Down
44 changes: 22 additions & 22 deletions chains/solana/contracts/programs/mcm/src/instructions/set_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::state::root::*;
/// Set the configuration for the multisig instance after validating the input
pub fn set_config(
ctx: Context<SetConfig>,
_multisig_name: [u8; MULTISIG_NAME_PADDED], // for pda derivation
_multisig_id: [u8; MULTISIG_ID_PADDED], // for pda derivation
signer_groups: Vec<u8>,
group_quorums: [u8; NUM_GROUPS],
group_parents: [u8; NUM_GROUPS],
Expand Down Expand Up @@ -155,7 +155,7 @@ pub fn set_config(

pub fn init_signers(
ctx: Context<InitSigners>,
_multisig_name: [u8; MULTISIG_NAME_PADDED],
_multisig_id: [u8; MULTISIG_ID_PADDED],
total_signers: u8,
) -> Result<()> {
require!(
Expand All @@ -171,7 +171,7 @@ pub fn init_signers(

pub fn append_signers(
ctx: Context<AppendSigners>,
_multisig_name: [u8; MULTISIG_NAME_PADDED],
_multisig_id: [u8; MULTISIG_ID_PADDED],
signers_batch: Vec<[u8; 20]>,
) -> Result<()> {
let config_signers = &mut ctx.accounts.config_signers;
Expand Down Expand Up @@ -203,7 +203,7 @@ pub fn append_signers(

pub fn clear_signers(
_ctx: Context<ClearSigners>,
_multisig_name: [u8; MULTISIG_NAME_PADDED],
_multisig_id: [u8; MULTISIG_ID_PADDED],
) -> Result<()> {
// NOTE: ctx.accounts.config_signers is closed to be able to re-initialized,
// also allow finalized config_signers to be cleared
Expand All @@ -212,7 +212,7 @@ pub fn clear_signers(

pub fn finalize_signers(
ctx: Context<FinalizeSigners>,
_multisig_name: [u8; MULTISIG_NAME_PADDED],
_multisig_id: [u8; MULTISIG_ID_PADDED],
) -> Result<()> {
let config_signers = &mut ctx.accounts.config_signers;

Expand All @@ -227,11 +227,11 @@ pub fn finalize_signers(
}

#[derive(Accounts)]
#[instruction(multisig_name: [u8; MULTISIG_NAME_PADDED])]
#[instruction(multisig_id: [u8; MULTISIG_ID_PADDED])]
pub struct SetConfig<'info> {
#[account(
mut,
seeds = [CONFIG_SEED, multisig_name.as_ref()],
seeds = [CONFIG_SEED, multisig_id.as_ref()],
bump,
realloc = ANCHOR_DISCRIMINATOR + MultisigConfig::space_with_signers(
config_signers.signer_addresses.len()
Expand All @@ -243,17 +243,17 @@ pub struct SetConfig<'info> {

#[account(
mut,
seeds = [CONFIG_SIGNERS_SEED, multisig_name.as_ref()],
seeds = [CONFIG_SIGNERS_SEED, multisig_id.as_ref()],
bump,
constraint = config_signers.is_finalized @ McmError::SignersNotFinalized,
close = authority // close after config set
)]
pub config_signers: Account<'info, ConfigSigners>, // preloaded signers account

#[account(mut, seeds = [ROOT_METADATA_SEED, multisig_name.as_ref()], bump)]
#[account(mut, seeds = [ROOT_METADATA_SEED, multisig_id.as_ref()], bump)]
pub root_metadata: Account<'info, RootMetadata>,

#[account(mut, seeds = [EXPIRING_ROOT_AND_OP_COUNT_SEED, multisig_name.as_ref()], bump)]
#[account(mut, seeds = [EXPIRING_ROOT_AND_OP_COUNT_SEED, multisig_id.as_ref()], bump)]
pub expiring_root_and_op_count: Account<'info, ExpiringRootAndOpCount>,

#[account(mut, address = multisig_config.owner @ AuthError::Unauthorized)]
Expand All @@ -263,16 +263,16 @@ pub struct SetConfig<'info> {
}

#[derive(Accounts)]
#[instruction(multisig_name: [u8; MULTISIG_NAME_PADDED], total_signers: u8)]
#[instruction(multisig_id: [u8; MULTISIG_ID_PADDED], total_signers: u8)]
pub struct InitSigners<'info> {
#[account(seeds = [CONFIG_SEED, multisig_name.as_ref()], bump)]
#[account(seeds = [CONFIG_SEED, multisig_id.as_ref()], bump)]
pub multisig_config: Account<'info, MultisigConfig>,

#[account(
init,
payer = authority,
space = ConfigSigners::space(total_signers as usize),
seeds = [CONFIG_SIGNERS_SEED, multisig_name.as_ref()],
seeds = [CONFIG_SIGNERS_SEED, multisig_id.as_ref()],
bump
)]
pub config_signers: Account<'info, ConfigSigners>,
Expand All @@ -284,14 +284,14 @@ pub struct InitSigners<'info> {
}

#[derive(Accounts)]
#[instruction(multisig_name: [u8; MULTISIG_NAME_PADDED])]
#[instruction(multisig_id: [u8; MULTISIG_ID_PADDED])]
pub struct AppendSigners<'info> {
#[account(seeds = [CONFIG_SEED, multisig_name.as_ref()], bump)]
#[account(seeds = [CONFIG_SEED, multisig_id.as_ref()], bump)]
pub multisig_config: Account<'info, MultisigConfig>,

#[account(
mut,
seeds = [CONFIG_SIGNERS_SEED, multisig_name.as_ref()],
seeds = [CONFIG_SIGNERS_SEED, multisig_id.as_ref()],
bump,
constraint = !config_signers.is_finalized @ McmError::SignersAlreadyFinalized
)]
Expand All @@ -302,14 +302,14 @@ pub struct AppendSigners<'info> {
}

#[derive(Accounts)]
#[instruction(multisig_name: [u8; MULTISIG_NAME_PADDED])]
#[instruction(multisig_id: [u8; MULTISIG_ID_PADDED])]
pub struct ClearSigners<'info> {
#[account(seeds = [CONFIG_SEED, multisig_name.as_ref()], bump)]
#[account(seeds = [CONFIG_SEED, multisig_id.as_ref()], bump)]
pub multisig_config: Account<'info, MultisigConfig>,

#[account(
mut,
seeds = [CONFIG_SIGNERS_SEED, multisig_name.as_ref()],
seeds = [CONFIG_SIGNERS_SEED, multisig_id.as_ref()],
bump,
close = authority // close so that it can be re-initialized
)]
Expand All @@ -320,14 +320,14 @@ pub struct ClearSigners<'info> {
}

#[derive(Accounts)]
#[instruction(multisig_name: [u8; MULTISIG_NAME_PADDED])]
#[instruction(multisig_id: [u8; MULTISIG_ID_PADDED])]
pub struct FinalizeSigners<'info> {
#[account(seeds = [CONFIG_SEED, multisig_name.as_ref()], bump)]
#[account(seeds = [CONFIG_SEED, multisig_id.as_ref()], bump)]
pub multisig_config: Account<'info, MultisigConfig>,

#[account(
mut,
seeds = [CONFIG_SIGNERS_SEED, multisig_name.as_ref()],
seeds = [CONFIG_SIGNERS_SEED, multisig_id.as_ref()],
bump,
constraint = !config_signers.is_finalized @ McmError::SignersAlreadyFinalized
)]
Expand Down
38 changes: 19 additions & 19 deletions chains/solana/contracts/programs/mcm/src/instructions/set_root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::state::root::*;

pub fn set_root(
ctx: Context<SetRoot>,
_multisig_name: [u8; MULTISIG_NAME_PADDED],
_multisig_id: [u8; MULTISIG_ID_PADDED],
root: [u8; 32],
valid_until: u32,
metadata: RootMetadataInput,
Expand Down Expand Up @@ -172,7 +172,7 @@ fn verify_ecdsa_signatures(

pub fn init_signatures(
ctx: Context<InitSignatures>,
_multisig_name: [u8; MULTISIG_NAME_PADDED],
_multisig_id: [u8; MULTISIG_ID_PADDED],
_root: [u8; 32],
_valid_until: u32,
total_signatures: u8,
Expand All @@ -186,7 +186,7 @@ pub fn init_signatures(

pub fn append_signatures(
ctx: Context<AppendSignatures>,
_multisig_name: [u8; MULTISIG_NAME_PADDED],
_multisig_id: [u8; MULTISIG_ID_PADDED],
_root: [u8; 32],
_valid_until: u32,
signatures_batch: Vec<Signature>,
Expand All @@ -207,7 +207,7 @@ pub fn append_signatures(

pub fn clear_signatures(
_ctx: Context<ClearSignatures>,
_multisig_name: [u8; MULTISIG_NAME_PADDED],
_multisig_id: [u8; MULTISIG_ID_PADDED],
_root: [u8; 32],
_valid_until: u32,
) -> Result<()> {
Expand All @@ -218,7 +218,7 @@ pub fn clear_signatures(

pub fn finalize_signatures(
ctx: Context<FinalizeSignatures>,
_multisig_name: [u8; MULTISIG_NAME_PADDED],
_multisig_id: [u8; MULTISIG_ID_PADDED],
_root: [u8; 32],
_valid_until: u32,
) -> Result<()> {
Expand All @@ -235,33 +235,33 @@ pub fn finalize_signatures(
}

#[derive(Accounts)]
#[instruction(multisig_name: [u8; MULTISIG_NAME_PADDED], root: [u8; 32], valid_until: u32)]
#[instruction(multisig_id: [u8; MULTISIG_ID_PADDED], root: [u8; 32], valid_until: u32)]
pub struct SetRoot<'info> {
#[account(
mut,
seeds = [ROOT_SIGNATURES_SEED, multisig_name.as_ref(), root.as_ref(), valid_until.to_le_bytes().as_ref()],
seeds = [ROOT_SIGNATURES_SEED, multisig_id.as_ref(), root.as_ref(), valid_until.to_le_bytes().as_ref()],
bump,
constraint = root_signatures.is_finalized @ McmError::SignaturesNotFinalized,
close = authority
)]
pub root_signatures: Account<'info, RootSignatures>, // preloaded signatures account

#[account(mut, seeds = [ROOT_METADATA_SEED, multisig_name.as_ref()], bump)]
#[account(mut, seeds = [ROOT_METADATA_SEED, multisig_id.as_ref()], bump)]
pub root_metadata: Account<'info, RootMetadata>,

#[account(
init,
seeds = [SEEN_SIGNED_HASHES_SEED, multisig_name.as_ref(), root.as_ref(), valid_until.to_le_bytes().as_ref()],
seeds = [SEEN_SIGNED_HASHES_SEED, multisig_id.as_ref(), root.as_ref(), valid_until.to_le_bytes().as_ref()],
bump,
payer = authority,
space = ANCHOR_DISCRIMINATOR + SeenSignedHash::INIT_SPACE,
)]
pub seen_signed_hashes: Account<'info, SeenSignedHash>,

#[account(mut, seeds = [EXPIRING_ROOT_AND_OP_COUNT_SEED, multisig_name.as_ref()], bump)]
#[account(mut, seeds = [EXPIRING_ROOT_AND_OP_COUNT_SEED, multisig_id.as_ref()], bump)]
pub expiring_root_and_op_count: Account<'info, ExpiringRootAndOpCount>,

#[account(seeds = [CONFIG_SEED, multisig_name.as_ref()],bump)]
#[account(seeds = [CONFIG_SEED, multisig_id.as_ref()],bump)]
pub multisig_config: Account<'info, MultisigConfig>,

#[account(mut)]
Expand All @@ -272,7 +272,7 @@ pub struct SetRoot<'info> {

#[derive(Accounts)]
#[instruction(
multisig_name: [u8; MULTISIG_NAME_PADDED],
multisig_id: [u8; MULTISIG_ID_PADDED],
root: [u8; 32],
valid_until: u32,
total_signatures: u8,
Expand All @@ -282,7 +282,7 @@ pub struct InitSignatures<'info> {
init,
payer = authority,
space = RootSignatures::space(total_signatures as usize),
seeds = [ROOT_SIGNATURES_SEED, multisig_name.as_ref(), root.as_ref(), valid_until.to_le_bytes().as_ref()],
seeds = [ROOT_SIGNATURES_SEED, multisig_id.as_ref(), root.as_ref(), valid_until.to_le_bytes().as_ref()],
bump
)]
pub signatures: Account<'info, RootSignatures>,
Expand All @@ -294,11 +294,11 @@ pub struct InitSignatures<'info> {
}

#[derive(Accounts)]
#[instruction(multisig_name: [u8; MULTISIG_NAME_PADDED], root: [u8; 32], valid_until: u32)]
#[instruction(multisig_id: [u8; MULTISIG_ID_PADDED], root: [u8; 32], valid_until: u32)]
pub struct AppendSignatures<'info> {
#[account(
mut,
seeds = [ROOT_SIGNATURES_SEED, multisig_name.as_ref(), root.as_ref(), valid_until.to_le_bytes().as_ref()],
seeds = [ROOT_SIGNATURES_SEED, multisig_id.as_ref(), root.as_ref(), valid_until.to_le_bytes().as_ref()],
bump,
constraint = !signatures.is_finalized @ McmError::SignaturesAlreadyFinalized
)]
Expand All @@ -309,11 +309,11 @@ pub struct AppendSignatures<'info> {
}

#[derive(Accounts)]
#[instruction(multisig_name: [u8; MULTISIG_NAME_PADDED], root: [u8; 32], valid_until: u32)]
#[instruction(multisig_id: [u8; MULTISIG_ID_PADDED], root: [u8; 32], valid_until: u32)]
pub struct ClearSignatures<'info> {
#[account(
mut,
seeds = [ROOT_SIGNATURES_SEED, multisig_name.as_ref(), root.as_ref(), valid_until.to_le_bytes().as_ref()],
seeds = [ROOT_SIGNATURES_SEED, multisig_id.as_ref(), root.as_ref(), valid_until.to_le_bytes().as_ref()],
bump,
close = authority // close so that it can be re-initialized
)]
Expand All @@ -324,11 +324,11 @@ pub struct ClearSignatures<'info> {
}

#[derive(Accounts)]
#[instruction(multisig_name: [u8; MULTISIG_NAME_PADDED], root: [u8; 32], valid_until: u32)]
#[instruction(multisig_id: [u8; MULTISIG_ID_PADDED], root: [u8; 32], valid_until: u32)]
pub struct FinalizeSignatures<'info> {
#[account(
mut,
seeds = [ROOT_SIGNATURES_SEED, multisig_name.as_ref(), root.as_ref(), valid_until.to_le_bytes().as_ref()],
seeds = [ROOT_SIGNATURES_SEED, multisig_id.as_ref(), root.as_ref(), valid_until.to_le_bytes().as_ref()],
bump,
constraint = !signatures.is_finalized @ McmError::SignaturesAlreadyFinalized
)]
Expand Down
Loading

0 comments on commit 516f05d

Please sign in to comment.