Skip to content

Commit 0c8bd3b

Browse files
authored
Merge pull request #2582 from ProvableHQ/additional-tx-verification
Add additional `Input/Output` checks
2 parents 1de86e7 + d76d1a7 commit 0c8bd3b

File tree

7 files changed

+70
-11
lines changed

7 files changed

+70
-11
lines changed

console/program/src/data_types/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,4 @@ mod struct_type;
3535
pub use struct_type::StructType;
3636

3737
mod value_type;
38-
pub use value_type::ValueType;
38+
pub use value_type::{ValueType, Variant};

console/program/src/data_types/value_type/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ use snarkvm_console_network::prelude::*;
2222

2323
use enum_index::EnumIndex;
2424

25+
pub type Variant = u8;
26+
2527
#[derive(Clone, PartialEq, Eq, Hash, EnumIndex)]
2628
pub enum ValueType<N: Network> {
2729
/// A constant type.
@@ -38,6 +40,20 @@ pub enum ValueType<N: Network> {
3840
Future(Locator<N>),
3941
}
4042

43+
impl<N: Network> ValueType<N> {
44+
/// Returns the variant of the value type.
45+
pub const fn variant(&self) -> Variant {
46+
match self {
47+
ValueType::Constant(..) => 0,
48+
ValueType::Public(..) => 1,
49+
ValueType::Private(..) => 2,
50+
ValueType::Record(..) => 3,
51+
ValueType::ExternalRecord(..) => 4,
52+
ValueType::Future(..) => 5,
53+
}
54+
}
55+
}
56+
4157
impl<N: Network> From<EntryType<N>> for ValueType<N> {
4258
fn from(entry: EntryType<N>) -> Self {
4359
match entry {

synthesizer/process/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ use console::{
4949
program::{Identifier, Literal, Locator, Plaintext, ProgramID, Record, Response, Value, compute_function_id},
5050
types::{Field, U16, U64},
5151
};
52-
use ledger_block::{Deployment, Execution, Fee, Input, Transition};
52+
use ledger_block::{Deployment, Execution, Fee, Input, Output, Transition};
5353
use ledger_store::{FinalizeStorage, FinalizeStore, atomic_batch_scope};
5454
use synthesizer_program::{
5555
Branch,

synthesizer/process/src/verify_execution.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,14 @@ impl<N: Network> Process<N> {
112112
ensure!(function.inputs().len() == num_inputs, "The number of transition inputs is incorrect");
113113
ensure!(function.outputs().len() == num_outputs, "The number of transition outputs is incorrect");
114114

115+
// Ensure the input and output types are equivalent to the ones defined in the function.
116+
// We only need to check that the variant type matches because we already check the hashes in
117+
// the `Input::verify` and `Output::verify` functions.
118+
let transition_input_variants = transition.inputs().iter().map(Input::variant).collect::<Vec<_>>();
119+
let transition_output_variants = transition.outputs().iter().map(Output::variant).collect::<Vec<_>>();
120+
ensure!(function.input_variants() == transition_input_variants, "The input variants do not match");
121+
ensure!(function.output_variants() == transition_output_variants, "The output variants do not match");
122+
115123
// Retrieve the parent program ID.
116124
// Note: The last transition in the execution does not have a parent, by definition.
117125
let parent = reverse_call_graph.get(transition.id()).and_then(|tid| execution.get_program_id(tid));

synthesizer/process/src/verify_fee.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@ impl<N: Network> Process<N> {
2222
pub fn verify_fee(&self, fee: &Fee<N>, deployment_or_execution_id: Field<N>) -> Result<()> {
2323
let timer = timer!("Process::verify_fee");
2424

25+
// Retrieve the stack.
26+
let stack = self.get_stack(fee.program_id())?;
27+
// Retrieve the function from the stack.
28+
let function = stack.get_function(fee.function_name())?;
29+
2530
#[cfg(debug_assertions)]
2631
{
2732
println!("Verifying fee from {}/{}...", fee.program_id(), fee.function_name());
28-
// Retrieve the stack.
29-
let stack = self.get_stack(fee.program_id())?;
30-
// Retrieve the function from the stack.
31-
let function = stack.get_function(fee.function_name())?;
3233
// Ensure the number of function calls in this function is 1.
3334
if stack.get_number_of_calls(function.name())? != 1 {
3435
bail!("The number of function calls in '{}/{}' should be 1", stack.program_id(), function.name())
@@ -52,6 +53,14 @@ impl<N: Network> Process<N> {
5253
// Ensure the number of outputs is within the allowed range.
5354
ensure!(fee.outputs().len() <= N::MAX_INPUTS, "Fee exceeded maximum number of outputs");
5455

56+
// Ensure the input and output types are equivalent to the ones defined in the function.
57+
// We only need to check that the variant type matches because we already check the hashes in
58+
// the `Input::verify` and `Output::verify` functions.
59+
let fee_input_variants = fee.inputs().iter().map(Input::variant).collect::<Vec<_>>();
60+
let fee_output_variants = fee.outputs().iter().map(Output::variant).collect::<Vec<_>>();
61+
ensure!(function.input_variants() == fee_input_variants, "The fee input variants do not match");
62+
ensure!(function.output_variants() == fee_output_variants, "The fee output variants do not match");
63+
5564
// Retrieve the candidate deployment or execution ID.
5665
let Ok(candidate_id) = fee.deployment_or_execution_id() else {
5766
bail!("Failed to get the deployment or execution ID in the fee transition")

synthesizer/program/src/function/mod.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use crate::{
2828
};
2929
use console::{
3030
network::prelude::*,
31-
program::{Identifier, Register, ValueType},
31+
program::{Identifier, Register, ValueType, Variant},
3232
};
3333

3434
use indexmap::IndexSet;
@@ -69,6 +69,11 @@ impl<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>> Fun
6969
self.inputs.iter().map(|input| input.value_type()).cloned().collect()
7070
}
7171

72+
/// Returns the function input type variants.
73+
pub fn input_variants(&self) -> Vec<Variant> {
74+
self.inputs.iter().map(|input| input.value_type().variant()).collect()
75+
}
76+
7277
/// Returns the function instructions.
7378
pub fn instructions(&self) -> &[Instruction] {
7479
&self.instructions
@@ -84,6 +89,11 @@ impl<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>> Fun
8489
self.outputs.iter().map(|output| output.value_type()).cloned().collect()
8590
}
8691

92+
/// Returns the function output type variants.
93+
pub fn output_variants(&self) -> Vec<Variant> {
94+
self.outputs.iter().map(|output| output.value_type().variant()).collect()
95+
}
96+
8797
/// Returns the function finalize logic.
8898
pub const fn finalize_logic(&self) -> Option<&FinalizeCore<N, Command>> {
8999
self.finalize_logic.as_ref()

synthesizer/program/src/lib.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -808,9 +808,17 @@ function swap:
808808
assert_eq!(function.inputs().len(), 2);
809809
assert_eq!(function.input_types().len(), 2);
810810

811+
// Declare the expected input types.
812+
let expected_input_type_1 = ValueType::ExternalRecord(Locator::from_str("eth.aleo/eth")?);
813+
let expected_input_type_2 = ValueType::ExternalRecord(Locator::from_str("usdc.aleo/usdc")?);
814+
811815
// Ensure the inputs are external records.
812-
assert_eq!(function.input_types()[0], ValueType::ExternalRecord(Locator::from_str("eth.aleo/eth")?));
813-
assert_eq!(function.input_types()[1], ValueType::ExternalRecord(Locator::from_str("usdc.aleo/usdc")?));
816+
assert_eq!(function.input_types()[0], expected_input_type_1);
817+
assert_eq!(function.input_types()[1], expected_input_type_2);
818+
819+
// Ensure the input variants are correct.
820+
assert_eq!(function.input_types()[0].variant(), expected_input_type_1.variant());
821+
assert_eq!(function.input_types()[1].variant(), expected_input_type_2.variant());
814822

815823
// Ensure there are two instructions.
816824
assert_eq!(function.instructions().len(), 2);
@@ -823,9 +831,17 @@ function swap:
823831
assert_eq!(function.outputs().len(), 2);
824832
assert_eq!(function.output_types().len(), 2);
825833

834+
// Declare the expected output types.
835+
let expected_output_type_1 = ValueType::ExternalRecord(Locator::from_str("eth.aleo/eth")?);
836+
let expected_output_type_2 = ValueType::ExternalRecord(Locator::from_str("usdc.aleo/usdc")?);
837+
826838
// Ensure the outputs are external records.
827-
assert_eq!(function.output_types()[0], ValueType::ExternalRecord(Locator::from_str("eth.aleo/eth")?));
828-
assert_eq!(function.output_types()[1], ValueType::ExternalRecord(Locator::from_str("usdc.aleo/usdc")?));
839+
assert_eq!(function.output_types()[0], expected_output_type_1);
840+
assert_eq!(function.output_types()[1], expected_output_type_2);
841+
842+
// Ensure the output variants are correct.
843+
assert_eq!(function.output_types()[0].variant(), expected_output_type_1.variant());
844+
assert_eq!(function.output_types()[1].variant(), expected_output_type_2.variant());
829845

830846
Ok(())
831847
}

0 commit comments

Comments
 (0)