openvm_static_verifier/
codec.rs

1use std::io::{self, Read, Write};
2
3use halo2_base::{
4    gates::circuit::builder::BaseCircuitBuilder,
5    halo2_proofs::{
6        halo2curves::bn256::{Fr, G1Affine},
7        plonk::ProvingKey,
8        SerdeFormat,
9    },
10};
11use openvm_stark_sdk::openvm_stark_backend::codec::{Decode, Encode};
12use serde::{de::DeserializeOwned, Serialize};
13
14use crate::{
15    keygen::StaticVerifierProvingKey,
16    prover::{Halo2ProvingMetadata, Halo2ProvingPinning},
17    wrapper::Halo2WrapperProvingKey,
18};
19
20// The default graph program is currently about 143 MiB when serialized as JSON.
21const MAX_JSON_SECTION_LEN: usize = 256 * 1024 * 1024;
22
23impl Encode for StaticVerifierProvingKey {
24    fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()> {
25        write_json_section(writer, &(&self.circuit, &self.shape))?;
26        write_json_section(writer, &self.graph_program)?;
27        self.pinning.encode(writer)
28    }
29}
30
31impl Decode for StaticVerifierProvingKey {
32    fn decode<R: Read>(reader: &mut R) -> io::Result<Self> {
33        let (circuit, shape) = read_json_section(reader)?;
34        let graph_program = read_json_section(reader)?;
35        let pinning = Halo2ProvingPinning::decode(reader)?;
36        Ok(Self {
37            circuit,
38            pinning,
39            shape,
40            graph_program,
41        })
42    }
43}
44
45impl Encode for Halo2WrapperProvingKey {
46    fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()> {
47        self.pinning.encode(writer)
48    }
49}
50
51impl Decode for Halo2WrapperProvingKey {
52    fn decode<R: Read>(reader: &mut R) -> io::Result<Self> {
53        Ok(Self {
54            pinning: Halo2ProvingPinning::decode(reader)?,
55        })
56    }
57}
58
59impl Encode for Halo2ProvingPinning {
60    fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()> {
61        write_json_section(writer, &self.metadata)?;
62        self.pk.write(writer, SerdeFormat::RawBytes)
63    }
64}
65
66impl Decode for Halo2ProvingPinning {
67    fn decode<R: Read>(reader: &mut R) -> io::Result<Self> {
68        let metadata: Halo2ProvingMetadata = read_json_section(reader)?;
69        let pk = ProvingKey::<G1Affine>::read::<_, BaseCircuitBuilder<Fr>>(
70            reader,
71            SerdeFormat::RawBytes,
72            metadata.config_params.clone(),
73        )?;
74        Ok(Self { pk, metadata })
75    }
76}
77
78// Each JSON section is length-prefixed because it is followed by raw Halo2 proving-key bytes.
79fn write_json_section<W: Write, T: Serialize>(writer: &mut W, value: &T) -> io::Result<()> {
80    let bytes = serde_json::to_vec(value).map_err(io::Error::other)?;
81    writer.write_all(&(bytes.len() as u64).to_le_bytes())?;
82    writer.write_all(&bytes)
83}
84
85fn read_json_section<R: Read, T: DeserializeOwned>(reader: &mut R) -> io::Result<T> {
86    let mut len_bytes = [0u8; 8];
87    reader.read_exact(&mut len_bytes)?;
88    let len = usize::try_from(u64::from_le_bytes(len_bytes)).map_err(|_| {
89        io::Error::new(
90            io::ErrorKind::InvalidData,
91            "JSON section length overflows usize",
92        )
93    })?;
94    if len > MAX_JSON_SECTION_LEN {
95        return Err(io::Error::new(
96            io::ErrorKind::InvalidData,
97            "JSON section is too large",
98        ));
99    }
100    let mut bytes = vec![0u8; len];
101    reader.read_exact(&mut bytes)?;
102    serde_json::from_slice(&bytes).map_err(io::Error::other)
103}