use crate::traits::PairingEngine;
use ff::PrimeField;
#[derive(Debug, Clone)]
pub struct SerializedVk {
pub alpha: (String, String),
pub beta: (String, String, String, String),
pub gamma: (String, String, String, String),
pub delta: (String, String, String, String),
pub tau_g2: (String, String, String, String),
pub ic: Vec<(String, String)>,
pub num_public_inputs: usize,
pub domain_size: usize,
pub omega: String,
}
pub fn serialize_g1<E: PairingEngine>(point: &E::G1Affine) -> (String, String) {
use group::GroupEncoding;
let bytes = point.to_bytes();
let bytes_ref: &[u8] = bytes.as_ref();
if bytes_ref.len() >= 32 {
let x_bytes = &bytes_ref[..32.min(bytes_ref.len())];
let mut x_padded = [0u8; 32];
x_padded[..x_bytes.len()].copy_from_slice(x_bytes);
let y_padded = [0u8; 32];
(
format!("0x{}", hex::encode(x_padded)),
format!("0x{}", hex::encode(y_padded)),
)
} else {
(
format!("0x{}", hex::encode(bytes_ref)),
format!("0x{}", hex::encode([0u8; 32])),
)
}
}
pub fn serialize_scalar<F: PrimeField>(scalar: &F) -> String {
let repr = scalar.to_repr();
format!("0x{}", hex::encode(repr.as_ref()))
}
#[derive(Debug, Clone)]
pub struct EncodedProof {
pub calldata: Vec<u8>,
pub hex: String,
}
impl EncodedProof {
pub fn from_bytes(bytes: Vec<u8>) -> Self {
let hex = format!("0x{}", hex::encode(&bytes));
Self {
calldata: bytes,
hex,
}
}
pub fn len(&self) -> usize {
self.calldata.len()
}
pub fn is_empty(&self) -> bool {
self.calldata.is_empty()
}
}
pub fn encode_public_inputs<F: PrimeField>(inputs: &[F]) -> Vec<[u8; 32]> {
inputs
.iter()
.map(|f| {
let repr = f.to_repr();
let mut bytes = [0u8; 32];
bytes.copy_from_slice(repr.as_ref());
bytes
})
.collect()
}
pub fn decode_public_inputs<F: PrimeField>(inputs: &[[u8; 32]]) -> Result<Vec<F>, String> {
inputs
.iter()
.map(|bytes| {
let mut repr = F::Repr::default();
repr.as_mut().copy_from_slice(bytes);
F::from_repr(repr)
.into_option()
.ok_or_else(|| "Invalid field element".to_string())
})
.collect()
}
pub fn generate_vk_constants(vk: &SerializedVk) -> String {
let mut lines = Vec::new();
lines.push(format!(
" uint256 constant VK_DOMAIN_SIZE = {};",
vk.domain_size
));
lines.push(format!(" uint256 constant VK_OMEGA = {};", vk.omega));
lines.push(format!(" uint256 constant VK_ALPHA_X = {};", vk.alpha.0));
lines.push(format!(" uint256 constant VK_ALPHA_Y = {};", vk.alpha.1));
lines.push(format!(" uint256 constant VK_BETA_X1 = {};", vk.beta.0));
lines.push(format!(" uint256 constant VK_BETA_X2 = {};", vk.beta.1));
lines.push(format!(" uint256 constant VK_BETA_Y1 = {};", vk.beta.2));
lines.push(format!(" uint256 constant VK_BETA_Y2 = {};", vk.beta.3));
lines.push(format!(
" uint256 constant VK_GAMMA_X1 = {};",
vk.gamma.0
));
lines.push(format!(
" uint256 constant VK_GAMMA_X2 = {};",
vk.gamma.1
));
lines.push(format!(
" uint256 constant VK_GAMMA_Y1 = {};",
vk.gamma.2
));
lines.push(format!(
" uint256 constant VK_GAMMA_Y2 = {};",
vk.gamma.3
));
lines.push(format!(
" uint256 constant VK_DELTA_X1 = {};",
vk.delta.0
));
lines.push(format!(
" uint256 constant VK_DELTA_X2 = {};",
vk.delta.1
));
lines.push(format!(
" uint256 constant VK_DELTA_Y1 = {};",
vk.delta.2
));
lines.push(format!(
" uint256 constant VK_DELTA_Y2 = {};",
vk.delta.3
));
lines.push(format!(
" uint256 constant VK_TAU_G2_X1 = {};",
vk.tau_g2.0
));
lines.push(format!(
" uint256 constant VK_TAU_G2_X2 = {};",
vk.tau_g2.1
));
lines.push(format!(
" uint256 constant VK_TAU_G2_Y1 = {};",
vk.tau_g2.2
));
lines.push(format!(
" uint256 constant VK_TAU_G2_Y2 = {};",
vk.tau_g2.3
));
for (i, (x, y)) in vk.ic.iter().enumerate() {
lines.push(format!(" uint256 constant VK_IC_{}_X = {};", i, x));
lines.push(format!(" uint256 constant VK_IC_{}_Y = {};", i, y));
}
lines.join("\n")
}
#[cfg(test)]
mod tests {
use super::*;
use halo2curves::bn256::Fr;
#[test]
fn serialize_scalar_produces_hex() {
let scalar = Fr::from(42u64);
let hex = serialize_scalar(&scalar);
assert!(hex.starts_with("0x"));
assert_eq!(hex.len(), 66); }
#[test]
fn encode_decode_public_inputs_roundtrip() {
let inputs = vec![Fr::from(100u64), Fr::from(200u64)];
let encoded = encode_public_inputs(&inputs);
let decoded: Vec<Fr> = decode_public_inputs(&encoded).unwrap();
assert_eq!(inputs, decoded);
}
#[test]
fn encoded_proof_from_bytes() {
let bytes = vec![1, 2, 3, 4, 5];
let proof = EncodedProof::from_bytes(bytes.clone());
assert_eq!(proof.len(), 5);
assert_eq!(proof.hex, "0x0102030405");
}
#[test]
fn generate_vk_constants_format() {
let vk = SerializedVk {
alpha: ("0x1".to_string(), "0x2".to_string()),
beta: (
"0x3".to_string(),
"0x4".to_string(),
"0x5".to_string(),
"0x6".to_string(),
),
gamma: (
"0x7".to_string(),
"0x8".to_string(),
"0x9".to_string(),
"0xa".to_string(),
),
delta: (
"0xb".to_string(),
"0xc".to_string(),
"0xd".to_string(),
"0xe".to_string(),
),
tau_g2: (
"0xf".to_string(),
"0x10".to_string(),
"0x11".to_string(),
"0x12".to_string(),
),
ic: vec![("0xf".to_string(), "0x10".to_string())],
num_public_inputs: 1,
domain_size: 1024,
omega: "0xabc".to_string(),
};
let constants = generate_vk_constants(&vk);
assert!(constants.contains("VK_DOMAIN_SIZE = 1024"));
assert!(constants.contains("VK_OMEGA = 0xabc"));
assert!(constants.contains("VK_ALPHA_X"));
assert!(constants.contains("VK_BETA_X1"));
assert!(constants.contains("VK_IC_0_X"));
}
}