Merge pull request #108 from vacp2p/schouhy/add-privacy-preserving-circuit-tests

Add privacy preserving circuit tests
This commit is contained in:
Sergio Chouhy 2025-09-18 12:11:53 -03:00 committed by GitHub
commit b7c2e8a3ec
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 843 additions and 14 deletions

View File

@ -32,11 +32,13 @@ fn main() {
// Check that the program is well behaved.
// See the # Programs section for the definition of the `validate_execution` method.
validate_execution(&pre_states, &post_states, program_id);
if !validate_execution(&pre_states, &post_states, program_id) {
panic!("Bad behaved program");
}
let n_accounts = pre_states.len();
if visibility_mask.len() != n_accounts {
panic!();
panic!("Invalid visibility mask length");
}
// These lists will be the public outputs of this circuit
@ -59,7 +61,9 @@ fn main() {
public_pre_states.push(pre_states[i].clone());
let mut post = post_states[i].clone();
post.nonce += 1;
if pre_states[i].is_authorized {
post.nonce += 1;
}
if post.program_owner == DEFAULT_PROGRAM_ID {
// Claim account
post.program_owner = program_id;
@ -136,7 +140,7 @@ fn main() {
}
if private_keys_iter.next().is_some() {
panic!("Too many private accounts keys.");
panic!("Too many private account keys.");
}
if private_auth_iter.next().is_some() {

View File

@ -45,4 +45,7 @@ pub enum NssaError {
#[error("Invalid privacy preserving execution circuit proof")]
InvalidPrivacyPreservingProof,
#[error("Circuit proving error")]
CircuitProvingError(String),
}

View File

@ -47,7 +47,9 @@ pub fn execute_and_prove(
env_builder.write(&circuit_input).unwrap();
let env = env_builder.build().unwrap();
let prover = default_prover();
let prove_info = prover.prove(env, PRIVACY_PRESERVING_CIRCUIT_ELF).unwrap();
let prove_info = prover
.prove(env, PRIVACY_PRESERVING_CIRCUIT_ELF)
.map_err(|e| NssaError::CircuitProvingError(e.to_string()))?;
let proof = Proof(borsh::to_vec(&prove_info.receipt.inner)?);
@ -109,6 +111,7 @@ mod tests {
let program = Program::authenticated_transfer_program();
let sender = AccountWithMetadata {
account: Account {
program_owner: program.id(),
balance: 100,
..Account::default()
},
@ -175,11 +178,13 @@ mod tests {
#[test]
fn prove_privacy_preserving_execution_circuit_fully_private() {
let program = Program::authenticated_transfer_program();
let sender_pre = AccountWithMetadata {
account: Account {
balance: 100,
nonce: 0xdeadbeef,
..Account::default()
program_owner: program.id(),
data: vec![],
},
is_authorized: true,
};

View File

@ -145,12 +145,6 @@ impl V01State {
*current_account = post;
}
// // 5. Increment nonces
for address in tx.signer_addresses() {
let current_account = self.get_account_by_address_mut(address);
current_account.nonce += 1;
}
Ok(())
}
@ -219,6 +213,7 @@ pub mod tests {
use crate::{
Address, PublicKey, PublicTransaction, V01State,
error::NssaError,
execute_and_prove,
privacy_preserving_transaction::{
PrivacyPreservingTransaction, circuit, message::Message, witness_set::WitnessSet,
},
@ -941,6 +936,13 @@ pub mod tests {
&state,
);
let expected_sender_post = {
let mut this = state.get_account_by_address(&sender_keys.address());
this.balance -= balance_to_move;
this.nonce += 1;
this
};
let [expected_new_commitment] = tx.message().new_commitments.clone().try_into().unwrap();
assert!(!state.private_state.0.contains(&expected_new_commitment));
@ -948,6 +950,8 @@ pub mod tests {
.transition_from_privacy_preserving_transaction(&tx)
.unwrap();
let sender_post = state.get_account_by_address(&sender_keys.address());
assert_eq!(sender_post, expected_sender_post);
assert!(state.private_state.0.contains(&expected_new_commitment));
assert_eq!(
@ -1040,6 +1044,12 @@ pub mod tests {
let balance_to_move = 37;
let expected_recipient_post = {
let mut this = state.get_account_by_address(&recipient_keys.address());
this.balance += balance_to_move;
this
};
let tx = deshielded_balance_transfer_for_tests(
&sender_keys,
&sender_private_account,
@ -1070,6 +1080,8 @@ pub mod tests {
.transition_from_privacy_preserving_transaction(&tx)
.unwrap();
let recipient_post = state.get_account_by_address(&recipient_keys.address());
assert_eq!(recipient_post, expected_recipient_post);
assert!(state.private_state.0.contains(&sender_pre_commitment));
assert!(state.private_state.0.contains(&expected_new_commitment));
assert!(state.private_state.1.contains(&expected_new_nullifier));
@ -1080,4 +1092,809 @@ pub mod tests {
recipient_initial_balance + balance_to_move
);
}
#[test]
fn test_burner_program_should_fail_in_privacy_preserving_circuit() {
let program = Program::burner();
let public_account = AccountWithMetadata {
account: Account {
program_owner: program.id(),
balance: 100,
..Account::default()
},
is_authorized: true,
};
let result = execute_and_prove(
&[public_account],
&Program::serialize_instruction(10u128).unwrap(),
&[0],
&[],
&[],
&[],
&program,
);
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
}
#[test]
fn test_minter_program_should_fail_in_privacy_preserving_circuit() {
let program = Program::minter();
let public_account = AccountWithMetadata {
account: Account {
program_owner: program.id(),
balance: 0,
..Account::default()
},
is_authorized: true,
};
let result = execute_and_prove(
&[public_account],
&Program::serialize_instruction(10u128).unwrap(),
&[0],
&[],
&[],
&[],
&program,
);
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
}
#[test]
fn test_nonce_changer_program_should_fail_in_privacy_preserving_circuit() {
let program = Program::nonce_changer_program();
let public_account = AccountWithMetadata {
account: Account {
program_owner: program.id(),
balance: 0,
..Account::default()
},
is_authorized: true,
};
let result = execute_and_prove(
&[public_account],
&Program::serialize_instruction(()).unwrap(),
&[0],
&[],
&[],
&[],
&program,
);
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
}
#[test]
fn test_data_changer_program_should_fail_for_non_owned_account_in_privacy_preserving_circuit() {
let program = Program::data_changer();
let public_account = AccountWithMetadata {
account: Account {
program_owner: [0, 1, 2, 3, 4, 5, 6, 7],
balance: 0,
..Account::default()
},
is_authorized: true,
};
let result = execute_and_prove(
&[public_account],
&Program::serialize_instruction(()).unwrap(),
&[0],
&[],
&[],
&[],
&program,
);
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
}
#[test]
fn test_extra_output_program_should_fail_in_privacy_preserving_circuit() {
let program = Program::extra_output_program();
let public_account = AccountWithMetadata {
account: Account {
program_owner: program.id(),
balance: 0,
..Account::default()
},
is_authorized: true,
};
let result = execute_and_prove(
&[public_account],
&Program::serialize_instruction(()).unwrap(),
&[0],
&[],
&[],
&[],
&program,
);
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
}
#[test]
fn test_missing_output_program_should_fail_in_privacy_preserving_circuit() {
let program = Program::missing_output_program();
let public_account_1 = AccountWithMetadata {
account: Account {
program_owner: program.id(),
balance: 0,
..Account::default()
},
is_authorized: true,
};
let public_account_2 = AccountWithMetadata {
account: Account {
program_owner: program.id(),
balance: 0,
..Account::default()
},
is_authorized: true,
};
let result = execute_and_prove(
&[public_account_1, public_account_2],
&Program::serialize_instruction(()).unwrap(),
&[0, 0],
&[],
&[],
&[],
&program,
);
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
}
#[test]
fn test_program_owner_changer_should_fail_in_privacy_preserving_circuit() {
let program = Program::program_owner_changer();
let public_account = AccountWithMetadata {
account: Account {
program_owner: program.id(),
balance: 0,
..Account::default()
},
is_authorized: true,
};
let result = execute_and_prove(
&[public_account],
&Program::serialize_instruction(()).unwrap(),
&[0],
&[],
&[],
&[],
&program,
);
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
}
#[test]
fn test_transfer_from_non_owned_account_should_fail_in_privacy_preserving_circuit() {
let program = Program::simple_balance_transfer();
let public_account_1 = AccountWithMetadata {
account: Account {
program_owner: [0, 1, 2, 3, 4, 5, 6, 7],
balance: 100,
..Account::default()
},
is_authorized: true,
};
let public_account_2 = AccountWithMetadata {
account: Account {
program_owner: program.id(),
balance: 0,
..Account::default()
},
is_authorized: true,
};
let result = execute_and_prove(
&[public_account_1, public_account_2],
&Program::serialize_instruction(10u128).unwrap(),
&[0, 0],
&[],
&[],
&[],
&program,
);
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
}
#[test]
fn test_circuit_fails_if_visibility_masks_have_incorrect_lenght() {
let program = Program::simple_balance_transfer();
let public_account_1 = AccountWithMetadata {
account: Account {
program_owner: program.id(),
balance: 100,
..Account::default()
},
is_authorized: true,
};
let public_account_2 = AccountWithMetadata {
account: Account {
program_owner: program.id(),
balance: 0,
..Account::default()
},
is_authorized: true,
};
// Setting only one visibility mask for a circuit execution with two pre_state accounts.
let visibility_mask = [0];
let result = execute_and_prove(
&[public_account_1, public_account_2],
&Program::serialize_instruction(10u128).unwrap(),
&visibility_mask,
&[],
&[],
&[],
&program,
);
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
}
#[test]
fn test_circuit_fails_if_insufficient_nonces_are_provided() {
let program = Program::simple_balance_transfer();
let sender_keys = test_private_account_keys_1();
let recipient_keys = test_private_account_keys_2();
let private_account_1 = AccountWithMetadata {
account: Account {
program_owner: program.id(),
balance: 100,
..Account::default()
},
is_authorized: true,
};
let private_account_2 = AccountWithMetadata {
account: Account::default(),
is_authorized: false,
};
// Setting only one nonce for an execution with two private accounts.
let private_account_nonces = [0xdeadbeef1];
let result = execute_and_prove(
&[private_account_1, private_account_2],
&Program::serialize_instruction(10u128).unwrap(),
&[1, 2],
&private_account_nonces,
&[
(
sender_keys.npk(),
SharedSecretKey::new(&[55; 32], &sender_keys.ivk()),
),
(
recipient_keys.npk(),
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
),
],
&[(sender_keys.nsk, (0, vec![]))],
&program,
);
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
}
#[test]
fn test_circuit_fails_if_insufficient_keys_are_provided() {
let program = Program::simple_balance_transfer();
let sender_keys = test_private_account_keys_1();
let private_account_1 = AccountWithMetadata {
account: Account {
program_owner: program.id(),
balance: 100,
..Account::default()
},
is_authorized: true,
};
let private_account_2 = AccountWithMetadata {
account: Account::default(),
is_authorized: false,
};
// Setting only one key for an execution with two private accounts.
let private_account_keys = [(
sender_keys.npk(),
SharedSecretKey::new(&[55; 32], &sender_keys.ivk()),
)];
let result = execute_and_prove(
&[private_account_1, private_account_2],
&Program::serialize_instruction(10u128).unwrap(),
&[1, 2],
&[0xdeadbeef1, 0xdeadbeef2],
&private_account_keys,
&[(sender_keys.nsk, (0, vec![]))],
&program,
);
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
}
#[test]
fn test_circuit_fails_if_insufficient_auth_keys_are_provided() {
let program = Program::simple_balance_transfer();
let sender_keys = test_private_account_keys_1();
let recipient_keys = test_private_account_keys_2();
let private_account_1 = AccountWithMetadata {
account: Account {
program_owner: program.id(),
balance: 100,
..Account::default()
},
is_authorized: true,
};
let private_account_2 = AccountWithMetadata {
account: Account::default(),
is_authorized: false,
};
// Setting no auth key for an execution with one non default private accounts.
let private_account_auth = [];
let result = execute_and_prove(
&[private_account_1, private_account_2],
&Program::serialize_instruction(10u128).unwrap(),
&[1, 2],
&[0xdeadbeef1, 0xdeadbeef2],
&[
(
sender_keys.npk(),
SharedSecretKey::new(&[55; 32], &sender_keys.ivk()),
),
(
recipient_keys.npk(),
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
),
],
&private_account_auth,
&program,
);
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
}
#[test]
fn test_circuit_fails_if_invalid_auth_keys_are_provided() {
let program = Program::simple_balance_transfer();
let sender_keys = test_private_account_keys_1();
let recipient_keys = test_private_account_keys_2();
let private_account_1 = AccountWithMetadata {
account: Account {
program_owner: program.id(),
balance: 100,
..Account::default()
},
is_authorized: true,
};
let private_account_2 = AccountWithMetadata {
account: Account::default(),
is_authorized: false,
};
let private_account_keys = [
// First private account is the sender
(
sender_keys.npk(),
SharedSecretKey::new(&[55; 32], &sender_keys.ivk()),
),
// Second private account is the recipient
(
recipient_keys.npk(),
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
),
];
let private_account_auth = [
// Setting the recipient key to authorize the sender.
// This should be set to the sender private account in
// a normal circumstance. The recipient can't authorize this.
(recipient_keys.nsk, (0, vec![])),
];
let result = execute_and_prove(
&[private_account_1, private_account_2],
&Program::serialize_instruction(10u128).unwrap(),
&[1, 2],
&[0xdeadbeef1, 0xdeadbeef2],
&private_account_keys,
&private_account_auth,
&program,
);
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
}
#[test]
fn test_circuit_should_fail_if_new_private_account_with_non_default_balance_is_provided() {
let program = Program::simple_balance_transfer();
let sender_keys = test_private_account_keys_1();
let recipient_keys = test_private_account_keys_2();
let private_account_1 = AccountWithMetadata {
account: Account {
program_owner: program.id(),
balance: 100,
..Account::default()
},
is_authorized: true,
};
let private_account_2 = AccountWithMetadata {
account: Account {
// Non default balance
balance: 1,
..Account::default()
},
is_authorized: false,
};
let result = execute_and_prove(
&[private_account_1, private_account_2],
&Program::serialize_instruction(10u128).unwrap(),
&[1, 2],
&[0xdeadbeef1, 0xdeadbeef2],
&[
(
sender_keys.npk(),
SharedSecretKey::new(&[55; 32], &sender_keys.ivk()),
),
(
recipient_keys.npk(),
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
),
],
&[(sender_keys.nsk, (0, vec![]))],
&program,
);
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
}
#[test]
fn test_circuit_should_fail_if_new_private_account_with_non_default_program_owner_is_provided()
{
let program = Program::simple_balance_transfer();
let sender_keys = test_private_account_keys_1();
let recipient_keys = test_private_account_keys_2();
let private_account_1 = AccountWithMetadata {
account: Account {
program_owner: program.id(),
balance: 100,
..Account::default()
},
is_authorized: true,
};
let private_account_2 = AccountWithMetadata {
account: Account {
// Non default program_owner
program_owner: [0, 1, 2, 3, 4, 5, 6, 7],
..Account::default()
},
is_authorized: false,
};
let result = execute_and_prove(
&[private_account_1, private_account_2],
&Program::serialize_instruction(10u128).unwrap(),
&[1, 2],
&[0xdeadbeef1, 0xdeadbeef2],
&[
(
sender_keys.npk(),
SharedSecretKey::new(&[55; 32], &sender_keys.ivk()),
),
(
recipient_keys.npk(),
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
),
],
&[(sender_keys.nsk, (0, vec![]))],
&program,
);
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
}
#[test]
fn test_circuit_should_fail_if_new_private_account_with_non_default_data_is_provided() {
let program = Program::simple_balance_transfer();
let sender_keys = test_private_account_keys_1();
let recipient_keys = test_private_account_keys_2();
let private_account_1 = AccountWithMetadata {
account: Account {
program_owner: program.id(),
balance: 100,
..Account::default()
},
is_authorized: true,
};
let private_account_2 = AccountWithMetadata {
account: Account {
// Non default data
data: b"hola mundo".to_vec(),
..Account::default()
},
is_authorized: false,
};
let result = execute_and_prove(
&[private_account_1, private_account_2],
&Program::serialize_instruction(10u128).unwrap(),
&[1, 2],
&[0xdeadbeef1, 0xdeadbeef2],
&[
(
sender_keys.npk(),
SharedSecretKey::new(&[55; 32], &sender_keys.ivk()),
),
(
recipient_keys.npk(),
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
),
],
&[(sender_keys.nsk, (0, vec![]))],
&program,
);
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
}
#[test]
fn test_circuit_should_fail_if_new_private_account_with_non_default_nonce_is_provided() {
let program = Program::simple_balance_transfer();
let sender_keys = test_private_account_keys_1();
let recipient_keys = test_private_account_keys_2();
let private_account_1 = AccountWithMetadata {
account: Account {
program_owner: program.id(),
balance: 100,
..Account::default()
},
is_authorized: true,
};
let private_account_2 = AccountWithMetadata {
account: Account {
// Non default nonce
nonce: 0xdeadbeef,
..Account::default()
},
is_authorized: false,
};
let result = execute_and_prove(
&[private_account_1, private_account_2],
&Program::serialize_instruction(10u128).unwrap(),
&[1, 2],
&[0xdeadbeef1, 0xdeadbeef2],
&[
(
sender_keys.npk(),
SharedSecretKey::new(&[55; 32], &sender_keys.ivk()),
),
(
recipient_keys.npk(),
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
),
],
&[(sender_keys.nsk, (0, vec![]))],
&program,
);
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
}
#[test]
fn test_circuit_should_fail_if_new_private_account_is_provided_with_default_values_but_marked_as_authorized()
{
let program = Program::simple_balance_transfer();
let sender_keys = test_private_account_keys_1();
let recipient_keys = test_private_account_keys_2();
let private_account_1 = AccountWithMetadata {
account: Account {
program_owner: program.id(),
balance: 100,
..Account::default()
},
is_authorized: true,
};
let private_account_2 = AccountWithMetadata {
account: Account::default(),
// This should be set to false in normal circumstances
is_authorized: true,
};
let result = execute_and_prove(
&[private_account_1, private_account_2],
&Program::serialize_instruction(10u128).unwrap(),
&[1, 2],
&[0xdeadbeef1, 0xdeadbeef2],
&[
(
sender_keys.npk(),
SharedSecretKey::new(&[55; 32], &sender_keys.ivk()),
),
(
recipient_keys.npk(),
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
),
],
&[(sender_keys.nsk, (0, vec![]))],
&program,
);
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
}
#[test]
fn test_circuit_should_fail_with_invalid_visibility_mask_value() {
let program = Program::simple_balance_transfer();
let public_account_1 = AccountWithMetadata {
account: Account {
program_owner: program.id(),
balance: 100,
..Account::default()
},
is_authorized: true,
};
let public_account_2 = AccountWithMetadata {
account: Account::default(),
is_authorized: false,
};
let visibility_mask = [0, 3];
let result = execute_and_prove(
&[public_account_1, public_account_2],
&Program::serialize_instruction(10u128).unwrap(),
&visibility_mask,
&[],
&[],
&[],
&program,
);
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
}
#[test]
fn test_circuit_should_fail_with_too_many_nonces() {
let program = Program::simple_balance_transfer();
let sender_keys = test_private_account_keys_1();
let recipient_keys = test_private_account_keys_2();
let private_account_1 = AccountWithMetadata {
account: Account {
program_owner: program.id(),
balance: 100,
..Account::default()
},
is_authorized: true,
};
let private_account_2 = AccountWithMetadata {
account: Account::default(),
is_authorized: false,
};
// Setting three new private account nonces for a circuit execution with only two private
// accounts.
let private_account_nonces = [0xdeadbeef1, 0xdeadbeef2, 0xdeadbeef3];
let result = execute_and_prove(
&[private_account_1, private_account_2],
&Program::serialize_instruction(10u128).unwrap(),
&[1, 2],
&private_account_nonces,
&[
(
sender_keys.npk(),
SharedSecretKey::new(&[55; 32], &sender_keys.ivk()),
),
(
recipient_keys.npk(),
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
),
],
&[(sender_keys.nsk, (0, vec![]))],
&program,
);
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
}
#[test]
fn test_circuit_should_fail_with_too_many_private_account_keys() {
let program = Program::simple_balance_transfer();
let sender_keys = test_private_account_keys_1();
let recipient_keys = test_private_account_keys_2();
let private_account_1 = AccountWithMetadata {
account: Account {
program_owner: program.id(),
balance: 100,
..Account::default()
},
is_authorized: true,
};
let private_account_2 = AccountWithMetadata {
account: Account::default(),
is_authorized: false,
};
// Setting three private account keys for a circuit execution with only two private
// accounts.
let private_account_keys = [
(
sender_keys.npk(),
SharedSecretKey::new(&[55; 32], &sender_keys.ivk()),
),
(
recipient_keys.npk(),
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
),
(
sender_keys.npk(),
SharedSecretKey::new(&[57; 32], &sender_keys.ivk()),
),
];
let result = execute_and_prove(
&[private_account_1, private_account_2],
&Program::serialize_instruction(10u128).unwrap(),
&[1, 2],
&[0xdeadbeef1, 0xdeadbeef2],
&private_account_keys,
&[(sender_keys.nsk, (0, vec![]))],
&program,
);
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
}
#[test]
fn test_circuit_should_fail_with_too_many_private_account_auth_keys() {
let program = Program::simple_balance_transfer();
let sender_keys = test_private_account_keys_1();
let recipient_keys = test_private_account_keys_2();
let private_account_1 = AccountWithMetadata {
account: Account {
program_owner: program.id(),
balance: 100,
..Account::default()
},
is_authorized: true,
};
let private_account_2 = AccountWithMetadata {
account: Account::default(),
is_authorized: false,
};
// Setting two private account keys for a circuit execution with only one non default
// private account (visibility mask equal to 1 means that auth keys are expected).
let visibility_mask = [1, 2];
let private_account_auth = [
(sender_keys.nsk, (0, vec![])),
(recipient_keys.nsk, (1, vec![])),
];
let result = execute_and_prove(
&[private_account_1, private_account_2],
&Program::serialize_instruction(10u128).unwrap(),
&visibility_mask,
&[0xdeadbeef1, 0xdeadbeef2],
&[
(
sender_keys.npk(),
SharedSecretKey::new(&[55; 32], &sender_keys.ivk()),
),
(
recipient_keys.npk(),
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
),
],
&private_account_auth,
&program,
);
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
}
}

View File

@ -5,12 +5,12 @@ type Instruction = ();
fn main() {
let ProgramInput { pre_states, .. } = read_nssa_inputs::<Instruction>();
let [pre1, _] = match pre_states.try_into() {
let [pre1, pre2] = match pre_states.try_into() {
Ok(array) => array,
Err(_) => return,
};
let account_pre1 = pre1.account.clone();
write_nssa_outputs(vec![pre1], vec![account_pre1]);
write_nssa_outputs(vec![pre1, pre2], vec![account_pre1]);
}