Skip to main content

data_anchor_blober/state/
checkpoint.rs

1use anchor_lang::{
2    prelude::*,
3    solana_program::{clock::Slot, hash::HASH_BYTES, pubkey::PUBKEY_BYTES},
4};
5
6use crate::{
7    error::ErrorCode, GROTH16_PROOF_SIZE, PROOF_PUBLIC_VALUES_MAX_SIZE, PROOF_VERIFICATION_KEY_SIZE,
8};
9
10#[account]
11#[derive(Debug, InitSpace, PartialEq, Eq, PartialOrd, Ord)]
12pub struct Checkpoint {
13    pub slot: u64,
14    pub proof: [u8; GROTH16_PROOF_SIZE],
15    #[max_len(PROOF_VERIFICATION_KEY_SIZE)]
16    pub verification_key: String,
17    #[max_len(PROOF_PUBLIC_VALUES_MAX_SIZE)]
18    pub public_values: Vec<u8>,
19}
20
21impl Checkpoint {
22    pub fn new(
23        proof: [u8; GROTH16_PROOF_SIZE],
24        public_values: Vec<u8>,
25        verification_key: String,
26        slot: Slot,
27    ) -> Result<Self> {
28        if public_values.len() > PROOF_PUBLIC_VALUES_MAX_SIZE {
29            return Err(error!(ErrorCode::PublicValuesExceedMaxSize));
30        }
31        if verification_key.len() != PROOF_VERIFICATION_KEY_SIZE {
32            return Err(error!(ErrorCode::InvalidVerificationKeySize));
33        }
34
35        Ok(Self {
36            proof,
37            public_values,
38            verification_key,
39            slot,
40        })
41    }
42
43    pub fn is_initial(&self) -> bool {
44        self.slot == 0
45            && self.public_values.is_empty()
46            && self.verification_key.is_empty()
47            && self.proof == [0u8; GROTH16_PROOF_SIZE]
48    }
49
50    pub fn store(&mut self, new: Self) -> Result<()> {
51        if new.public_values.len() > PROOF_PUBLIC_VALUES_MAX_SIZE {
52            return Err(error!(ErrorCode::PublicValuesExceedMaxSize));
53        }
54        if new.verification_key.len() != PROOF_VERIFICATION_KEY_SIZE {
55            return Err(error!(ErrorCode::InvalidVerificationKeySize));
56        }
57
58        self.proof = new.proof;
59        self.public_values = new.public_values;
60        self.verification_key = new.verification_key;
61        self.slot = new.slot;
62        Ok(())
63    }
64
65    #[cfg(feature = "sp1")]
66    pub fn verify_zk_proof(&self) -> Result<()> {
67        sp1_solana::verify_proof(
68            &self.proof,
69            &self.public_values,
70            &self.verification_key,
71            sp1_solana::GROTH16_VK_5_0_0_BYTES,
72        )
73        .map_err(|_| error!(ErrorCode::ProofVerificationFailed))
74    }
75
76    pub fn blober(&self) -> Result<Pubkey> {
77        bincode::deserialize::<Pubkey>(
78            self.public_values
79                .get(..PUBKEY_BYTES)
80                .ok_or_else(|| error!(ErrorCode::InvalidPublicValue))?,
81        )
82        .map_err(|_| error!(ErrorCode::InvalidPublicValue))
83    }
84
85    pub fn initial_hash(&self) -> Result<[u8; HASH_BYTES]> {
86        bincode::deserialize::<[u8; HASH_BYTES]>(
87            self.public_values
88                .get(PUBKEY_BYTES..PUBKEY_BYTES + HASH_BYTES)
89                .ok_or_else(|| error!(ErrorCode::InvalidPublicValue))?,
90        )
91        .map_err(|_| error!(ErrorCode::InvalidPublicValue))
92    }
93
94    pub fn final_hash(&self) -> Result<[u8; HASH_BYTES]> {
95        bincode::deserialize::<[u8; HASH_BYTES]>(
96            self.public_values
97                .get(PUBKEY_BYTES + HASH_BYTES..)
98                .ok_or_else(|| error!(ErrorCode::InvalidPublicValue))?,
99        )
100        .map_err(|_| error!(ErrorCode::InvalidPublicValue))
101    }
102
103    pub fn non_base_commitments(&self) -> Option<&[u8]> {
104        self.public_values.get(PUBKEY_BYTES + HASH_BYTES * 2..)
105    }
106
107    #[cfg(feature = "cpi")]
108    pub fn cpi_create_checkpoint<'info>(
109        &self,
110        blober: Pubkey,
111        data_anchor: AccountInfo<'info>,
112        account_infos: crate::cpi::accounts::CreateCheckpoint<'info>,
113        pda_signer_bump: &[u8],
114    ) -> Result<()> {
115        use crate::{CHECKPOINT_PDA_SIGNER_SEED, CHECKPOINT_SEED, SEED};
116
117        let seeds = &[&[
118            SEED,
119            CHECKPOINT_SEED,
120            CHECKPOINT_PDA_SIGNER_SEED,
121            blober.as_ref(),
122            pda_signer_bump,
123        ][..]];
124
125        let cpi_context = CpiContext::new(data_anchor, account_infos).with_signer(seeds);
126
127        crate::cpi::create_checkpoint(
128            cpi_context,
129            blober,
130            self.proof,
131            self.public_values.clone(),
132            self.verification_key.clone(),
133            self.slot,
134        )
135    }
136}
137
138#[account]
139#[derive(Debug, InitSpace, PartialEq, Eq, PartialOrd, Ord)]
140pub struct CheckpointConfig {
141    pub blober: Pubkey,
142    pub authority: Pubkey,
143}
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148
149    #[test]
150    fn test_checkpoint_serde() {
151        let checkpoint = Checkpoint {
152            slot: 123456,
153            proof: [1; GROTH16_PROOF_SIZE],
154            verification_key: [
155                ["0x"].as_slice(),
156                ["0"; PROOF_VERIFICATION_KEY_SIZE - 2].as_slice(),
157            ]
158            .concat()
159            .join(""),
160            public_values: vec![2; PROOF_PUBLIC_VALUES_MAX_SIZE - 32],
161        };
162
163        let serialized = checkpoint.try_to_vec().unwrap();
164        let deserialized = Checkpoint::try_from_slice(&serialized).unwrap();
165        assert_eq!(checkpoint, deserialized);
166        let slot_bytes = [58, 0, 0, 0, 0, 0, 0, 0];
167        let groth16_bytes = [
168            164, 89, 76, 89, 44, 223, 17, 194, 251, 101, 97, 41, 176, 230, 219, 11, 162, 36, 92,
169            57, 173, 127, 79, 160, 29, 151, 174, 198, 52, 107, 226, 56, 172, 240, 32, 218, 15, 158,
170            106, 75, 63, 123, 94, 65, 249, 31, 115, 83, 252, 159, 13, 220, 48, 93, 244, 134, 12,
171            87, 61, 215, 180, 13, 103, 247, 235, 136, 132, 99, 24, 214, 43, 235, 25, 16, 59, 220,
172            201, 75, 88, 109, 240, 33, 14, 71, 60, 153, 181, 225, 16, 197, 255, 58, 185, 142, 168,
173            235, 138, 162, 253, 62, 11, 218, 213, 145, 139, 92, 213, 124, 214, 7, 218, 184, 146,
174            236, 207, 77, 134, 83, 203, 224, 141, 86, 123, 153, 32, 38, 88, 151, 41, 114, 244, 126,
175            46, 75, 209, 182, 185, 186, 89, 203, 201, 147, 126, 200, 232, 224, 187, 81, 229, 26,
176            211, 192, 143, 255, 37, 155, 243, 94, 93, 202, 187, 237, 216, 39, 3, 82, 175, 113, 181,
177            129, 184, 71, 170, 200, 41, 157, 94, 233, 138, 61, 241, 169, 253, 202, 224, 91, 145,
178            99, 5, 187, 189, 140, 205, 41, 112, 6, 12, 14, 86, 45, 63, 35, 84, 28, 99, 230, 188,
179            235, 149, 19, 16, 91, 241, 74, 136, 170, 215, 222, 108, 129, 108, 64, 83, 154, 71, 200,
180            145, 66, 20, 63, 124, 47, 7, 227, 127, 174, 250, 247, 124, 167, 144, 233, 140, 122,
181            233, 253, 244, 30, 139, 185, 240, 133, 144, 197, 144, 88, 74, 237, 166, 119,
182        ];
183        let verification_key_bytes = [
184            66, 0, 0, 0, // size hint
185            48, 120, 48, 48, 54, 102, 54, 101, 54, 98, 52, 101, 57, 54, 50, 52, 53, 102, 57, 56,
186            101, 48, 52, 55, 98, 49, 55, 99, 99, 50, 98, 100, 52, 98, 101, 55, 56, 102, 98, 48, 50,
187            101, 48, 56, 55, 54, 57, 51, 56, 49, 51, 53, 97, 97, 99, 100, 50, 100, 48, 51, 99, 97,
188            51, 99, 102, 49,
189        ];
190        let public_value_bytes = [
191            96, 0, 0, 0, // size hint
192            3, 145, 232, 95, 237, 197, 86, 36, 133, 7, 130, 192, 44, 20, 165, 56, 142, 241, 131,
193            217, 169, 251, 153, 244, 24, 200, 141, 237, 87, 185, 20, 36, 227, 176, 196, 66, 152,
194            252, 28, 20, 154, 251, 244, 200, 153, 111, 185, 36, 39, 174, 65, 228, 100, 155, 147,
195            76, 164, 149, 153, 27, 120, 82, 184, 85, 235, 11, 55, 168, 251, 31, 209, 12, 182, 200,
196            154, 42, 5, 29, 196, 222, 85, 16, 54, 24, 5, 250, 103, 79, 41, 124, 74, 196, 185, 94,
197            22, 176, 0, 0, 0, 0, 0, 0, 0, 0,
198        ];
199        let example = [
200            &slot_bytes[..],
201            &groth16_bytes[..],
202            &verification_key_bytes[..],
203            &public_value_bytes[..],
204        ]
205        .concat();
206        let example = &mut example.as_slice();
207        let slot = example
208            .get(..8)
209            .and_then(|s| s.try_into().ok())
210            .map(u64::from_le_bytes)
211            .unwrap();
212
213        let groth16 = example.get(8..8 + GROTH16_PROOF_SIZE).unwrap();
214
215        let vk_key_size = example
216            .get(8 + GROTH16_PROOF_SIZE..8 + GROTH16_PROOF_SIZE + 4)
217            .and_then(|s| s.try_into().ok())
218            .map(u32::from_le_bytes)
219            .unwrap() as usize;
220
221        let vk_key_hex = example
222            .get(8 + GROTH16_PROOF_SIZE + 4..8 + GROTH16_PROOF_SIZE + 4 + vk_key_size)
223            .unwrap();
224
225        let public_values_size = example
226            .get(
227                8 + GROTH16_PROOF_SIZE + 4 + vk_key_size
228                    ..8 + GROTH16_PROOF_SIZE + 4 + vk_key_size + 4,
229            )
230            .and_then(|s| s.try_into().ok())
231            .map(u32::from_le_bytes)
232            .unwrap() as usize;
233
234        let public_values = example
235            .get(
236                8 + GROTH16_PROOF_SIZE + 4 + vk_key_size + 4
237                    ..8 + GROTH16_PROOF_SIZE + 4 + vk_key_size + 4 + public_values_size,
238            )
239            .unwrap()
240            .to_vec();
241
242        let _checkpoint = Checkpoint {
243            proof: groth16.try_into().unwrap(),
244            verification_key: String::from_utf8(vk_key_hex.to_vec()).unwrap(),
245            public_values,
246            slot,
247        };
248        let _deserialized = Checkpoint::deserialize(example).unwrap();
249    }
250}