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