Skip to main content

data_anchor_blober/instructions/
create_checkpoint.rs

1use anchor_lang::{prelude::*, solana_program::clock::Slot};
2
3use crate::{
4    checkpoint::CheckpointConfig, error::ErrorCode, state::checkpoint::Checkpoint,
5    CHECKPOINT_CONFIG_SEED, CHECKPOINT_PDA_SIGNER_SEED, CHECKPOINT_SEED, GROTH16_PROOF_SIZE, SEED,
6};
7
8#[derive(Accounts)]
9#[instruction(blober: Pubkey)]
10pub struct CreateCheckpoint<'info> {
11    #[account(
12        init_if_needed,
13        payer = payer,
14        space = Checkpoint::DISCRIMINATOR.len() + Checkpoint::INIT_SPACE,
15        seeds = [
16            SEED,
17            CHECKPOINT_SEED,
18            blober.as_ref(),
19        ],
20        bump
21    )]
22    pub checkpoint: Account<'info, Checkpoint>,
23
24    #[account(
25        seeds = [
26            SEED,
27            CHECKPOINT_SEED,
28            CHECKPOINT_CONFIG_SEED,
29            blober.as_ref(),
30        ],
31        bump,
32    )]
33    pub checkpoint_config: Account<'info, CheckpointConfig>,
34
35    #[account(
36        mut,
37        seeds = [
38            SEED,
39            CHECKPOINT_SEED,
40            CHECKPOINT_PDA_SIGNER_SEED,
41            blober.as_ref(),
42        ],
43        seeds::program = checkpoint_config.authority,
44        bump,
45    )]
46    pub pda_signer: Signer<'info>,
47
48    #[account(mut)]
49    pub payer: Signer<'info>,
50
51    pub system_program: Program<'info, System>,
52}
53
54pub fn create_checkpoint_handler(
55    ctx: Context<CreateCheckpoint>,
56    blober: Pubkey,
57    proof: [u8; GROTH16_PROOF_SIZE],
58    public_values: Vec<u8>,
59    verification_key: String,
60    slot: Slot,
61) -> Result<()> {
62    let new_checkpoint = Checkpoint::new(proof, public_values, verification_key, slot)?;
63
64    let public_value_blober = new_checkpoint.blober()?;
65
66    if public_value_blober != blober {
67        return Err(error!(ErrorCode::BloberMismatch));
68    }
69
70    if ctx.accounts.checkpoint.slot == 0 {
71        return ctx.accounts.checkpoint.store(new_checkpoint);
72    }
73
74    if public_value_blober != ctx.accounts.checkpoint.blober()? {
75        return Err(error!(ErrorCode::BloberMismatch));
76    }
77
78    if ctx.accounts.checkpoint.slot >= slot {
79        return Err(error!(ErrorCode::SlotTooLow));
80    }
81
82    if new_checkpoint.initial_hash()? != ctx.accounts.checkpoint.final_hash()? {
83        return Err(error!(ErrorCode::ProofHashMismatch));
84    }
85
86    ctx.accounts.checkpoint.store(new_checkpoint)
87}
88
89#[cfg(all(test, feature = "sp1"))]
90mod tests {
91    use anchor_lang::{
92        prelude::{AccountMeta, Pubkey},
93        ToAccountMetas,
94    };
95
96    use crate::accounts::CreateCheckpoint;
97
98    #[test]
99    fn test_first_account_is_the_checkpoint() {
100        let checkpoint = Pubkey::new_unique();
101        let checkpoint_config = Pubkey::new_unique();
102        let pda_signer = Pubkey::new_unique();
103        let payer = Pubkey::new_unique();
104        let system_program = Pubkey::new_unique();
105
106        let account = CreateCheckpoint {
107            checkpoint,
108            checkpoint_config,
109            payer,
110            pda_signer,
111            system_program,
112        };
113
114        let expected = AccountMeta {
115            pubkey: checkpoint,
116            is_signer: false,
117            is_writable: true,
118        };
119
120        let is_signer = None;
121        let actual = &account.to_account_metas(is_signer)[0];
122        assert_eq!(actual, &expected);
123    }
124}