Skip to content

Commit

Permalink
solana: implement chunk-based instruction data upload for timelock ops (
Browse files Browse the repository at this point in the history
#528)

* feat: uploading instruction data by chunk

* chore: chunk append tests

* chore: refactor operation helpers

* chore: operation related docs

* chore: fmt

* chore: golint shadowing errors

* feat: timelock operation state enum management (#549)
  • Loading branch information
jadepark-dev authored Feb 10, 2025
1 parent 586c037 commit 2b97679
Show file tree
Hide file tree
Showing 48 changed files with 2,686 additions and 480 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ pub mod external_program_cpi_stub {
u8_value.value += 1;
Ok(())
}

///instruction that accepts arbitrarily large instruction data.
pub fn big_instruction_data(_ctx: Context<Empty>, data: Vec<u8>) -> Result<()> {
msg!(
"Called `big_instruction_data` with data length: {}",
data.len()
);
Ok(())
}
}

const VALUE_SEED: &[u8] = b"u8_value";
Expand Down
25 changes: 12 additions & 13 deletions chains/solana/contracts/programs/timelock/src/access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use crate::state::{Config, Role};
/// This is used as an attribute macro with #[access_control] to guard program instructions.
///
/// # Arguments
/// * `$ctx` - The context containing program accounts
/// * `$role` - One or more roles that are allowed to execute the instruction
/// - `$ctx` - The context containing program accounts
/// - `$role` - One or more roles that are allowed to execute the instruction
///
#[macro_export]
macro_rules! require_role_or_admin {
Expand All @@ -28,16 +28,15 @@ macro_rules! require_role_or_admin {
/// Returns Ok(()) if the authority is either the admin or has at least one of the roles.
///
/// # Arguments
/// * `config` - Account containing program configuration including owner
/// * `role_controller` - Account managing role-based access control
/// * `authority` - The signer attempting to execute the instruction
/// * `roles` - Array of roles being checked for authorization
/// - `config` - Account containing program configuration including owner
/// - `role_controller` - Account managing role-based access control
/// - `authority` - The signer attempting to execute the instruction
/// - `roles` - Array of roles being checked for authorization
///
/// # Returns
/// * `Result<()>` - Ok if authorized
/// * `Err(TimelockError::InvalidAccessController)` - If provided controller isn't configured for any roles
/// * `Err(AuthError::Unauthorized)` - If authority lacks admin rights and required roles
/// - `Result<()>` - Ok if authorized
/// - `Err(TimelockError::InvalidAccessController)` - If provided controller isn't configured for any roles
/// - `Err(AuthError::Unauthorized)` - If authority lacks admin rights and required roles
pub fn only_role_or_admin(
config: &Account<Config>,
role_controller: &AccountLoader<AccessController>,
Expand Down Expand Up @@ -80,11 +79,11 @@ macro_rules! require_only_admin {
/// Returns Ok(()) if the authority is the admin, Err otherwise.
///
/// # Arguments
/// * `config` - Account containing program configuration including owner
/// * `authority` - The signer attempting to execute the instruction
/// - `config` - Account containing program configuration including owner
/// - `authority` - The signer attempting to execute the instruction
///
/// # Returns
/// * `Result<()>` - Ok if authorized, Err with AuthError::Unauthorized otherwise
/// - `Result<()>` - Ok if authorized, Err with AuthError::Unauthorized otherwise
pub fn only_admin(config: &Account<Config>, authority: &Signer) -> Result<()> {
require_keys_eq!(authority.key(), config.owner, AuthError::Unauthorized);
Ok(())
Expand Down
1 change: 0 additions & 1 deletion chains/solana/contracts/programs/timelock/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ pub const TIMELOCK_BLOCKED_FUNCITON_SELECTOR_SEED: &[u8] = b"timelock_blocked_fu

/// constants
pub const ANCHOR_DISCRIMINATOR: usize = 8;
pub const DONE_TIMESTAMP: u64 = 1;
pub const EMPTY_PREDECESSOR: [u8; 32] = [0; 32];
pub const TIMELOCK_ID_PADDED: usize = 32; // fixed size timelock id for distinguishing different timelock states
pub const MAX_SELECTORS: usize = 128; // max number of function selectors that can be blocked(arrayvec)
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,15 @@ pub fn execute_batch<'info>(
});
}

// all executed, update the timestamp
// all executed, update the operation state
op.mark_done();

Ok(())
}

/// execute operation(instructions) w/o checking predecessors and readiness
/// bypasser_execute also need the operation to be uploaded formerly
/// NOTE: operation should be closed after execution
/// NOTE: operation is closed after execution
pub fn bypasser_execute_batch<'info>(
ctx: Context<'_, '_, '_, 'info, BypasserExecuteBatch<'info>>,
timelock_id: [u8; TIMELOCK_ID_PADDED],
Expand Down Expand Up @@ -181,8 +181,8 @@ pub struct BypasserExecuteBatch<'info> {
mut,
seeds = [TIMELOCK_BYPASSER_OPERATION_SEED, timelock_id.as_ref(), id.as_ref()],
bump,
constraint = operation.is_finalized @ TimelockError::OperationNotFinalized,
close = authority, // close the bypasser operation after execution
constraint = operation.is_finalized() @ TimelockError::OperationNotFinalized,
close = authority, // close the operation after bypasser execution
)]
pub operation: Account<'info, Operation>,

Expand Down
Loading

0 comments on commit 2b97679

Please sign in to comment.