mod assignment;
pub use assignment::{TranslationAssignment, compute_console_dynamic_or_external_record_id};
mod prepare;
#[cfg(test)]
mod tests;
use crate::Stack;
use circuit::{Inject, traits::ToGroup};
use console::{
network::prelude::*,
program::{DynamicRecord, Identifier, Plaintext, ProgramID, Record, U16, ValueType, compute_function_id},
types::{Field, Group},
};
use snarkvm_ledger_block::{Input, Output, Transition};
use snarkvm_synthesizer_program::Function;
use snarkvm_synthesizer_snark::{ProvingKey, VerifyingKey};
use std::collections::HashMap;
#[derive(Clone, Debug, Default)]
pub struct Translation<N: Network> {
translation_tasks: HashMap<N::TransitionID, Vec<(TranslationAssignment<N>, ProvingKey<N>)>>,
}
impl<N: Network> Translation<N> {
pub fn new() -> Self {
Self { translation_tasks: HashMap::new() }
}
pub fn insert_transition(
&mut self,
transition_id: N::TransitionID,
translation_assignments: Vec<(TranslationAssignment<N>, ProvingKey<N>)>,
) -> Result<()> {
self.translation_tasks.insert(transition_id, translation_assignments);
Ok(())
}
pub fn prepare_verifier_inputs<'a, F>(
transitions: impl ExactSizeIterator<Item = &'a Transition<N>>,
transition_map: &HashMap<N::TransitionID, (&Transition<N>, Function<N>)>,
get_verifying_key: &F,
) -> Result<Vec<(VerifyingKey<N>, Vec<Vec<N::Field>>)>>
where
F: Fn(&(ProgramID<N>, Identifier<N>)) -> Result<VerifyingKey<N>>,
{
let mut batch_verifier_inputs: HashMap<(ProgramID<N>, Identifier<N>), Vec<Vec<N::Field>>> = HashMap::new();
let mut translation_index = 0;
for transition in transitions {
let (_, callee_function_core) = transition_map
.get(transition.id())
.ok_or_else(|| anyhow!("Transition {} from execution not found transition map", transition.id()))?;
let callee_function_id =
compute_function_id(&U16::<N>::new(N::ID), transition.program_id(), transition.function_name())?;
let callee_input_types = callee_function_core.input_types();
let num_inputs = transition.inputs().len();
ensure!(
num_inputs == callee_input_types.len(),
"The number of transition inputs ({num_inputs}) and callee input types ({}) do not match",
callee_input_types.len()
);
for (record_register_index, (input, callee_input_type)) in
transition.inputs().iter().zip_eq(callee_input_types.iter()).enumerate()
{
let Some(dynamic_id) = input.dynamic_id() else { continue };
let field_translation_index = *Field::<N>::from_u128(translation_index as u128);
let field_record_register_index = *Field::<N>::from_u128(record_register_index as u128);
let field_is_to_static = N::Field::one();
let field_function_id = *callee_function_id;
let field_id_static = **input.id();
let field_id_dynamic = **dynamic_id;
match (input, callee_input_type) {
(Input::RecordWithDynamicID(..), ValueType::Record(record_name)) => {
let field_is_external_record = N::Field::zero();
let verifier_inputs = vec![
N::Field::one(),
field_is_to_static,
field_is_external_record,
field_function_id,
field_translation_index,
field_record_register_index,
field_id_static,
field_id_dynamic,
];
batch_verifier_inputs
.entry((*transition.program_id(), *record_name))
.or_default()
.push(verifier_inputs);
translation_index += 1;
}
(Input::ExternalRecordWithDynamicID(..), ValueType::ExternalRecord(record_locator)) => {
let field_is_external_record = N::Field::one();
let verifier_inputs = vec![
N::Field::one(),
field_is_to_static,
field_is_external_record,
field_function_id,
field_translation_index,
field_record_register_index,
field_id_static,
field_id_dynamic,
];
batch_verifier_inputs
.entry((*record_locator.program_id(), *record_locator.resource()))
.or_default()
.push(verifier_inputs);
translation_index += 1;
}
_ => bail!(
"Unexpected input variant with dynamic_id in transition {} (index: {})",
transition.id(),
record_register_index
),
}
}
let callee_output_types = callee_function_core.output_types();
ensure!(
transition.outputs().len() == callee_output_types.len(),
"The number of transition outputs ({}) and callee output types ({}) do not match",
transition.outputs().len(),
callee_output_types.len()
);
for (record_register_index, (output, callee_output_type)) in
transition.outputs().iter().zip_eq(callee_output_types.iter()).enumerate()
{
let Some(dynamic_id) = output.dynamic_id() else { continue };
let field_translation_index = *Field::<N>::from_u128(translation_index as u128);
let field_record_register_index = *Field::<N>::from_u128((num_inputs + record_register_index) as u128);
let field_is_to_static = N::Field::zero();
let field_function_id = *callee_function_id;
let field_id_static = **output.id();
let field_id_dynamic = **dynamic_id;
match (output, callee_output_type) {
(Output::RecordWithDynamicID(..), ValueType::Record(record_name)) => {
let field_is_external_record = N::Field::zero();
let verifier_inputs = vec![
N::Field::one(),
field_is_to_static,
field_is_external_record,
field_function_id,
field_translation_index,
field_record_register_index,
field_id_static,
field_id_dynamic,
];
batch_verifier_inputs
.entry((*transition.program_id(), *record_name))
.or_default()
.push(verifier_inputs);
translation_index += 1;
}
(Output::ExternalRecordWithDynamicID(..), ValueType::ExternalRecord(record_locator)) => {
let field_is_external_record = N::Field::one();
let verifier_inputs = vec![
N::Field::one(),
field_is_to_static,
field_is_external_record,
field_function_id,
field_translation_index,
field_record_register_index,
field_id_static,
field_id_dynamic,
];
batch_verifier_inputs
.entry((*record_locator.program_id(), *record_locator.resource()))
.or_default()
.push(verifier_inputs);
translation_index += 1;
}
_ => bail!(
"Unexpected output variant with dynamic_id in transition {} (index: {})",
transition.id(),
record_register_index
),
}
}
}
let batch_with_verifying_keys = batch_verifier_inputs
.into_iter()
.map(|(key, inputs)| Ok((get_verifying_key(&key)?, inputs)))
.collect::<Result<Vec<(VerifyingKey<N>, Vec<Vec<N::Field>>)>>>()?;
Ok(batch_with_verifying_keys)
}
}