snarkvm_synthesizer_process/
verify_fee.rsuse super::*;
impl<N: Network> Process<N> {
#[inline]
pub fn verify_fee(&self, fee: &Fee<N>, deployment_or_execution_id: Field<N>) -> Result<()> {
let timer = timer!("Process::verify_fee");
let stack = self.get_stack(fee.program_id())?;
let function = stack.get_function(fee.function_name())?;
#[cfg(debug_assertions)]
{
println!("Verifying fee from {}/{}...", fee.program_id(), fee.function_name());
if stack.get_number_of_calls(function.name())? != 1 {
bail!("The number of function calls in '{}/{}' should be 1", stack.program_id(), function.name())
}
debug_assert_eq!(
**fee.id(),
N::hash_bhp512(&(fee.to_root()?, *fee.tcm()).to_bits_le())?,
"Transition ID of the fee is incorrect"
);
}
let is_fee_private = fee.is_fee_private();
let is_fee_public = fee.is_fee_public();
ensure!(is_fee_private || is_fee_public, "Incorrect program ID or function name for fee transition");
ensure!(fee.inputs().len() <= N::MAX_INPUTS, "Fee exceeded maximum number of inputs");
ensure!(fee.outputs().len() <= N::MAX_INPUTS, "Fee exceeded maximum number of outputs");
let fee_input_variants = fee.inputs().iter().map(Input::variant).collect::<Vec<_>>();
let fee_output_variants = fee.outputs().iter().map(Output::variant).collect::<Vec<_>>();
ensure!(function.input_variants() == fee_input_variants, "The fee input variants do not match");
ensure!(function.output_variants() == fee_output_variants, "The fee output variants do not match");
let Ok(candidate_id) = fee.deployment_or_execution_id() else {
bail!("Failed to get the deployment or execution ID in the fee transition")
};
if candidate_id != deployment_or_execution_id {
bail!("Incorrect deployment or execution ID in the fee transition")
}
lap!(timer, "Verify the deployment or execution ID");
match is_fee_private {
true => self.verify_fee_private(&fee)?,
false => self.verify_fee_public(&fee)?,
}
finish!(timer, "Verify the fee transition");
Ok(())
}
}
impl<N: Network> Process<N> {
fn verify_fee_private(&self, fee: &&Fee<N>) -> Result<()> {
let timer = timer!("Process::verify_fee_private");
let network_id = U16::new(N::ID);
let function_id = compute_function_id(&network_id, fee.program_id(), fee.function_name())?;
ensure!(
fee.inputs().iter().filter(|input| matches!(input, Input::Record(..))).count() == 1,
"The fee transition must contain *1* input record"
);
let num_inputs = fee.inputs().len();
ensure!(num_inputs == 4, "The number of inputs in the fee transition should be 4, found {num_inputs}",);
if fee.inputs().iter().enumerate().any(|(index, input)| !input.verify(function_id, fee.tcm(), index)) {
bail!("Failed to verify a fee input")
}
lap!(timer, "Verify the inputs");
ensure!(
fee.outputs().len() == 1,
"The number of outputs in the fee transition should be 1, found {}",
fee.outputs().len()
);
if fee
.outputs()
.iter()
.enumerate()
.any(|(index, output)| !output.verify(function_id, fee.tcm(), num_inputs + index))
{
bail!("Failed to verify a fee output")
}
lap!(timer, "Verify the outputs");
let (tpk_x, tpk_y) = fee.tpk().to_xy_coordinates();
let (parent_x, parent_y) = fee.program_id().to_address()?.to_xy_coordinates();
let mut inputs = vec![N::Field::one(), *tpk_x, *tpk_y, **fee.tcm(), **fee.scm()];
inputs.extend(fee.inputs().iter().flat_map(|input| input.verifier_inputs()));
inputs.extend([*Field::<N>::one(), *parent_x, *parent_y]);
inputs.extend(fee.outputs().iter().flat_map(|output| output.verifier_inputs()));
lap!(timer, "Construct the verifier inputs");
#[cfg(debug_assertions)]
println!("Fee public inputs ({} elements): {:#?}", inputs.len(), inputs);
let verifying_key = self.get_verifying_key(fee.program_id(), fee.function_name())?;
Trace::verify_fee_proof((verifying_key, vec![inputs]), fee)?;
finish!(timer, "Verify the fee proof");
Ok(())
}
fn verify_fee_public(&self, fee: &&Fee<N>) -> Result<()> {
let timer = timer!("Process::verify_fee_public");
let network_id = U16::new(N::ID);
let function_id = compute_function_id(&network_id, fee.program_id(), fee.function_name())?;
ensure!(
fee.inputs().iter().all(|input| matches!(input, Input::Public(..))),
"The fee transition must contain *only* public inputs"
);
let num_inputs = fee.inputs().len();
ensure!(num_inputs == 3, "The number of inputs in the fee transition should be 3, found {num_inputs}",);
if fee.inputs().iter().enumerate().any(|(index, input)| !input.verify(function_id, fee.tcm(), index)) {
bail!("Failed to verify a fee input")
}
lap!(timer, "Verify the inputs");
ensure!(
fee.outputs().len() == 1,
"The number of outputs in the fee transition should be 1, found {}",
fee.outputs().len()
);
if fee
.outputs()
.iter()
.enumerate()
.any(|(index, output)| !output.verify(function_id, fee.tcm(), num_inputs + index))
{
bail!("Failed to verify a fee output")
}
lap!(timer, "Verify the outputs");
let (tpk_x, tpk_y) = fee.tpk().to_xy_coordinates();
let (parent_x, parent_y) = fee.program_id().to_address()?.to_xy_coordinates();
let mut inputs = vec![N::Field::one(), *tpk_x, *tpk_y, **fee.tcm(), **fee.scm()];
inputs.extend(fee.inputs().iter().flat_map(|input| input.verifier_inputs()));
inputs.extend([*Field::<N>::one(), *parent_x, *parent_y]);
inputs.extend(fee.outputs().iter().flat_map(|output| output.verifier_inputs()));
lap!(timer, "Construct the verifier inputs");
#[cfg(debug_assertions)]
println!("Fee public inputs ({} elements): {:#?}", inputs.len(), inputs);
let verifying_key = self.get_verifying_key(fee.program_id(), fee.function_name())?;
Trace::verify_fee_proof((verifying_key, vec![inputs]), fee)?;
finish!(timer, "Verify the fee proof");
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use console::prelude::TestRng;
use ledger_block::Transaction;
#[test]
fn test_verify_fee() {
let rng = &mut TestRng::default();
let transactions = [
ledger_test_helpers::sample_deployment_transaction(true, rng),
ledger_test_helpers::sample_deployment_transaction(false, rng),
ledger_test_helpers::sample_execution_transaction_with_fee(true, rng),
ledger_test_helpers::sample_execution_transaction_with_fee(false, rng),
ledger_test_helpers::sample_fee_private_transaction(rng),
ledger_test_helpers::sample_fee_public_transaction(rng),
];
let process = Process::load().unwrap();
for transaction in transactions {
match transaction {
Transaction::Deploy(_, _, deployment, fee) => {
let deployment_id = deployment.to_deployment_id().unwrap();
process.verify_fee(&fee, deployment_id).unwrap();
}
Transaction::Execute(_, execution, fee) => {
let execution_id = execution.to_execution_id().unwrap();
process.verify_fee(&fee.unwrap(), execution_id).unwrap();
}
Transaction::Fee(_, fee) => match fee.is_fee_private() {
true => process.verify_fee_private(&&fee).unwrap(),
false => process.verify_fee_public(&&fee).unwrap(),
},
}
}
}
}