use std::sync::{Arc, Mutex, OnceLock};
use lens::WhirParams;
use nebu::Goldilocks;
use super::codec::{Reader, Writer};
use super::machine::{read_statement, statement};
use super::recursive::{self, STEP_LOG_ROWS};
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;
use crate::recursion::word::Digest;
use crate::recursion::wrap::{self, FinalProof, Inner, Mode, WrapKey, WrapParams};
pub const WRAPPED_FORMAT: u8 = 6;
pub const MAX_PROOF_BYTES: usize = wrap::MAX_FINAL_BYTES;
pub const LEVELS: usize = 3;
pub fn chain() -> (WhirParams, [WrapParams; LEVELS]) {
let mut ivc = recursive::params();
ivc.log_inv_rate = ivc::SHIPPED_RATE;
ivc.pow_bits = ivc::SHIPPED_POW;
let level = |rate: u8, pow: u8, mode: Mode| {
let mut whir = ivc;
whir.log_inv_rate = rate;
whir.pow_bits = pow;
WrapParams { whir, n: 0, mode }
};
(
ivc,
[
level(4, 16, Mode::Inner),
level(7, 22, Mode::Inner),
level(9, 26, Mode::Final),
],
)
}
pub fn chain_bytes() -> Vec<u8> {
let (ivc, levels) = chain();
let mut out = ivc.header().to_vec();
out.push(LEVELS as u8);
for l in levels {
out.extend_from_slice(&l.whir.header());
out.push(u8::from(l.mode == Mode::Final));
}
out
}
pub const INNER_ROOTS: [[u64; 4]; LEVELS - 1] = [
[
5540538452882874538,
11187504229086026122,
4829481360188449553,
13504963134371504296,
],
[
12648886311615323299,
12421038178371526965,
9103158066756740675,
1252468810972271725,
],
];
fn pinned_root(i: usize) -> Digest {
INNER_ROOTS[i].map(Goldilocks::new)
}
fn final_slot() -> &'static Mutex<Option<Arc<WrapKey>>> {
static K: OnceLock<Mutex<Option<Arc<WrapKey>>>> = OnceLock::new();
K.get_or_init( Mutex::new(None))
}
pub fn chain_keys() -> Result<Arc<Vec<Arc<WrapKey>>>, String> {
type Keys = Arc<Vec<Arc<WrapKey>>>;
static C: OnceLock<Mutex<Option<Keys>>> = OnceLock::new();
let slot = C.get_or_init( Mutex::new(None));
let mut g = slot.lock().expect("wrap keys");
if let Some(c) = g.as_ref() {
return Ok(c.clone());
}
let (ivc, levels) = chain();
let ikey = ivc::key(&ivc, STEP_LOG_ROWS as usize)?;
let mut keys: Vec<Arc<WrapKey>> = Vec::with_capacity(LEVELS);
for p in levels {
let k = match keys.last() {
None => wrap::derive_key_ivc(p, &ikey)?,
Some(prev) => wrap::derive_key_wrap(p, prev)?,
};
keys.push(Arc::new(k));
}
let roots: Vec<[u64; 4]> = keys[..LEVELS - 1]
.iter()
.map(k k.key_root.expect("an inner level").map(v v.as_u64()))
.collect();
if roots != INNER_ROOTS {
return Err(format!(
"wrapped: the inner levels' key roots {roots:?} are not the pinned ones (envelope::wrapped::INNER_ROOTS is stale)"
));
}
let c = Arc::new(keys);
let last = c.last().expect("a level").clone();
final_slot().lock().expect("final key").get_or_insert(last);
*g = Some(c.clone());
Ok(c)
}
pub fn final_key_cached() -> bool {
final_slot().lock().expect("final key").is_some()
}
pub fn admitted_ivc() -> (WhirParams, usize) {
(chain().0, STEP_LOG_ROWS as usize)
}
fn check_bound(k: &WrapKey) -> Result<(), String> {
let last = chain().1[LEVELS - 1];
if k.ivc != admitted_ivc() k.params.whir != last.whir k.params.mode != last.mode {
return Err("wrapped: the final key is not the admitted chain's".into());
}
if wrap::max_proof_bytes(k) > MAX_PROOF_BYTES {
return Err("wrapped: the final key's proofs can exceed 64 KiB".into());
}
Ok(())
}
pub(super) fn install_final(k: WrapKey) -> Result<(), String> {
check_bound(&k)?;
final_slot()
.lock()
.expect("final key")
.get_or_insert(Arc::new(k));
Ok(())
}
pub fn final_key() -> Result<Arc<WrapKey>, String> {
let mut slot = final_slot().lock().expect("final key");
if let Some(k) = slot.as_ref() {
return Ok(k.clone());
}
let (ivc_whir, [l0, l1, l2]) = chain();
let ikey = recursive::pinned_key(&ivc_whir, ivc::key(&ivc_whir, STEP_LOG_ROWS as usize)?)?;
let k0 = wrap::derive_key_ivc_at(l0, &ikey, pinned_root(0))?;
let k1 = wrap::derive_key_wrap_at(l1, &k0, pinned_root(1))?;
let k = Arc::new(wrap::derive_key_wrap(l2, &k1)?);
check_bound(&k)?;
*slot = Some(k.clone());
Ok(k)
}
pub fn prove(program: &ExecutionNoun, input: &[u64], budget: u64) -> Result<Envelope, String> {
let (ivc_whir, _) = chain();
let run = machine::execute_exact(program, input, budget, STEP_LOG_ROWS)
.map_err(e format!("machine: {e:?}"))?;
let proof = ivc::prove_run(&run, &ivc_whir)?;
wrap_recursive(&run.statement, &proof)
}
pub fn wrap_recursive(st: &MachineStatement, proof: &ivc::IvcProof) -> Result<Envelope, String> {
let (ivc_whir, _) = chain();
let keys = chain_keys()?;
let ikey = ivc::key(&ivc_whir, STEP_LOG_ROWS as usize)?;
let prep = ivc::prepare(
st,
&ivc_whir,
proof.log_rows,
proof.start,
proof.segments,
proof.chain,
)?;
let pn = ivc::verify_claim(&prep, proof)?;
let mut last: Option<wrap::WrapProof> = None;
for (i, k) in keys.iter().enumerate() {
let inner = match &last {
None => Inner::Ivc { key: &ikey, proof },
Some(w) => Inner::Wrap {
key: &keys[i - 1],
proof: w,
},
};
let (w, _) = wrap::prove(k, &inner, &prep.publics, &pn)?;
last = Some(w);
}
let fp = FinalProof {
log_rows: proof.log_rows,
start: proof.start,
segments: proof.segments,
chain: proof.chain,
pn,
wrap: last.expect("a level"),
};
Ok(Envelope::Wrapped {
statement: st.public(),
proof: Box::new(fp),
})
}
pub(super) fn verify(
st: &MachineStatement,
fp: &FinalProof,
state: Option<&StateEvidence>,
) -> Result<(), String> {
machine::authenticate_state(st, state)?;
if fp.log_rows as usize != admitted_ivc().1 {
return Err("wrapped envelope: step size not admitted".into());
}
let k = final_key()?;
check_bound(&k)?;
wrap::verify_statement(st, &admitted_ivc().0, &k, fp)
}
pub fn proof_bytes(fp: &FinalProof) -> Result<Vec<u8>, String> {
Ok(fp.to_bytes(&*final_key()?))
}
pub(super) fn encode(st: &MachinePublic, fp: &FinalProof, w: &mut Writer) {
let bytes = proof_bytes(fp).expect("the wrapped chain's final key");
w.raw(&[WRAPPED_FORMAT]);
w.raw(&chain_bytes());
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()? != WRAPPED_FORMAT {
return Err(E::NonCanonical);
}
let want = chain_bytes();
if r.raw(want.len())? != want.as_slice() {
return Err(E::NonCanonical);
}
let statement = read_statement(r, expect)?;
let n = r.len(MAX_PROOF_BYTES, 1)?;
let bytes = r.raw(n)?;
if usize::from(*bytes.first().ok_or(E::Truncated)?) != admitted_ivc().1 {
return Err(E::NonCanonical);
}
let key = final_key().map_err(_ E::NonCanonical)?;
let proof = FinalProof::from_bytes(bytes, &key).map_err(_ E::NonCanonical)?;
Ok(Envelope::Wrapped {
statement,
proof: Box::new(proof),
})
}