Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix deserialization of authorizations with multiple transitions #2479

Open
wants to merge 3 commits into
base: staging
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 53 additions & 3 deletions synthesizer/process/src/stack/authorization/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,13 @@ impl<N: Network> TryFrom<(Vec<Request<N>>, Vec<Transition<N>>)> for Authorizatio
requests.len(),
transitions.len()
);
// Move the first request to the back in order to match the transitions.
let mut requests_deque = VecDeque::from(requests.clone());
if let Some(first_request) = requests_deque.pop_front() {
requests_deque.push_back(first_request);
}
// Ensure the requests and transitions are in order.
for (index, (request, transition)) in requests.iter().zip_eq(&transitions).enumerate() {
for (index, (request, transition)) in requests_deque.iter().zip_eq(&transitions).enumerate() {
// Ensure the request and transition correspond to one another.
ensure_request_and_transition_matches(index, request, transition)?;
}
Expand Down Expand Up @@ -252,8 +257,8 @@ fn ensure_request_and_transition_matches<N: Network>(
#[cfg(test)]
pub(crate) mod test_helpers {
use super::*;
use crate::Process;
use console::account::PrivateKey;
use crate::{Identifier, Process, ProgramID, Value};
use console::account::{Address, PrivateKey};

type CurrentNetwork = console::network::MainnetV0;
type CurrentAleo = circuit::AleoV0;
Expand Down Expand Up @@ -285,4 +290,49 @@ pub(crate) mod test_helpers {
assert!(authorization.is_fee_public(), "Authorization must be for a call to 'credits.aleo/fee_public'");
authorization
}

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

// Sample a private key.
let private_key = PrivateKey::new(rng).unwrap();

// Initialize the process.
let process = Process::<CurrentNetwork>::load().unwrap();

// Specify the program ID
let program_id = ProgramID::<CurrentNetwork>::from_str("credits.aleo").unwrap();

// Specify the function name
let function_name = Identifier::<CurrentNetwork>::from_str("transfer_public").unwrap();

// Generate the inputs
let destination =
Value::<CurrentNetwork>::from_str(&format!("{}", Address::try_from(private_key).unwrap())).unwrap();
let amount = Value::<CurrentNetwork>::from_str("1u64").unwrap();

// Generate the Authorization
let authorization = process
.authorize::<CurrentAleo, _>(
&private_key,
&program_id,
&function_name,
vec![destination, amount].iter(),
rng,
)
.unwrap();

// Assert there is only 1 transition
assert!(authorization.transitions().len() == 1);

// Serialize the Authorization into a String
let authorization_serialized = authorization.to_string();

// Attempt to deserialize the Authorization from String
let deserialization_result = Authorization::<CurrentNetwork>::from_str(&authorization_serialized);

// Assert that the deserialization result is Ok
assert!(deserialization_result.is_ok());
}
}
88 changes: 88 additions & 0 deletions synthesizer/src/vm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,7 @@ impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
#[cfg(test)]
pub(crate) mod test_helpers {
use super::*;
use circuit::AleoV0;
use console::{
account::{Address, ViewKey},
network::MainnetV0,
Expand All @@ -484,6 +485,7 @@ pub(crate) mod test_helpers {
use synthesizer_snark::VerifyingKey;

pub(crate) type CurrentNetwork = MainnetV0;
type CurrentAleo = AleoV0;

/// Samples a new finalize state.
pub(crate) fn sample_finalize_state(block_height: u32) -> FinalizeGlobalState {
Expand Down Expand Up @@ -2545,6 +2547,92 @@ finalize transfer_public_to_private:
vm.puzzle.prove(rng.gen(), rng.gen(), rng.gen(), None).unwrap();
}

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

// Initialize a private key.
let private_key = sample_genesis_private_key(rng);

// Initialize the genesis block.
let genesis = sample_genesis_block(rng);

// Initialize the VM.
let vm = sample_vm();
// Update the VM.
vm.add_next_block(&genesis).unwrap();

// Deploy the base program.
let child_program = Program::from_str(
r"
program child_program.aleo;

function check:
input r0 as field.private;
assert.eq r0 123456789123456789123456789123456789123456789123456789field;
",
)
.unwrap();

let deployment = vm.deploy(&private_key, &child_program, None, 0, None, rng).unwrap();
assert!(vm.check_transaction(&deployment, None, rng).is_ok());
vm.add_next_block(&sample_next_block(&vm, &private_key, &[deployment], rng).unwrap()).unwrap();

// Check that program is deployed.
assert!(vm.contains_program(&ProgramID::from_str("child_program.aleo").unwrap()));

// Deploy the program that calls the program from the previous layer.
let parent_program = Program::from_str(
r"
import child_program.aleo;

program parent_program.aleo;

function check:
input r0 as field.private;
call child_program.aleo/check r0;
",
)
.unwrap();

let deployment = vm.deploy(&private_key, &parent_program, None, 0, None, rng).unwrap();
assert!(vm.check_transaction(&deployment, None, rng).is_ok());
vm.add_next_block(&sample_next_block(&vm, &private_key, &[deployment], rng).unwrap()).unwrap();

// Check that program is deployed.
assert!(vm.contains_program(&ProgramID::from_str("parent_program.aleo").unwrap()));

// Initialize the process.
let mut process = Process::<CurrentNetwork>::load().unwrap();

// Load the child and parent program
process.add_program(&child_program).unwrap();
process.add_program(&parent_program).unwrap();

// Specify the function name on the parent program
let function_name = Identifier::<CurrentNetwork>::from_str("check").unwrap();

// Generate a random Field for input
let input = Value::<CurrentNetwork>::from_str(&Field::<CurrentNetwork>::rand(rng).to_string()).unwrap();

// Generate the authorization that will contain multiple transitions
let authorization = process
.authorize::<CurrentAleo, _>(&private_key, parent_program.id(), &function_name, vec![input].iter(), rng)
.unwrap();

// Assert the Authorization has more than 1 transitions
assert!(authorization.transitions().len() > 1);

// Serialize the Authorization into a String
let authorization_serialized = authorization.to_string();

// Attempt to deserialize the Authorization from String
let deserialization_result = Authorization::<CurrentNetwork>::from_str(&authorization_serialized);

// Assert that the deserialization result is Ok
assert!(deserialization_result.is_ok());
}

#[cfg(feature = "rocks")]
#[test]
fn test_atomic_unpause_on_error() {
Expand Down