openvm_sdk/
commit.rs

1use std::{array::from_fn, sync::Arc};
2
3use num_bigint::BigUint;
4use openvm_circuit::arch::{instructions::exe::VmExe, MemoryConfig};
5pub use openvm_circuit::system::program::trace::VmCommittedExe;
6use openvm_native_compiler::ir::DIGEST_SIZE;
7use openvm_stark_backend::{
8    config::{Com, StarkGenericConfig, Val},
9    engine::StarkEngine,
10    p3_field::PrimeField32,
11};
12use openvm_stark_sdk::{
13    config::{baby_bear_poseidon2::BabyBearPoseidon2Engine, FriParameters},
14    engine::StarkFriEngine,
15    openvm_stark_backend::p3_field::FieldAlgebra,
16    p3_baby_bear::BabyBear,
17    p3_bn254_fr::Bn254Fr,
18};
19use serde::{Deserialize, Serialize};
20use serde_with::serde_as;
21
22use crate::{types::BN254_BYTES, F, SC};
23
24/// Wrapper for an array of big-endian bytes, representing an unsigned big integer. Each commit can
25/// be converted to a Bn254Fr using the trivial identification as natural numbers or into a `u32`
26/// digest by decomposing the big integer base-`F::MODULUS`.
27#[serde_as]
28#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
29pub struct CommitBytes(#[serde_as(as = "serde_with::hex::Hex")] [u8; BN254_BYTES]);
30
31impl CommitBytes {
32    pub fn new(bytes: [u8; BN254_BYTES]) -> Self {
33        Self(bytes)
34    }
35
36    pub fn as_slice(&self) -> &[u8; BN254_BYTES] {
37        &self.0
38    }
39
40    pub fn to_bn254(&self) -> Bn254Fr {
41        bytes_to_bn254(&self.0)
42    }
43
44    pub fn to_u32_digest(&self) -> [u32; DIGEST_SIZE] {
45        bytes_to_u32_digest(&self.0)
46    }
47
48    pub fn from_bn254(bn254: Bn254Fr) -> Self {
49        Self(bn254_to_bytes(bn254))
50    }
51
52    pub fn from_u32_digest(digest: &[u32; DIGEST_SIZE]) -> Self {
53        Self(u32_digest_to_bytes(digest))
54    }
55
56    pub fn reverse(&mut self) {
57        self.0.reverse();
58    }
59}
60
61impl std::fmt::Display for CommitBytes {
62    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63        write!(f, "{}", hex::encode(self.0))
64    }
65}
66
67/// `AppExecutionCommit` has all the commitments users should check against the final proof.
68#[serde_as]
69#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
70pub struct AppExecutionCommit {
71    /// Commitment of the executable. In base-F::MODULUS, it's computed as
72    /// compress(
73    ///     compress(
74    ///         hash(app_program_commit),
75    ///         hash(init_memory_commit)
76    ///     ),
77    ///     hash(right_pad(pc_start, 0))
78    /// )
79    /// `right_pad` example, if pc_start = 123, right_pad(pc_start, 0) = \[123,0,0,0,0,0,0,0\]
80    pub app_exe_commit: CommitBytes,
81
82    /// Commitment of the leaf VM verifier program which commits the VmConfig of App VM.
83    // Internal verifier will verify `app_vm_commit`.
84    // Internally this is also known as `leaf_verifier_program_commit`.
85    pub app_vm_commit: CommitBytes,
86}
87
88impl AppExecutionCommit {
89    /// Users should use this function to compute `AppExecutionCommit` and check it against the
90    /// final proof.
91    pub fn compute<SC: StarkGenericConfig>(
92        app_memory_config: &MemoryConfig,
93        app_exe: &VmExe<Val<SC>>,
94        app_program_commit: Com<SC>,
95        leaf_verifier_program_commit: Com<SC>,
96    ) -> Self
97    where
98        Com<SC>: AsRef<[Val<SC>; DIGEST_SIZE]>
99            + From<[Val<SC>; DIGEST_SIZE]>
100            + Into<[Val<SC>; DIGEST_SIZE]>,
101        Val<SC>: PrimeField32,
102    {
103        let exe_commit: [Val<SC>; DIGEST_SIZE] = VmCommittedExe::<SC>::compute_exe_commit(
104            &app_program_commit,
105            app_exe,
106            app_memory_config,
107        )
108        .into();
109        let vm_commit: [Val<SC>; DIGEST_SIZE] = leaf_verifier_program_commit.into();
110        Self::from_field_commit(exe_commit, vm_commit)
111    }
112
113    pub fn from_field_commit<F: PrimeField32>(
114        exe_commit: [F; DIGEST_SIZE],
115        vm_commit: [F; DIGEST_SIZE],
116    ) -> Self {
117        Self {
118            app_exe_commit: CommitBytes::from_u32_digest(&exe_commit.map(|x| x.as_canonical_u32())),
119            app_vm_commit: CommitBytes::from_u32_digest(&vm_commit.map(|x| x.as_canonical_u32())),
120        }
121    }
122}
123
124pub fn commit_app_exe(
125    app_fri_params: FriParameters,
126    app_exe: impl Into<VmExe<F>>,
127) -> Arc<VmCommittedExe<SC>> {
128    let exe: VmExe<_> = app_exe.into();
129    let app_engine = BabyBearPoseidon2Engine::new(app_fri_params);
130    Arc::new(VmCommittedExe::<SC>::commit(exe, app_engine.config().pcs()))
131}
132
133pub(crate) fn babybear_digest_to_bn254(digest: &[F; DIGEST_SIZE]) -> Bn254Fr {
134    let mut ret = Bn254Fr::ZERO;
135    let order = Bn254Fr::from_canonical_u32(BabyBear::ORDER_U32);
136    let mut base = Bn254Fr::ONE;
137    digest.iter().for_each(|&x| {
138        ret += base * Bn254Fr::from_canonical_u32(x.as_canonical_u32());
139        base *= order;
140    });
141    ret
142}
143
144fn bytes_to_bn254(bytes: &[u8; BN254_BYTES]) -> Bn254Fr {
145    let order = Bn254Fr::from_canonical_u32(1 << 8);
146    let mut ret = Bn254Fr::ZERO;
147    let mut base = Bn254Fr::ONE;
148    for byte in bytes.iter().rev() {
149        ret += base * Bn254Fr::from_canonical_u8(*byte);
150        base *= order;
151    }
152    ret
153}
154
155fn bn254_to_bytes(bn254: Bn254Fr) -> [u8; BN254_BYTES] {
156    let mut ret = bn254.value.to_bytes();
157    ret.reverse();
158    ret
159}
160
161fn bytes_to_u32_digest(bytes: &[u8; BN254_BYTES]) -> [u32; DIGEST_SIZE] {
162    let mut bigint = BigUint::ZERO;
163    for byte in bytes.iter() {
164        bigint <<= 8;
165        bigint += BigUint::from(*byte);
166    }
167    let order = BabyBear::ORDER_U32;
168    from_fn(|_| {
169        let bigint_digit = bigint.clone() % order;
170        let digit = if bigint_digit == BigUint::ZERO {
171            0u32
172        } else {
173            bigint_digit.to_u32_digits()[0]
174        };
175        bigint /= order;
176        digit
177    })
178}
179
180fn u32_digest_to_bytes(digest: &[u32; DIGEST_SIZE]) -> [u8; BN254_BYTES] {
181    bn254_to_bytes(babybear_digest_to_bn254(&digest.map(F::from_canonical_u32)))
182}