solana_zk_sdk/zk_elgamal_proof_program/
state.rs

1use {
2    crate::zk_elgamal_proof_program::proof_data::{pod::PodProofType, ProofType},
3    bytemuck::{bytes_of, Pod, Zeroable},
4    num_traits::ToPrimitive,
5    solana_instruction::error::{InstructionError, InstructionError::InvalidAccountData},
6    solana_pubkey::Pubkey,
7    std::mem::size_of,
8};
9
10/// The on-chain state for a verified zero-knowledge proof statement.
11///
12/// In a zero-knowledge proof system, there is a distinction between a **proof** and a
13/// **statement**.
14/// - The **statement** consists of the public values that a proof is certifying. For example, in a
15///   `VerifyZeroCiphertext` instruction, the statement is the ElGamal ciphertext itself.
16/// - The **proof** is the cryptographic data that demonstrates the statement's validity without
17///   revealing any secret information.
18///
19/// A proof is ephemeral and is discarded after it is successfully verified by a proof
20/// instruction. However, the instruction can optionally store the verified public statement
21/// on-chain in a dedicated account. The `ProofContextState` struct defines the layout of this
22/// account.
23///
24/// Storing the statement on-chain acts as a verifiable receipt or certificate that a specific
25/// proof was successfully processed. This state can then be referenced by other on-chain programs.
26#[derive(Clone, Copy, Debug, PartialEq)]
27#[repr(C)]
28pub struct ProofContextState<T: Pod> {
29    /// The proof context authority that can close the account
30    pub context_state_authority: Pubkey,
31    /// The proof type for the context data
32    pub proof_type: PodProofType,
33    /// The proof context data
34    pub proof_context: T,
35}
36
37// `bytemuck::Pod` cannot be derived for generic structs unless the struct is marked
38// `repr(packed)`, which may cause unnecessary complications when referencing its fields. Directly
39// mark `ProofContextState` as `Zeroable` and `Pod` since none of its fields has an alignment
40// requirement greater than 1 and therefore, guaranteed to be `packed`.
41unsafe impl<T: Pod> Zeroable for ProofContextState<T> {}
42unsafe impl<T: Pod> Pod for ProofContextState<T> {}
43
44impl<T: Pod> ProofContextState<T> {
45    pub fn encode(
46        context_state_authority: &Pubkey,
47        proof_type: ProofType,
48        proof_context: &T,
49    ) -> Vec<u8> {
50        let mut buf = Vec::with_capacity(size_of::<Self>());
51        buf.extend_from_slice(context_state_authority.as_ref());
52        buf.push(ToPrimitive::to_u8(&proof_type).unwrap());
53        buf.extend_from_slice(bytes_of(proof_context));
54        buf
55    }
56
57    /// Interpret a slice as a `ProofContextState`.
58    ///
59    /// This function requires a generic parameter. To access only the generic-independent fields
60    /// in `ProofContextState` without a generic parameter, use
61    /// `ProofContextStateMeta::try_from_bytes` instead.
62    pub fn try_from_bytes(input: &[u8]) -> Result<&Self, InstructionError> {
63        bytemuck::try_from_bytes(input).map_err(|_| InvalidAccountData)
64    }
65}
66
67/// The `ProofContextState` without the proof context itself. This struct exists to facilitate the
68/// decoding of generic-independent fields in `ProofContextState`.
69#[derive(Clone, Copy, Debug, PartialEq, bytemuck_derive::Pod, bytemuck_derive::Zeroable)]
70#[repr(C)]
71pub struct ProofContextStateMeta {
72    /// The proof context authority that can close the account
73    pub context_state_authority: Pubkey,
74    /// The proof type for the context data
75    pub proof_type: PodProofType,
76}
77
78impl ProofContextStateMeta {
79    pub fn try_from_bytes(input: &[u8]) -> Result<&Self, InstructionError> {
80        input
81            .get(..size_of::<ProofContextStateMeta>())
82            .and_then(|data| bytemuck::try_from_bytes(data).ok())
83            .ok_or(InvalidAccountData)
84    }
85}