use circuit::{Aleo, Field as CircuitField, traits::ToFields};
use super::*;
pub fn compute_console_dynamic_or_external_record_id<N: Network>(
function_id: Field<N>,
record_fields: Vec<Field<N>>,
tvk: Field<N>,
index: U16<N>,
) -> Result<Field<N>> {
let mut preimage = Vec::new();
preimage.push(function_id);
preimage.extend(record_fields);
preimage.push(tvk);
preimage.push(index.to_field()?);
N::hash_psd8(&preimage)
}
fn compute_circuit_dynamic_or_external_record_id<A: Aleo>(
function_id: CircuitField<A>,
record_fields: Vec<CircuitField<A>>,
tvk: CircuitField<A>,
index: CircuitField<A>,
) -> CircuitField<A> {
let mut preimage = Vec::new();
preimage.push(function_id);
preimage.extend(record_fields);
preimage.push(tvk);
preimage.push(index);
A::hash_psd8(&preimage)
}
#[derive(Clone, Debug)]
pub struct TranslationAssignment<N: Network> {
pub(crate) record_static: Record<N, Plaintext<N>>,
pub(crate) record_dynamic: DynamicRecord<N>,
pub(crate) program_id: ProgramID<N>,
pub(crate) function_id: Field<N>,
pub(crate) record_name: Identifier<N>,
pub(crate) is_to_static: bool,
pub(crate) is_external_record: bool,
pub(crate) tvk: Field<N>,
pub(crate) record_view_key: Option<Field<N>>,
pub(crate) gamma: Option<Group<N>>,
pub(crate) record_register_index: u16,
pub(crate) id_dynamic: Field<N>,
pub(crate) id_static: Field<N>,
}
impl<N: Network> TranslationAssignment<N> {
#[allow(clippy::too_many_arguments)]
pub fn new(
record_static: Record<N, Plaintext<N>>,
record_dynamic: DynamicRecord<N>,
program_id: ProgramID<N>,
function_id: Field<N>,
record_name: Identifier<N>,
is_to_static: bool,
is_external_record: bool,
tvk: Field<N>,
record_view_key: Option<Field<N>>,
gamma: Option<Group<N>>,
record_register_index: u16,
id_dynamic: Field<N>,
id_static: Field<N>,
) -> Self {
Self {
record_static,
record_dynamic,
program_id,
function_id,
record_name,
is_to_static,
is_external_record,
tvk,
record_view_key,
gamma,
record_register_index,
id_dynamic,
id_static,
}
}
pub(crate) fn to_circuit_assignment_internal<A: Aleo<Network = N>>(&self, translation_index: u16) -> Result<()> {
ensure!(
A::count() == (0, 1, 0, 0, (0, 0, 0)),
"Circuit environment is not clean: expected (0, 1, 0, 0, (0, 0, 0)), got {:?}",
A::count()
);
A::reset();
let circuit_program_id = circuit::ProgramID::<A>::constant(self.program_id);
let circuit_record_name = circuit::Identifier::<A>::constant(self.record_name);
let circuit_is_to_static = circuit::Boolean::<A>::new(circuit::Mode::Public, self.is_to_static);
let circuit_is_external_record = circuit::Boolean::<A>::new(circuit::Mode::Public, self.is_external_record);
let circuit_function_id = circuit::Field::<A>::new(circuit::Mode::Public, self.function_id);
let _circuit_translation_index =
circuit::Field::<A>::new(circuit::Mode::Public, console::types::Field::<N>::from_u16(translation_index));
let circuit_record_register_index = circuit::Field::<A>::new(
circuit::Mode::Public,
console::types::Field::<N>::from_u16(self.record_register_index),
);
let circuit_id_static = circuit::Field::<A>::new(circuit::Mode::Public, self.id_static);
let circuit_id_dynamic = circuit::Field::<A>::new(circuit::Mode::Public, self.id_dynamic);
let circuit_record_static =
circuit::Record::<A, circuit::Plaintext<A>>::new(circuit::Mode::Private, self.record_static.clone());
let circuit_record_dynamic =
circuit::DynamicRecord::<A>::new(circuit::Mode::Private, self.record_dynamic.clone());
let circuit_tvk = circuit::Field::<A>::new(circuit::Mode::Private, self.tvk);
let circuit_record_view_key =
circuit::Field::<A>::new(circuit::Mode::Private, self.record_view_key.unwrap_or_else(Field::zero));
let circuit_gamma = circuit::Group::<A>::new(circuit::Mode::Private, self.gamma.unwrap_or_else(Group::zero));
let actual_id_dynamic = compute_circuit_dynamic_or_external_record_id(
circuit_function_id.clone(),
circuit_record_dynamic.to_fields(),
circuit_tvk.clone(),
circuit_record_register_index.clone(),
);
let circuit_static_commitment =
circuit_record_static.to_commitment(&circuit_program_id, &circuit_record_name, &circuit_record_view_key);
let circuit_static_serial_number = circuit::Record::<A, circuit::Plaintext<A>>::serial_number_from_gamma(
&circuit_gamma,
circuit_static_commitment.clone(),
);
let actual_id_static_non_external = circuit::Field::<A>::ternary(
&circuit_is_to_static,
&circuit_static_serial_number,
&circuit_static_commitment,
);
let actual_id_static_external = compute_circuit_dynamic_or_external_record_id(
circuit_function_id,
circuit_record_static.to_fields(),
circuit_tvk,
circuit_record_register_index,
);
let actual_id_static = circuit::Field::<A>::ternary(
&circuit_is_external_record,
&actual_id_static_external,
&actual_id_static_non_external,
);
let circuit_tree = circuit::DynamicRecord::<A>::merkleize_data(circuit_record_static.data())?;
let circuit_data_root = circuit_tree.root();
A::assert_eq(circuit_record_static.owner().to_group(), circuit_record_dynamic.owner().to_group())?;
A::assert_eq(circuit_record_static.nonce(), circuit_record_dynamic.nonce())?;
A::assert_eq(circuit_record_static.version(), circuit_record_dynamic.version())?;
A::assert_eq(circuit_data_root, circuit_record_dynamic.root())?;
A::assert_eq(actual_id_static, circuit_id_static)?;
A::assert_eq(actual_id_dynamic, circuit_id_dynamic)?;
Ok(())
}
pub fn to_circuit_assignment<A: circuit::Aleo<Network = N>>(
&self,
translation_index: u16,
) -> Result<circuit::Assignment<N::Field>> {
self.to_circuit_assignment_internal::<A>(translation_index)?;
Stack::log_circuit::<A>(
format_args!("Translation circuit for dynamic record with nonce {}", self.record_static.nonce()),
"TranslationAssignment",
);
Ok(A::eject_assignment_and_reset())
}
}