use lens::WhirParams;
use super::codec::{Reader, Writer};
use super::machine::{read_statement, statement};
use super::{Envelope, EnvelopeError as E};
use crate::execution::ExecutionNoun;
use crate::execution::state_evidence::StateEvidence;
use crate::machine::{self, MachinePublic, MachineStatement};
use crate::recursion::ivc::{self, IvcProof};
pub const RECURSIVE_FORMAT: u8 = 3;
pub const STEP_LOG_ROWS: u32 = 15;
pub const MAX_PROOF_BYTES: usize = 1 << 20;
pub const ADMITTED: [(u8, u32); 2] = [(4, STEP_LOG_ROWS), (6, STEP_LOG_ROWS)];
pub fn params() -> WhirParams {
params_at(4)
}
fn params_at(log_inv_rate: u8) -> WhirParams {
let mut w = crate::execution::succinct::params_for(20);
w.log_inv_rate = log_inv_rate;
w.pow_bits = 24;
w
}
pub fn admitted(whir: &WhirParams, log_rows: u32) -> bool {
ADMITTED
.iter()
.any(|&(rate, n)| n == log_rows && params_at(rate).header() == whir.header())
}
pub const KEY_ROOTS: [([u8; 8], [u64; 4]); 3] = [
(
[1, 4, 4, 24, 128, 0, 1, 8],
[
4491905516725670020,
3328524470941199224,
7822895334886079753,
284418518571696544,
],
),
(
[1, 6, 4, 24, 128, 0, 1, 8],
[
12054528635242799571,
12665011618142561561,
5178125058008943233,
14871959739192700421,
],
),
(
[1, 3, 4, 16, 128, 0, 1, 8],
[
14487870178850513477,
16787388207891397930,
14712683618482113395,
17511682741490682146,
],
),
];
pub fn pinned_key(
whir: &WhirParams,
k: std::sync::Arc<ivc::Key>,
) -> Result<std::sync::Arc<ivc::Key>, String> {
let want = KEY_ROOTS
.iter()
.find(|(h, _)| *h == whir.header())
.map(|(_, r)| *r);
match want {
Some(r) if k.params.n == STEP_LOG_ROWS as usize && k.kw_root.map(|x| x.as_u64()) == r => Ok(k),
Some(_) => Err("recursive envelope: the derived IVC key's words are not the pinned ones (prover backend?)".into()),
None => Err("recursive envelope: no pinned IVC key for these parameters".into()),
}
}
pub fn key(whir: &WhirParams, log_rows: u32) -> Result<std::sync::Arc<ivc::Key>, String> {
if !admitted(whir, log_rows) {
return Err("recursive envelope: parameters not admitted".into());
}
pinned_key(whir, ivc::key(whir, log_rows as usize)?)
}
pub fn prove(
program: &ExecutionNoun,
input: &[u64],
budget: u64,
whir: &WhirParams,
) -> Result<Envelope, String> {
key(whir, STEP_LOG_ROWS)?;
let run = machine::execute_exact(program, input, budget, STEP_LOG_ROWS)
.map_err(|e| format!("machine: {e:?}"))?;
let proof = ivc::prove_run(&run, whir)?;
Ok(Envelope::Recursive {
params: *whir,
statement: run.statement.public(),
proof: Box::new(proof),
})
}
pub(super) fn verify(
whir: &WhirParams,
st: &MachineStatement,
proof: &IvcProof,
state: Option<&StateEvidence>,
) -> Result<(), String> {
machine::authenticate_state(st, state)?;
key(whir, proof.log_rows)?;
ivc::verify(st, proof, whir)
}
pub fn proof_bytes(whir: &WhirParams, proof: &IvcProof) -> Result<Vec<u8>, String> {
Ok(proof.to_bytes(&*key(whir, proof.log_rows)?))
}
pub(super) fn encode(params: &WhirParams, st: &MachinePublic, proof: &IvcProof, w: &mut Writer) {
let key = ivc::key(params, proof.log_rows as usize)
.expect("a recursion key for the envelope's parameters");
let bytes = proof.to_bytes(&key);
w.raw(&[RECURSIVE_FORMAT]);
w.raw(¶ms.header());
statement(st, w);
w.len(bytes.len());
w.raw(&bytes);
}
pub(super) fn decode(r: &mut Reader, expect: Option<&[u8; 32]>) -> Result<Envelope, E> {
if r.byte()? != RECURSIVE_FORMAT {
return Err(E::NonCanonical);
}
let header = r.raw(8)?;
let params = ADMITTED
.iter()
.map(|&(rate, _)| params_at(rate))
.find(|p| p.header()[..] == *header)
.ok_or(E::NonCanonical)?;
let statement = read_statement(r, expect)?;
let n = r.len(MAX_PROOF_BYTES, 1)?;
let bytes = r.raw(n)?;
let log_rows = u32::from(*bytes.first().ok_or(E::Truncated)?);
if !admitted(¶ms, log_rows) {
return Err(E::NonCanonical);
}
let proof = IvcProof::from_bytes(bytes, |lr| key(¶ms, lr)).map_err(|_| E::NonCanonical)?;
Ok(Envelope::Recursive {
params,
statement,
proof: Box::new(proof),
})
}