mod prepare;
mod assignment_v0;
pub use assignment_v0::*;
mod assignment;
pub use assignment::*;
use crate::Stack;
use circuit::{
U32,
traits::{FromBits as _, FromField, ToBits as _},
};
use console::{
network::prelude::*,
program::{InputID, StatePath, TRANSACTION_DEPTH, TransactionLeaf, TransitionLeaf, TransitionPath},
types::{Field, Group},
};
use snarkvm_ledger_block::{Input, Output, Transaction, Transition};
use snarkvm_ledger_query::QueryTrait;
use std::collections::{HashMap, VecDeque};
#[derive(Clone, Copy, Debug)]
pub enum InclusionVersion {
V0,
V1,
}
#[derive(Clone, Debug)]
pub enum InclusionAssignmentWrapper<N: Network> {
V0(InclusionV0Assignment<N>),
V1(InclusionAssignment<N>),
}
#[derive(Clone, Debug)]
struct InputTask<N: Network> {
commitment: Field<N>,
gamma: Group<N>,
serial_number: Field<N>,
local: Option<(TransactionLeaf<N>, Field<N>, Field<N>, TransitionPath<N>, TransitionLeaf<N>)>,
}
#[derive(Clone, Debug, Default)]
pub(super) struct Inclusion<N: Network> {
input_tasks: HashMap<N::TransitionID, Vec<InputTask<N>>>,
output_commitments:
HashMap<Field<N>, (TransactionLeaf<N>, Field<N>, Field<N>, TransitionPath<N>, TransitionLeaf<N>)>,
}
impl<N: Network> Inclusion<N> {
pub fn new() -> Self {
Self { input_tasks: HashMap::new(), output_commitments: HashMap::new() }
}
pub fn insert_transition(&mut self, input_ids: &[InputID<N>], transition: &Transition<N>) -> Result<()> {
if input_ids.len() != transition.inputs().len() {
bail!("Inclusion expected the same number of input IDs as transition inputs")
}
let transition_index = u16::try_from(self.input_tasks.len())?;
let input_tasks = self.input_tasks.entry(*transition.id()).or_default();
for input_id in input_ids {
if let InputID::Record(commitment, gamma, _, serial_number, _) = input_id {
input_tasks.push(InputTask {
commitment: *commitment,
gamma: *gamma,
serial_number: *serial_number,
local: self.output_commitments.get(commitment).cloned(),
});
}
}
if !transition.outputs().is_empty() {
let transaction_leaf = TransactionLeaf::new_execution(transition_index, **transition.id());
let transition_root = transition.to_root()?;
let tcm = *transition.tcm();
for (index, output) in transition.outputs().iter().enumerate() {
if let Output::Record(commitment, ..) = output {
let output_index = u8::try_from(input_ids.len().saturating_add(index))?;
let transition_leaf = output.to_transition_leaf(output_index);
let transition_path = transition.to_path(&transition_leaf)?;
self.output_commitments.insert(
*commitment,
(transaction_leaf, transition_root, tcm, transition_path, transition_leaf),
);
}
}
}
Ok(())
}
}
impl<N: Network> Inclusion<N> {
pub fn prepare_verifier_inputs<'a>(
global_state_root: N::StateRoot,
inclusion_version: InclusionVersion,
transitions: impl ExactSizeIterator<Item = &'a Transition<N>>,
) -> Result<Vec<Vec<N::Field>>> {
let num_transitions = transitions.len();
let mut transaction_tree = N::merkle_tree_bhp::<TRANSACTION_DEPTH>(&[])?;
let mut batch_verifier_inputs = vec![];
for (transition_index, transition) in transitions.enumerate() {
let local_state_root = *transaction_tree.root();
let (is_record_block_height_reached, upgrade_block_height) = match transition.is_credits() {
true => (!transition.is_upgrade(), N::INCLUSION_UPGRADE_HEIGHT()?.saturating_add(1)),
false => (true, 0),
};
for input in transition.inputs() {
if let Input::Record(serial_number, _) = input {
let mut verifier_inputs =
vec![N::Field::one(), **global_state_root, *local_state_root, **serial_number];
match inclusion_version {
InclusionVersion::V0 => {}
InclusionVersion::V1 => {
verifier_inputs.push(*Field::<N>::from_bits_le(&[is_record_block_height_reached])?);
verifier_inputs.push(*Field::<N>::from_u32(upgrade_block_height));
}
}
batch_verifier_inputs.push(verifier_inputs);
}
}
if transition_index + 1 != num_transitions {
let leaf = TransactionLeaf::new_execution(u16::try_from(transition_index)?, **transition.id());
transaction_tree.append(&[leaf.to_bits_le()])?;
}
}
if batch_verifier_inputs.is_empty() && *global_state_root == Field::zero() {
bail!("Inclusion expected the global state root in the execution to *not* be zero")
}
Ok(batch_verifier_inputs)
}
}