Skip to content

Commit

Permalink
Merge pull request #2511 from ProvableHQ/additional-tx-checks
Browse files Browse the repository at this point in the history
[Fix] Add additional input/output checks to `Execution` verification
  • Loading branch information
zosorock authored Jul 29, 2024
2 parents d170a9f + f12c33d commit ef547d9
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 1 deletion.
5 changes: 5 additions & 0 deletions synthesizer/process/src/verify_execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ impl<N: Network> Process<N> {

// Ensure each output is valid.
let num_inputs = transition.inputs().len();
let num_outputs = transition.outputs().len();
if transition
.outputs()
.iter()
Expand All @@ -106,6 +107,10 @@ impl<N: Network> Process<N> {
// Retrieve the function from the stack.
let function = stack.get_function(transition.function_name())?;

// Ensure the number of inputs and outputs match the expected number in the function.
ensure!(function.inputs().len() == num_inputs, "The number of transition inputs is incorrect");
ensure!(function.outputs().len() == num_outputs, "The number of transition outputs is incorrect");

// Retrieve the parent program ID.
// Note: The last transition in the execution does not have a parent, by definition.
let parent = reverse_call_graph.get(transition.id()).and_then(|tid| execution.get_program_id(tid));
Expand Down
71 changes: 70 additions & 1 deletion synthesizer/src/vm/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ mod tests {
account::{Address, ViewKey},
types::Field,
};
use ledger_block::{Block, Header, Metadata, Transaction};
use ledger_block::{Block, Header, Metadata, Transaction, Transition};

type CurrentNetwork = test_helpers::CurrentNetwork;

Expand Down Expand Up @@ -641,4 +641,73 @@ function compute:
// Ensure that the program can't be deployed.
assert!(vm.deploy_raw(&program, rng).is_err());
}

#[test]
fn test_check_mutated_execution() {
let rng = &mut TestRng::default();

// Initialize the VM.
let vm = crate::vm::test_helpers::sample_vm();
// Fetch the caller's private key.
let caller_private_key = crate::vm::test_helpers::sample_genesis_private_key(rng);
// Initialize the genesis block.
let genesis = crate::vm::test_helpers::sample_genesis_block(rng);
// Update the VM.
vm.add_next_block(&genesis).unwrap();

// Fetch a valid execution transaction with a public fee.
let valid_transaction = crate::vm::test_helpers::sample_execution_transaction_with_public_fee(rng);
vm.check_transaction(&valid_transaction, None, rng).unwrap();

// Mutate the execution transaction by inserting a Field::Zero as an output.
let execution = valid_transaction.execution().unwrap();

// Extract the first transition from the execution.
let transitions: Vec<_> = execution.transitions().collect();
assert_eq!(transitions.len(), 1);
let transition = transitions[0].clone();

// Mutate the transition by adding an additional `Field::zero` output. This is significant because the Varuna
// verifier pads the inputs with `Field::zero`s, which means that the same proof is valid for both the
// original and the mutated executions.
let added_output = Output::ExternalRecord(Field::zero());
let mutated_outputs = [transition.outputs(), &[added_output]].concat();
let mutated_transition = Transition::new(
*transition.program_id(),
*transition.function_name(),
transition.inputs().to_vec(),
mutated_outputs,
*transition.tpk(),
*transition.tcm(),
*transition.scm(),
)
.unwrap();

// Construct the mutated execution.
let mutated_execution = Execution::from(
[mutated_transition].into_iter(),
execution.global_state_root(),
execution.proof().cloned(),
)
.unwrap();

// Authorize the fee.
let authorization = vm
.authorize_fee_public(
&caller_private_key,
10_000_000,
100,
mutated_execution.to_execution_id().unwrap(),
rng,
)
.unwrap();
// Compute the fee.
let fee = vm.execute_fee_authorization(authorization, None, rng).unwrap();

// Construct the transaction.
let mutated_transaction = Transaction::from_execution(mutated_execution, Some(fee)).unwrap();

// Ensure that the mutated transaction fails verification due to an extra output.
assert!(vm.check_transaction(&mutated_transaction, None, rng).is_err());
}
}

0 comments on commit ef547d9

Please sign in to comment.