Skip to content

Commit

Permalink
Authority timelock (#3)
Browse files Browse the repository at this point in the history
* Authority timelock

* Cleanup

* Clippy

* Comment

* Renames
  • Loading branch information
guibescos authored Nov 8, 2023
1 parent 05713de commit 9693677
Show file tree
Hide file tree
Showing 7 changed files with 564 additions and 0 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions programs/program-authority-timelock/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[package]
name = "program-authority-timelock"
version = "1.0.0"
description = "Created with Anchor"
edition = "2021"

[lib]
crate-type = ["cdylib", "lib"]
name = "program_authority_timelock"

[features]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
cpi = ["no-entrypoint"]
default = []

[dependencies]
anchor-lang = "0.26.0"

[dev-dependencies]
solana-program-test = "=1.14.7"
solana-sdk = "=1.14.7"
tokio = "1.14.1"
bincode = "1.3.3"
119 changes: 119 additions & 0 deletions programs/program-authority-timelock/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#![deny(warnings)]
#![allow(clippy::result_large_err)]

use anchor_lang::{
prelude::*,
solana_program::{
bpf_loader_upgradeable,
program::{
invoke,
invoke_signed,
},
},
};

#[cfg(test)]
mod tests;

declare_id!("escMHe7kSqPcDHx4HU44rAHhgdTLBZkUrU39aN8kMcL");
const ONE_YEAR: i64 = 365 * 24 * 60 * 60;

#[program]
pub mod program_authority_timelock {
use super::*;

pub fn commit(ctx: Context<Commit>, timestamp: i64) -> Result<()> {
let current_authority = &ctx.accounts.current_authority;
let escrow_authority = &ctx.accounts.escrow_authority;
let program_account = &ctx.accounts.program_account;

invoke(
&bpf_loader_upgradeable::set_upgrade_authority(
&program_account.key(),
&current_authority.key(),
Some(&escrow_authority.key()),
),
&ctx.accounts.to_account_infos(),
)?;

// Check that the timelock is no longer than 1 year
if Clock::get()?.unix_timestamp.saturating_add(ONE_YEAR) < timestamp {
return Err(ErrorCode::TimestampTooLate.into());
}

Ok(())
}

pub fn transfer(ctx: Context<Transfer>, timestamp: i64) -> Result<()> {
let new_authority = &ctx.accounts.new_authority;
let escrow_authority = &ctx.accounts.escrow_authority;
let program_account = &ctx.accounts.program_account;

invoke_signed(
&bpf_loader_upgradeable::set_upgrade_authority(
&program_account.key(),
&escrow_authority.key(),
Some(&new_authority.key()),
),
&ctx.accounts.to_account_infos(),
&[&[
new_authority.key().as_ref(),
timestamp.to_be_bytes().as_ref(),
&[*ctx.bumps.get("escrow_authority").unwrap()],
]],
)?;

if Clock::get()?.unix_timestamp < timestamp {
return Err(ErrorCode::TimestampTooEarly.into());
}

Ok(())
}
}

#[derive(Accounts)]
#[instruction(timestamp : i64)]
pub struct Commit<'info> {
pub current_authority: Signer<'info>,
/// CHECK: Unchecked new authority, can be a native wallet or a PDA of another program
pub new_authority: AccountInfo<'info>,
#[account(seeds = [new_authority.key().as_ref(), timestamp.to_be_bytes().as_ref()], bump)]
pub escrow_authority: SystemAccount<'info>,
#[account(executable, constraint = matches!(program_account.as_ref(), UpgradeableLoaderState::Program{..}))]
pub program_account: Account<'info, UpgradeableLoaderState>,
#[account(mut, seeds = [program_account.key().as_ref()], bump, seeds::program = bpf_upgradable_loader.key())]
pub program_data: Account<'info, ProgramData>,
pub bpf_upgradable_loader: Program<'info, BpfUpgradableLoader>,
}

#[derive(Accounts)]
#[instruction(timestamp : i64)]
pub struct Transfer<'info> {
/// CHECK: Unchecked new authority, can be a native wallet or a PDA of another program
pub new_authority: AccountInfo<'info>,
#[account(seeds = [new_authority.key().as_ref(), timestamp.to_be_bytes().as_ref()], bump)]
pub escrow_authority: SystemAccount<'info>,
#[account(executable, constraint = matches!(program_account.as_ref(), UpgradeableLoaderState::Program{..}))]
pub program_account: Account<'info, UpgradeableLoaderState>,
#[account(mut, seeds = [program_account.key().as_ref()], bump, seeds::program = bpf_upgradable_loader.key())]
pub program_data: Account<'info, ProgramData>,
pub bpf_upgradable_loader: Program<'info, BpfUpgradableLoader>,
}

#[derive(Clone)]
pub struct BpfUpgradableLoader {}

impl Id for BpfUpgradableLoader {
fn id() -> Pubkey {
bpf_loader_upgradeable::id()
}
}

#[error_code]
#[derive(PartialEq, Eq)]
pub enum ErrorCode {
#[msg("Timestamp too early")]
TimestampTooEarly,
#[msg("Timestamp too late")]
TimestampTooLate,
}
2 changes: 2 additions & 0 deletions programs/program-authority-timelock/src/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mod simulator;
mod test;
Loading

0 comments on commit 9693677

Please sign in to comment.