2025-11-26 00:27:20 +03:00
|
|
|
use std::collections::{HashMap, HashSet};
|
|
|
|
|
|
|
|
|
|
use nssa_core::{
|
|
|
|
|
Commitment, CommitmentSetDigest, DUMMY_COMMITMENT, MembershipProof, Nullifier,
|
|
|
|
|
account::{Account, AccountId},
|
|
|
|
|
program::ProgramId,
|
|
|
|
|
};
|
|
|
|
|
|
2025-08-06 20:05:04 -03:00
|
|
|
use crate::{
|
2025-09-30 15:30:28 -03:00
|
|
|
error::NssaError, merkle_tree::MerkleTree,
|
2025-08-14 15:34:21 -03:00
|
|
|
privacy_preserving_transaction::PrivacyPreservingTransaction, program::Program,
|
2025-10-15 17:25:26 -03:00
|
|
|
program_deployment_transaction::ProgramDeploymentTransaction,
|
2025-08-14 15:34:21 -03:00
|
|
|
public_transaction::PublicTransaction,
|
2025-08-06 20:05:04 -03:00
|
|
|
};
|
2025-08-14 15:34:21 -03:00
|
|
|
|
2025-11-12 19:55:02 -03:00
|
|
|
pub const MAX_NUMBER_CHAINED_CALLS: usize = 10;
|
|
|
|
|
|
2025-08-25 07:44:56 -03:00
|
|
|
pub(crate) struct CommitmentSet {
|
|
|
|
|
merkle_tree: MerkleTree,
|
|
|
|
|
commitments: HashMap<Commitment, usize>,
|
2025-08-27 16:24:20 -03:00
|
|
|
root_history: HashSet<CommitmentSetDigest>,
|
2025-08-25 07:44:56 -03:00
|
|
|
}
|
2025-08-14 15:34:21 -03:00
|
|
|
|
|
|
|
|
impl CommitmentSet {
|
2025-08-22 10:34:09 -03:00
|
|
|
pub(crate) fn digest(&self) -> CommitmentSetDigest {
|
2025-08-25 07:44:56 -03:00
|
|
|
self.merkle_tree.root()
|
2025-08-14 15:34:21 -03:00
|
|
|
}
|
|
|
|
|
|
2025-09-05 20:48:56 -04:00
|
|
|
/// Queries the `CommitmentSet` for a membership proof of commitment
|
2025-08-27 16:24:20 -03:00
|
|
|
pub fn get_proof_for(&self, commitment: &Commitment) -> Option<MembershipProof> {
|
2025-08-25 07:44:56 -03:00
|
|
|
let index = *self.commitments.get(commitment)?;
|
2025-08-27 16:24:20 -03:00
|
|
|
|
|
|
|
|
self.merkle_tree
|
2025-08-25 07:44:56 -03:00
|
|
|
.get_authentication_path_for(index)
|
2025-08-27 16:24:20 -03:00
|
|
|
.map(|path| (index, path))
|
2025-08-14 15:34:21 -03:00
|
|
|
}
|
2025-08-18 14:28:26 -03:00
|
|
|
|
2025-09-05 20:48:47 -04:00
|
|
|
/// Inserts a list of commitments to the `CommitmentSet`.
|
2025-08-22 13:42:37 -03:00
|
|
|
pub(crate) fn extend(&mut self, commitments: &[Commitment]) {
|
2025-08-25 07:44:56 -03:00
|
|
|
for commitment in commitments.iter().cloned() {
|
|
|
|
|
let index = self.merkle_tree.insert(commitment.to_byte_array());
|
|
|
|
|
self.commitments.insert(commitment, index);
|
2025-08-22 10:34:09 -03:00
|
|
|
}
|
2025-08-25 09:22:59 -03:00
|
|
|
self.root_history.insert(self.digest());
|
2025-08-22 10:34:09 -03:00
|
|
|
}
|
|
|
|
|
|
2025-08-18 14:28:26 -03:00
|
|
|
fn contains(&self, commitment: &Commitment) -> bool {
|
2025-08-25 07:44:56 -03:00
|
|
|
self.commitments.contains_key(commitment)
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-05 20:48:26 -04:00
|
|
|
/// Initializes an empty `CommitmentSet` with a given capacity.
|
2025-09-05 16:38:34 -04:00
|
|
|
/// If the capacity is not a power_of_two, then capacity is taken
|
2025-09-05 16:21:46 -04:00
|
|
|
/// to be the next power_of_two.
|
2025-08-25 07:44:56 -03:00
|
|
|
pub(crate) fn with_capacity(capacity: usize) -> CommitmentSet {
|
|
|
|
|
Self {
|
|
|
|
|
merkle_tree: MerkleTree::with_capacity(capacity),
|
|
|
|
|
commitments: HashMap::new(),
|
2025-08-25 09:22:59 -03:00
|
|
|
root_history: HashSet::new(),
|
2025-08-25 07:44:56 -03:00
|
|
|
}
|
2025-08-18 14:28:26 -03:00
|
|
|
}
|
2025-08-14 15:34:21 -03:00
|
|
|
}
|
2025-08-18 07:39:41 -03:00
|
|
|
|
2025-08-14 15:34:21 -03:00
|
|
|
type NullifierSet = HashSet<Nullifier>;
|
2025-08-06 20:05:04 -03:00
|
|
|
|
2025-10-16 16:24:18 -03:00
|
|
|
pub struct V02State {
|
2025-11-24 17:09:30 +03:00
|
|
|
public_state: HashMap<AccountId, Account>,
|
2025-08-27 16:24:20 -03:00
|
|
|
private_state: (CommitmentSet, NullifierSet),
|
2025-10-14 17:15:04 -03:00
|
|
|
programs: HashMap<ProgramId, Program>,
|
2025-08-06 20:05:04 -03:00
|
|
|
}
|
|
|
|
|
|
2025-10-16 16:24:18 -03:00
|
|
|
impl V02State {
|
2025-09-18 15:59:17 +03:00
|
|
|
pub fn new_with_genesis_accounts(
|
2025-11-24 17:09:30 +03:00
|
|
|
initial_data: &[(AccountId, u128)],
|
2025-09-18 15:59:17 +03:00
|
|
|
initial_commitments: &[nssa_core::Commitment],
|
|
|
|
|
) -> Self {
|
2025-08-10 00:53:53 -03:00
|
|
|
let authenticated_transfer_program = Program::authenticated_transfer_program();
|
2025-08-07 15:19:06 -03:00
|
|
|
let public_state = initial_data
|
2025-08-09 19:20:19 -03:00
|
|
|
.iter()
|
|
|
|
|
.copied()
|
2025-11-24 17:09:30 +03:00
|
|
|
.map(|(account_id, balance)| {
|
2025-08-09 18:40:32 -03:00
|
|
|
let account = Account {
|
2025-08-09 19:20:19 -03:00
|
|
|
balance,
|
2025-08-10 00:53:53 -03:00
|
|
|
program_owner: authenticated_transfer_program.id(),
|
2025-08-09 18:40:32 -03:00
|
|
|
..Account::default()
|
|
|
|
|
};
|
2025-11-24 17:09:30 +03:00
|
|
|
(account_id, account)
|
2025-08-07 15:19:06 -03:00
|
|
|
})
|
|
|
|
|
.collect();
|
2025-08-08 16:19:50 -03:00
|
|
|
|
2025-09-18 15:59:17 +03:00
|
|
|
let mut private_state = CommitmentSet::with_capacity(32);
|
2025-10-03 20:44:29 -03:00
|
|
|
private_state.extend(&[DUMMY_COMMITMENT]);
|
2025-09-18 15:59:17 +03:00
|
|
|
private_state.extend(initial_commitments);
|
|
|
|
|
|
2025-08-10 09:57:10 -03:00
|
|
|
let mut this = Self {
|
2025-08-08 16:19:50 -03:00
|
|
|
public_state,
|
2025-09-18 15:59:17 +03:00
|
|
|
private_state: (private_state, NullifierSet::new()),
|
2025-10-14 17:15:04 -03:00
|
|
|
programs: HashMap::new(),
|
2025-08-10 09:57:10 -03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.insert_program(Program::authenticated_transfer_program());
|
2025-09-12 15:18:25 -03:00
|
|
|
this.insert_program(Program::token());
|
2025-08-10 09:57:10 -03:00
|
|
|
|
|
|
|
|
this
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-12 21:27:27 -03:00
|
|
|
pub(crate) fn insert_program(&mut self, program: Program) {
|
2025-10-14 17:15:04 -03:00
|
|
|
self.programs.insert(program.id(), program);
|
2025-08-07 15:19:06 -03:00
|
|
|
}
|
|
|
|
|
|
2025-08-09 19:49:07 -03:00
|
|
|
pub fn transition_from_public_transaction(
|
|
|
|
|
&mut self,
|
|
|
|
|
tx: &PublicTransaction,
|
|
|
|
|
) -> Result<(), NssaError> {
|
2025-08-14 16:20:09 -03:00
|
|
|
let state_diff = tx.validate_and_produce_public_state_diff(self)?;
|
2025-08-06 20:05:04 -03:00
|
|
|
|
2025-11-24 17:09:30 +03:00
|
|
|
for (account_id, post) in state_diff.into_iter() {
|
|
|
|
|
let current_account = self.get_account_by_id_mut(account_id);
|
2025-08-14 14:48:20 -03:00
|
|
|
|
2025-08-06 20:05:04 -03:00
|
|
|
*current_account = post;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-24 17:09:30 +03:00
|
|
|
for account_id in tx.signer_account_ids() {
|
|
|
|
|
let current_account = self.get_account_by_id_mut(account_id);
|
2025-08-06 20:05:04 -03:00
|
|
|
current_account.nonce += 1;
|
|
|
|
|
}
|
2025-08-09 20:35:44 -03:00
|
|
|
|
2025-08-06 20:05:04 -03:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-14 15:34:21 -03:00
|
|
|
pub fn transition_from_privacy_preserving_transaction(
|
|
|
|
|
&mut self,
|
|
|
|
|
tx: &PrivacyPreservingTransaction,
|
|
|
|
|
) -> Result<(), NssaError> {
|
|
|
|
|
// 1. Verify the transaction satisfies acceptance criteria
|
2025-08-14 16:20:09 -03:00
|
|
|
let public_state_diff = tx.validate_and_produce_public_state_diff(self)?;
|
|
|
|
|
|
2025-08-14 15:34:21 -03:00
|
|
|
let message = tx.message();
|
|
|
|
|
|
|
|
|
|
// 2. Add new commitments
|
2025-08-21 10:19:08 -03:00
|
|
|
self.private_state.0.extend(&message.new_commitments);
|
2025-08-14 15:34:21 -03:00
|
|
|
|
|
|
|
|
// 3. Add new nullifiers
|
2025-08-25 09:22:59 -03:00
|
|
|
let new_nullifiers = message
|
|
|
|
|
.new_nullifiers
|
|
|
|
|
.iter()
|
|
|
|
|
.cloned()
|
|
|
|
|
.map(|(nullifier, _)| nullifier)
|
|
|
|
|
.collect::<Vec<Nullifier>>();
|
|
|
|
|
self.private_state.1.extend(new_nullifiers);
|
2025-08-14 15:34:21 -03:00
|
|
|
|
|
|
|
|
// 4. Update public accounts
|
2025-11-24 17:09:30 +03:00
|
|
|
for (account_id, post) in public_state_diff.into_iter() {
|
|
|
|
|
let current_account = self.get_account_by_id_mut(account_id);
|
2025-08-14 15:34:21 -03:00
|
|
|
*current_account = post;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-15 17:25:26 -03:00
|
|
|
pub fn transition_from_program_deployment_transaction(
|
|
|
|
|
&mut self,
|
|
|
|
|
tx: &ProgramDeploymentTransaction,
|
|
|
|
|
) -> Result<(), NssaError> {
|
|
|
|
|
let program = tx.validate_and_produce_public_state_diff(self)?;
|
|
|
|
|
self.insert_program(program);
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-24 17:09:30 +03:00
|
|
|
fn get_account_by_id_mut(&mut self, account_id: AccountId) -> &mut Account {
|
|
|
|
|
self.public_state.entry(account_id).or_default()
|
2025-08-06 20:05:04 -03:00
|
|
|
}
|
|
|
|
|
|
2025-11-24 17:09:30 +03:00
|
|
|
pub fn get_account_by_id(&self, account_id: &AccountId) -> Account {
|
2025-08-06 20:05:04 -03:00
|
|
|
self.public_state
|
2025-11-24 17:09:30 +03:00
|
|
|
.get(account_id)
|
2025-08-06 20:05:04 -03:00
|
|
|
.cloned()
|
|
|
|
|
.unwrap_or(Account::default())
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-27 16:24:20 -03:00
|
|
|
pub fn get_proof_for_commitment(&self, commitment: &Commitment) -> Option<MembershipProof> {
|
|
|
|
|
self.private_state.0.get_proof_for(commitment)
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-15 17:25:26 -03:00
|
|
|
pub(crate) fn programs(&self) -> &HashMap<ProgramId, Program> {
|
2025-10-14 17:15:04 -03:00
|
|
|
&self.programs
|
2025-08-06 20:05:04 -03:00
|
|
|
}
|
2025-08-14 16:20:09 -03:00
|
|
|
|
|
|
|
|
pub fn commitment_set_digest(&self) -> CommitmentSetDigest {
|
|
|
|
|
self.private_state.0.digest()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn check_commitments_are_new(
|
|
|
|
|
&self,
|
|
|
|
|
new_commitments: &[Commitment],
|
|
|
|
|
) -> Result<(), NssaError> {
|
2025-08-18 14:28:26 -03:00
|
|
|
for commitment in new_commitments.iter() {
|
|
|
|
|
if self.private_state.0.contains(commitment) {
|
|
|
|
|
return Err(NssaError::InvalidInput(
|
|
|
|
|
"Commitment already seen".to_string(),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
2025-08-14 16:20:09 -03:00
|
|
|
}
|
|
|
|
|
|
2025-08-25 09:22:59 -03:00
|
|
|
pub(crate) fn check_nullifiers_are_valid(
|
2025-08-14 16:20:09 -03:00
|
|
|
&self,
|
2025-08-25 09:22:59 -03:00
|
|
|
new_nullifiers: &[(Nullifier, CommitmentSetDigest)],
|
2025-08-14 16:20:09 -03:00
|
|
|
) -> Result<(), NssaError> {
|
2025-08-25 09:22:59 -03:00
|
|
|
for (nullifier, digest) in new_nullifiers.iter() {
|
2025-08-18 14:28:26 -03:00
|
|
|
if self.private_state.1.contains(nullifier) {
|
|
|
|
|
return Err(NssaError::InvalidInput(
|
|
|
|
|
"Nullifier already seen".to_string(),
|
|
|
|
|
));
|
|
|
|
|
}
|
2025-08-25 09:22:59 -03:00
|
|
|
if !self.private_state.0.root_history.contains(digest) {
|
|
|
|
|
return Err(NssaError::InvalidInput(
|
|
|
|
|
"Unrecognized commitment set digest".to_string(),
|
|
|
|
|
));
|
|
|
|
|
}
|
2025-08-18 14:28:26 -03:00
|
|
|
}
|
|
|
|
|
Ok(())
|
2025-08-14 16:20:09 -03:00
|
|
|
}
|
2025-08-06 20:05:04 -03:00
|
|
|
}
|
2025-08-13 01:33:11 -03:00
|
|
|
|
2025-09-05 23:45:44 -03:00
|
|
|
// TODO: Testnet only. Refactor to prevent compilation on mainnet.
|
2025-10-16 16:24:18 -03:00
|
|
|
impl V02State {
|
2025-11-24 17:09:30 +03:00
|
|
|
pub fn add_pinata_program(&mut self, account_id: AccountId) {
|
2025-09-04 17:05:12 -03:00
|
|
|
self.insert_program(Program::pinata());
|
|
|
|
|
|
|
|
|
|
self.public_state.insert(
|
2025-11-24 17:09:30 +03:00
|
|
|
account_id,
|
2025-09-04 17:05:12 -03:00
|
|
|
Account {
|
|
|
|
|
program_owner: Program::pinata().id(),
|
|
|
|
|
balance: 1500,
|
|
|
|
|
// Difficulty: 3
|
|
|
|
|
data: vec![3; 33],
|
|
|
|
|
nonce: 0,
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-13 01:33:11 -03:00
|
|
|
#[cfg(test)]
|
2025-08-22 12:29:45 -03:00
|
|
|
pub mod tests {
|
2025-08-13 01:33:11 -03:00
|
|
|
|
2025-11-21 19:17:50 -05:00
|
|
|
use serde::Serialize;
|
2025-08-13 01:33:11 -03:00
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
2025-11-26 00:27:20 +03:00
|
|
|
use nssa_core::{
|
|
|
|
|
Commitment, Nullifier, NullifierPublicKey, NullifierSecretKey, SharedSecretKey,
|
2025-12-02 15:20:16 -05:00
|
|
|
account::{Account, AccountId, AccountWithMetadata, Data, Nonce},
|
2025-11-26 00:27:20 +03:00
|
|
|
encryption::{EphemeralPublicKey, IncomingViewingPublicKey, Scalar},
|
|
|
|
|
program::ProgramId,
|
|
|
|
|
};
|
|
|
|
|
|
2025-08-13 01:33:11 -03:00
|
|
|
use crate::{
|
2025-11-24 17:09:30 +03:00
|
|
|
PublicKey, PublicTransaction, V02State,
|
2025-08-21 14:32:57 -03:00
|
|
|
error::NssaError,
|
2025-09-02 12:38:31 -03:00
|
|
|
execute_and_prove,
|
2025-08-21 14:32:57 -03:00
|
|
|
privacy_preserving_transaction::{
|
2025-08-27 16:24:20 -03:00
|
|
|
PrivacyPreservingTransaction, circuit, message::Message, witness_set::WitnessSet,
|
2025-11-21 19:17:50 -05:00
|
|
|
}, program::Program, program_methods, public_transaction, signature::PrivateKey, state::MAX_NUMBER_CHAINED_CALLS
|
2025-08-13 01:33:11 -03:00
|
|
|
};
|
2025-08-26 14:53:02 -03:00
|
|
|
|
2025-08-13 01:33:11 -03:00
|
|
|
fn transfer_transaction(
|
2025-11-24 17:09:30 +03:00
|
|
|
from: AccountId,
|
2025-08-13 01:33:11 -03:00
|
|
|
from_key: PrivateKey,
|
|
|
|
|
nonce: u128,
|
2025-11-24 17:09:30 +03:00
|
|
|
to: AccountId,
|
2025-08-13 01:33:11 -03:00
|
|
|
balance: u128,
|
|
|
|
|
) -> PublicTransaction {
|
2025-11-24 17:09:30 +03:00
|
|
|
let account_ids = vec![from, to];
|
2025-08-13 01:33:11 -03:00
|
|
|
let nonces = vec![nonce];
|
|
|
|
|
let program_id = Program::authenticated_transfer_program().id();
|
|
|
|
|
let message =
|
2025-11-24 17:09:30 +03:00
|
|
|
public_transaction::Message::try_new(program_id, account_ids, nonces, balance).unwrap();
|
2025-08-13 01:33:11 -03:00
|
|
|
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]);
|
|
|
|
|
PublicTransaction::new(message, witness_set)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_new_with_genesis() {
|
|
|
|
|
let key1 = PrivateKey::try_new([1; 32]).unwrap();
|
|
|
|
|
let key2 = PrivateKey::try_new([2; 32]).unwrap();
|
2025-11-24 17:09:30 +03:00
|
|
|
let addr1 = AccountId::from(&PublicKey::new_from_private_key(&key1));
|
|
|
|
|
let addr2 = AccountId::from(&PublicKey::new_from_private_key(&key2));
|
2025-08-13 13:42:00 +03:00
|
|
|
let initial_data = [(addr1, 100u128), (addr2, 151u128)];
|
2025-09-12 17:13:37 -03:00
|
|
|
let authenticated_transfers_program = Program::authenticated_transfer_program();
|
2025-08-13 01:33:11 -03:00
|
|
|
let expected_public_state = {
|
|
|
|
|
let mut this = HashMap::new();
|
|
|
|
|
this.insert(
|
|
|
|
|
addr1,
|
|
|
|
|
Account {
|
|
|
|
|
balance: 100,
|
2025-09-12 17:13:37 -03:00
|
|
|
program_owner: authenticated_transfers_program.id(),
|
2025-08-13 01:33:11 -03:00
|
|
|
..Account::default()
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
this.insert(
|
|
|
|
|
addr2,
|
|
|
|
|
Account {
|
|
|
|
|
balance: 151,
|
2025-09-12 17:13:37 -03:00
|
|
|
program_owner: authenticated_transfers_program.id(),
|
2025-08-13 01:33:11 -03:00
|
|
|
..Account::default()
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
this
|
|
|
|
|
};
|
|
|
|
|
let expected_builtin_programs = {
|
|
|
|
|
let mut this = HashMap::new();
|
2025-09-12 17:13:37 -03:00
|
|
|
this.insert(
|
|
|
|
|
authenticated_transfers_program.id(),
|
|
|
|
|
authenticated_transfers_program,
|
|
|
|
|
);
|
|
|
|
|
this.insert(Program::token().id(), Program::token());
|
2025-08-13 01:33:11 -03:00
|
|
|
this
|
|
|
|
|
};
|
|
|
|
|
|
2025-10-16 16:24:18 -03:00
|
|
|
let state = V02State::new_with_genesis_accounts(&initial_data, &[]);
|
2025-08-13 01:33:11 -03:00
|
|
|
|
|
|
|
|
assert_eq!(state.public_state, expected_public_state);
|
2025-10-14 17:15:04 -03:00
|
|
|
assert_eq!(state.programs, expected_builtin_programs);
|
2025-08-13 01:33:11 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_insert_program() {
|
2025-10-16 16:24:18 -03:00
|
|
|
let mut state = V02State::new_with_genesis_accounts(&[], &[]);
|
2025-08-13 01:33:11 -03:00
|
|
|
let program_to_insert = Program::simple_balance_transfer();
|
|
|
|
|
let program_id = program_to_insert.id();
|
2025-10-14 17:15:04 -03:00
|
|
|
assert!(!state.programs.contains_key(&program_id));
|
2025-08-13 01:33:11 -03:00
|
|
|
|
|
|
|
|
state.insert_program(program_to_insert);
|
|
|
|
|
|
2025-10-14 17:15:04 -03:00
|
|
|
assert!(state.programs.contains_key(&program_id));
|
2025-08-13 01:33:11 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2025-11-24 17:09:30 +03:00
|
|
|
fn test_get_account_by_account_id_non_default_account() {
|
2025-08-13 01:33:11 -03:00
|
|
|
let key = PrivateKey::try_new([1; 32]).unwrap();
|
2025-11-24 17:09:30 +03:00
|
|
|
let account_id = AccountId::from(&PublicKey::new_from_private_key(&key));
|
|
|
|
|
let initial_data = [(account_id, 100u128)];
|
2025-10-16 16:24:18 -03:00
|
|
|
let state = V02State::new_with_genesis_accounts(&initial_data, &[]);
|
2025-11-24 17:09:30 +03:00
|
|
|
let expected_account = state.public_state.get(&account_id).unwrap();
|
2025-08-13 01:33:11 -03:00
|
|
|
|
2025-11-24 17:09:30 +03:00
|
|
|
let account = state.get_account_by_id(&account_id);
|
2025-08-13 01:33:11 -03:00
|
|
|
|
|
|
|
|
assert_eq!(&account, expected_account);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2025-11-24 17:09:30 +03:00
|
|
|
fn test_get_account_by_account_id_default_account() {
|
|
|
|
|
let addr2 = AccountId::new([0; 32]);
|
2025-10-16 16:24:18 -03:00
|
|
|
let state = V02State::new_with_genesis_accounts(&[], &[]);
|
2025-08-13 01:33:11 -03:00
|
|
|
let expected_account = Account::default();
|
|
|
|
|
|
2025-11-24 17:09:30 +03:00
|
|
|
let account = state.get_account_by_id(&addr2);
|
2025-08-13 01:33:11 -03:00
|
|
|
|
|
|
|
|
assert_eq!(account, expected_account);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_builtin_programs_getter() {
|
2025-10-16 16:24:18 -03:00
|
|
|
let state = V02State::new_with_genesis_accounts(&[], &[]);
|
2025-08-13 01:33:11 -03:00
|
|
|
|
2025-10-15 17:25:26 -03:00
|
|
|
let builtin_programs = state.programs();
|
2025-08-13 01:33:11 -03:00
|
|
|
|
2025-10-14 17:15:04 -03:00
|
|
|
assert_eq!(builtin_programs, &state.programs);
|
2025-08-13 01:33:11 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn transition_from_authenticated_transfer_program_invocation_default_account_destination() {
|
|
|
|
|
let key = PrivateKey::try_new([1; 32]).unwrap();
|
2025-11-24 17:09:30 +03:00
|
|
|
let account_id = AccountId::from(&PublicKey::new_from_private_key(&key));
|
|
|
|
|
let initial_data = [(account_id, 100)];
|
2025-10-16 16:24:18 -03:00
|
|
|
let mut state = V02State::new_with_genesis_accounts(&initial_data, &[]);
|
2025-11-24 17:09:30 +03:00
|
|
|
let from = account_id;
|
|
|
|
|
let to = AccountId::new([2; 32]);
|
|
|
|
|
assert_eq!(state.get_account_by_id(&to), Account::default());
|
2025-08-13 01:33:11 -03:00
|
|
|
let balance_to_move = 5;
|
|
|
|
|
|
2025-08-13 14:15:05 +03:00
|
|
|
let tx = transfer_transaction(from, key, 0, to, balance_to_move);
|
2025-08-13 01:33:11 -03:00
|
|
|
state.transition_from_public_transaction(&tx).unwrap();
|
|
|
|
|
|
2025-11-24 17:09:30 +03:00
|
|
|
assert_eq!(state.get_account_by_id(&from).balance, 95);
|
|
|
|
|
assert_eq!(state.get_account_by_id(&to).balance, 5);
|
|
|
|
|
assert_eq!(state.get_account_by_id(&from).nonce, 1);
|
|
|
|
|
assert_eq!(state.get_account_by_id(&to).nonce, 0);
|
2025-08-13 01:33:11 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn transition_from_authenticated_transfer_program_invocation_insuficient_balance() {
|
|
|
|
|
let key = PrivateKey::try_new([1; 32]).unwrap();
|
2025-11-24 17:09:30 +03:00
|
|
|
let account_id = AccountId::from(&PublicKey::new_from_private_key(&key));
|
|
|
|
|
let initial_data = [(account_id, 100)];
|
2025-10-16 16:24:18 -03:00
|
|
|
let mut state = V02State::new_with_genesis_accounts(&initial_data, &[]);
|
2025-11-24 17:09:30 +03:00
|
|
|
let from = account_id;
|
2025-08-13 01:33:11 -03:00
|
|
|
let from_key = key;
|
2025-11-24 17:09:30 +03:00
|
|
|
let to = AccountId::new([2; 32]);
|
2025-08-13 01:33:11 -03:00
|
|
|
let balance_to_move = 101;
|
2025-11-24 17:09:30 +03:00
|
|
|
assert!(state.get_account_by_id(&from).balance < balance_to_move);
|
2025-08-13 01:33:11 -03:00
|
|
|
|
2025-08-13 14:15:05 +03:00
|
|
|
let tx = transfer_transaction(from, from_key, 0, to, balance_to_move);
|
2025-08-13 01:33:11 -03:00
|
|
|
let result = state.transition_from_public_transaction(&tx);
|
|
|
|
|
|
|
|
|
|
assert!(matches!(result, Err(NssaError::ProgramExecutionFailed(_))));
|
2025-11-24 17:09:30 +03:00
|
|
|
assert_eq!(state.get_account_by_id(&from).balance, 100);
|
|
|
|
|
assert_eq!(state.get_account_by_id(&to).balance, 0);
|
|
|
|
|
assert_eq!(state.get_account_by_id(&from).nonce, 0);
|
|
|
|
|
assert_eq!(state.get_account_by_id(&to).nonce, 0);
|
2025-08-13 01:33:11 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn transition_from_authenticated_transfer_program_invocation_non_default_account_destination() {
|
|
|
|
|
let key1 = PrivateKey::try_new([1; 32]).unwrap();
|
|
|
|
|
let key2 = PrivateKey::try_new([2; 32]).unwrap();
|
2025-11-24 17:09:30 +03:00
|
|
|
let account_id1 = AccountId::from(&PublicKey::new_from_private_key(&key1));
|
|
|
|
|
let account_id2 = AccountId::from(&PublicKey::new_from_private_key(&key2));
|
|
|
|
|
let initial_data = [(account_id1, 100), (account_id2, 200)];
|
2025-10-16 16:24:18 -03:00
|
|
|
let mut state = V02State::new_with_genesis_accounts(&initial_data, &[]);
|
2025-11-24 17:09:30 +03:00
|
|
|
let from = account_id2;
|
2025-08-13 01:33:11 -03:00
|
|
|
let from_key = key2;
|
2025-11-24 17:09:30 +03:00
|
|
|
let to = account_id1;
|
|
|
|
|
assert_ne!(state.get_account_by_id(&to), Account::default());
|
2025-08-13 01:33:11 -03:00
|
|
|
let balance_to_move = 8;
|
|
|
|
|
|
2025-08-13 14:15:05 +03:00
|
|
|
let tx = transfer_transaction(from, from_key, 0, to, balance_to_move);
|
2025-08-13 01:33:11 -03:00
|
|
|
state.transition_from_public_transaction(&tx).unwrap();
|
|
|
|
|
|
2025-11-24 17:09:30 +03:00
|
|
|
assert_eq!(state.get_account_by_id(&from).balance, 192);
|
|
|
|
|
assert_eq!(state.get_account_by_id(&to).balance, 108);
|
|
|
|
|
assert_eq!(state.get_account_by_id(&from).nonce, 1);
|
|
|
|
|
assert_eq!(state.get_account_by_id(&to).nonce, 0);
|
2025-08-13 01:33:11 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2025-10-30 10:52:31 -03:00
|
|
|
fn transition_from_sequence_of_authenticated_transfer_program_invocations() {
|
2025-08-13 01:33:11 -03:00
|
|
|
let key1 = PrivateKey::try_new([8; 32]).unwrap();
|
2025-11-24 17:09:30 +03:00
|
|
|
let account_id1 = AccountId::from(&PublicKey::new_from_private_key(&key1));
|
2025-08-13 01:33:11 -03:00
|
|
|
let key2 = PrivateKey::try_new([2; 32]).unwrap();
|
2025-11-24 17:09:30 +03:00
|
|
|
let account_id2 = AccountId::from(&PublicKey::new_from_private_key(&key2));
|
|
|
|
|
let initial_data = [(account_id1, 100)];
|
2025-10-16 16:24:18 -03:00
|
|
|
let mut state = V02State::new_with_genesis_accounts(&initial_data, &[]);
|
2025-11-24 17:09:30 +03:00
|
|
|
let account_id3 = AccountId::new([3; 32]);
|
2025-08-13 01:33:11 -03:00
|
|
|
let balance_to_move = 5;
|
|
|
|
|
|
2025-11-24 17:09:30 +03:00
|
|
|
let tx = transfer_transaction(account_id1, key1, 0, account_id2, balance_to_move);
|
2025-08-13 01:33:11 -03:00
|
|
|
state.transition_from_public_transaction(&tx).unwrap();
|
|
|
|
|
let balance_to_move = 3;
|
2025-11-24 17:09:30 +03:00
|
|
|
let tx = transfer_transaction(account_id2, key2, 0, account_id3, balance_to_move);
|
2025-08-13 01:33:11 -03:00
|
|
|
state.transition_from_public_transaction(&tx).unwrap();
|
|
|
|
|
|
2025-11-24 17:09:30 +03:00
|
|
|
assert_eq!(state.get_account_by_id(&account_id1).balance, 95);
|
|
|
|
|
assert_eq!(state.get_account_by_id(&account_id2).balance, 2);
|
|
|
|
|
assert_eq!(state.get_account_by_id(&account_id3).balance, 3);
|
|
|
|
|
assert_eq!(state.get_account_by_id(&account_id1).nonce, 1);
|
|
|
|
|
assert_eq!(state.get_account_by_id(&account_id2).nonce, 1);
|
|
|
|
|
assert_eq!(state.get_account_by_id(&account_id3).nonce, 0);
|
2025-08-13 01:33:11 -03:00
|
|
|
}
|
|
|
|
|
|
2025-10-16 16:24:18 -03:00
|
|
|
impl V02State {
|
2025-11-24 17:09:30 +03:00
|
|
|
pub fn force_insert_account(&mut self, account_id: AccountId, account: Account) {
|
|
|
|
|
self.public_state.insert(account_id, account);
|
2025-08-13 01:55:56 -03:00
|
|
|
}
|
|
|
|
|
|
2025-08-13 01:33:11 -03:00
|
|
|
/// Include test programs in the builtin programs map
|
|
|
|
|
pub fn with_test_programs(mut self) -> Self {
|
|
|
|
|
self.insert_program(Program::nonce_changer_program());
|
|
|
|
|
self.insert_program(Program::extra_output_program());
|
|
|
|
|
self.insert_program(Program::missing_output_program());
|
|
|
|
|
self.insert_program(Program::program_owner_changer());
|
|
|
|
|
self.insert_program(Program::simple_balance_transfer());
|
|
|
|
|
self.insert_program(Program::data_changer());
|
|
|
|
|
self.insert_program(Program::minter());
|
|
|
|
|
self.insert_program(Program::burner());
|
2025-10-29 15:34:11 -03:00
|
|
|
self.insert_program(Program::chain_caller());
|
2025-11-20 21:02:18 -05:00
|
|
|
self.insert_program(Program::amm());
|
2025-08-13 01:33:11 -03:00
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn with_non_default_accounts_but_default_program_owners(mut self) -> Self {
|
|
|
|
|
let account_with_default_values_except_balance = Account {
|
|
|
|
|
balance: 100,
|
|
|
|
|
..Account::default()
|
|
|
|
|
};
|
|
|
|
|
let account_with_default_values_except_nonce = Account {
|
|
|
|
|
nonce: 37,
|
|
|
|
|
..Account::default()
|
|
|
|
|
};
|
|
|
|
|
let account_with_default_values_except_data = Account {
|
|
|
|
|
data: vec![0xca, 0xfe],
|
|
|
|
|
..Account::default()
|
|
|
|
|
};
|
|
|
|
|
self.force_insert_account(
|
2025-11-24 17:09:30 +03:00
|
|
|
AccountId::new([255; 32]),
|
2025-08-13 01:33:11 -03:00
|
|
|
account_with_default_values_except_balance,
|
|
|
|
|
);
|
|
|
|
|
self.force_insert_account(
|
2025-11-24 17:09:30 +03:00
|
|
|
AccountId::new([254; 32]),
|
2025-08-13 01:33:11 -03:00
|
|
|
account_with_default_values_except_nonce,
|
|
|
|
|
);
|
|
|
|
|
self.force_insert_account(
|
2025-11-24 17:09:30 +03:00
|
|
|
AccountId::new([253; 32]),
|
2025-08-13 01:33:11 -03:00
|
|
|
account_with_default_values_except_data,
|
|
|
|
|
);
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn with_account_owned_by_burner_program(mut self) -> Self {
|
|
|
|
|
let account = Account {
|
|
|
|
|
program_owner: Program::burner().id(),
|
|
|
|
|
balance: 100,
|
|
|
|
|
..Default::default()
|
|
|
|
|
};
|
2025-11-24 17:09:30 +03:00
|
|
|
self.force_insert_account(AccountId::new([252; 32]), account);
|
2025-08-13 01:33:11 -03:00
|
|
|
self
|
|
|
|
|
}
|
2025-08-22 10:34:09 -03:00
|
|
|
|
2025-08-22 12:29:45 -03:00
|
|
|
pub fn with_private_account(mut self, keys: &TestPrivateKeys, account: &Account) -> Self {
|
2025-08-27 16:24:20 -03:00
|
|
|
let commitment = Commitment::new(&keys.npk(), account);
|
2025-08-22 10:34:09 -03:00
|
|
|
self.private_state.0.extend(&[commitment]);
|
2025-08-22 12:29:45 -03:00
|
|
|
self
|
2025-08-22 10:34:09 -03:00
|
|
|
}
|
2025-08-13 01:33:11 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_program_should_fail_if_modifies_nonces() {
|
2025-11-24 17:09:30 +03:00
|
|
|
let initial_data = [(AccountId::new([1; 32]), 100)];
|
2025-09-18 15:59:17 +03:00
|
|
|
let mut state =
|
2025-10-16 16:24:18 -03:00
|
|
|
V02State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs();
|
2025-11-24 17:09:30 +03:00
|
|
|
let account_ids = vec![AccountId::new([1; 32])];
|
2025-08-13 01:33:11 -03:00
|
|
|
let program_id = Program::nonce_changer_program().id();
|
|
|
|
|
let message =
|
2025-11-24 17:09:30 +03:00
|
|
|
public_transaction::Message::try_new(program_id, account_ids, vec![], ()).unwrap();
|
2025-08-13 01:33:11 -03:00
|
|
|
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
|
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
|
|
|
|
|
let result = state.transition_from_public_transaction(&tx);
|
|
|
|
|
|
|
|
|
|
assert!(matches!(result, Err(NssaError::InvalidProgramBehavior)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_program_should_fail_if_output_accounts_exceed_inputs() {
|
2025-11-24 17:09:30 +03:00
|
|
|
let initial_data = [(AccountId::new([1; 32]), 100)];
|
2025-09-18 15:59:17 +03:00
|
|
|
let mut state =
|
2025-10-16 16:24:18 -03:00
|
|
|
V02State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs();
|
2025-11-24 17:09:30 +03:00
|
|
|
let account_ids = vec![AccountId::new([1; 32])];
|
2025-08-13 01:33:11 -03:00
|
|
|
let program_id = Program::extra_output_program().id();
|
|
|
|
|
let message =
|
2025-11-24 17:09:30 +03:00
|
|
|
public_transaction::Message::try_new(program_id, account_ids, vec![], ()).unwrap();
|
2025-08-13 01:33:11 -03:00
|
|
|
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
|
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
|
|
|
|
|
let result = state.transition_from_public_transaction(&tx);
|
|
|
|
|
|
|
|
|
|
assert!(matches!(result, Err(NssaError::InvalidProgramBehavior)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_program_should_fail_with_missing_output_accounts() {
|
2025-11-24 17:09:30 +03:00
|
|
|
let initial_data = [(AccountId::new([1; 32]), 100)];
|
2025-09-18 15:59:17 +03:00
|
|
|
let mut state =
|
2025-10-16 16:24:18 -03:00
|
|
|
V02State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs();
|
2025-11-24 17:09:30 +03:00
|
|
|
let account_ids = vec![AccountId::new([1; 32]), AccountId::new([2; 32])];
|
2025-08-13 01:33:11 -03:00
|
|
|
let program_id = Program::missing_output_program().id();
|
|
|
|
|
let message =
|
2025-11-24 17:09:30 +03:00
|
|
|
public_transaction::Message::try_new(program_id, account_ids, vec![], ()).unwrap();
|
2025-08-13 01:33:11 -03:00
|
|
|
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
|
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
|
|
|
|
|
let result = state.transition_from_public_transaction(&tx);
|
|
|
|
|
|
|
|
|
|
assert!(matches!(result, Err(NssaError::InvalidProgramBehavior)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_program_owner() {
|
2025-11-24 17:09:30 +03:00
|
|
|
let initial_data = [(AccountId::new([1; 32]), 0)];
|
2025-09-18 15:59:17 +03:00
|
|
|
let mut state =
|
2025-10-16 16:24:18 -03:00
|
|
|
V02State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs();
|
2025-11-24 17:09:30 +03:00
|
|
|
let account_id = AccountId::new([1; 32]);
|
|
|
|
|
let account = state.get_account_by_id(&account_id);
|
2025-11-26 00:27:20 +03:00
|
|
|
// Assert the target account only differs from the default account in the program owner
|
|
|
|
|
// field
|
2025-08-13 01:33:11 -03:00
|
|
|
assert_ne!(account.program_owner, Account::default().program_owner);
|
|
|
|
|
assert_eq!(account.balance, Account::default().balance);
|
|
|
|
|
assert_eq!(account.nonce, Account::default().nonce);
|
|
|
|
|
assert_eq!(account.data, Account::default().data);
|
|
|
|
|
let program_id = Program::program_owner_changer().id();
|
|
|
|
|
let message =
|
2025-11-24 17:09:30 +03:00
|
|
|
public_transaction::Message::try_new(program_id, vec![account_id], vec![], ()).unwrap();
|
2025-08-13 01:33:11 -03:00
|
|
|
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
|
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
|
|
|
|
|
let result = state.transition_from_public_transaction(&tx);
|
|
|
|
|
|
|
|
|
|
assert!(matches!(result, Err(NssaError::InvalidProgramBehavior)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_balance() {
|
|
|
|
|
let initial_data = [];
|
2025-10-16 16:24:18 -03:00
|
|
|
let mut state = V02State::new_with_genesis_accounts(&initial_data, &[])
|
2025-08-13 01:33:11 -03:00
|
|
|
.with_test_programs()
|
|
|
|
|
.with_non_default_accounts_but_default_program_owners();
|
2025-11-24 17:09:30 +03:00
|
|
|
let account_id = AccountId::new([255; 32]);
|
|
|
|
|
let account = state.get_account_by_id(&account_id);
|
2025-08-13 01:33:11 -03:00
|
|
|
// Assert the target account only differs from the default account in balance field
|
|
|
|
|
assert_eq!(account.program_owner, Account::default().program_owner);
|
|
|
|
|
assert_ne!(account.balance, Account::default().balance);
|
|
|
|
|
assert_eq!(account.nonce, Account::default().nonce);
|
|
|
|
|
assert_eq!(account.data, Account::default().data);
|
|
|
|
|
let program_id = Program::program_owner_changer().id();
|
|
|
|
|
let message =
|
2025-11-24 17:09:30 +03:00
|
|
|
public_transaction::Message::try_new(program_id, vec![account_id], vec![], ()).unwrap();
|
2025-08-13 01:33:11 -03:00
|
|
|
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
|
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
|
|
|
|
|
let result = state.transition_from_public_transaction(&tx);
|
|
|
|
|
|
|
|
|
|
assert!(matches!(result, Err(NssaError::InvalidProgramBehavior)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_nonce() {
|
|
|
|
|
let initial_data = [];
|
2025-10-16 16:24:18 -03:00
|
|
|
let mut state = V02State::new_with_genesis_accounts(&initial_data, &[])
|
2025-08-13 01:33:11 -03:00
|
|
|
.with_test_programs()
|
|
|
|
|
.with_non_default_accounts_but_default_program_owners();
|
2025-11-24 17:09:30 +03:00
|
|
|
let account_id = AccountId::new([254; 32]);
|
|
|
|
|
let account = state.get_account_by_id(&account_id);
|
2025-08-13 01:33:11 -03:00
|
|
|
// Assert the target account only differs from the default account in nonce field
|
|
|
|
|
assert_eq!(account.program_owner, Account::default().program_owner);
|
|
|
|
|
assert_eq!(account.balance, Account::default().balance);
|
|
|
|
|
assert_ne!(account.nonce, Account::default().nonce);
|
|
|
|
|
assert_eq!(account.data, Account::default().data);
|
|
|
|
|
let program_id = Program::program_owner_changer().id();
|
|
|
|
|
let message =
|
2025-11-24 17:09:30 +03:00
|
|
|
public_transaction::Message::try_new(program_id, vec![account_id], vec![], ()).unwrap();
|
2025-08-13 01:33:11 -03:00
|
|
|
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
|
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
|
|
|
|
|
let result = state.transition_from_public_transaction(&tx);
|
|
|
|
|
|
|
|
|
|
assert!(matches!(result, Err(NssaError::InvalidProgramBehavior)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_data() {
|
|
|
|
|
let initial_data = [];
|
2025-10-16 16:24:18 -03:00
|
|
|
let mut state = V02State::new_with_genesis_accounts(&initial_data, &[])
|
2025-08-13 01:33:11 -03:00
|
|
|
.with_test_programs()
|
|
|
|
|
.with_non_default_accounts_but_default_program_owners();
|
2025-11-24 17:09:30 +03:00
|
|
|
let account_id = AccountId::new([253; 32]);
|
|
|
|
|
let account = state.get_account_by_id(&account_id);
|
2025-08-13 01:33:11 -03:00
|
|
|
// Assert the target account only differs from the default account in data field
|
|
|
|
|
assert_eq!(account.program_owner, Account::default().program_owner);
|
|
|
|
|
assert_eq!(account.balance, Account::default().balance);
|
|
|
|
|
assert_eq!(account.nonce, Account::default().nonce);
|
|
|
|
|
assert_ne!(account.data, Account::default().data);
|
|
|
|
|
let program_id = Program::program_owner_changer().id();
|
|
|
|
|
let message =
|
2025-11-24 17:09:30 +03:00
|
|
|
public_transaction::Message::try_new(program_id, vec![account_id], vec![], ()).unwrap();
|
2025-08-13 01:33:11 -03:00
|
|
|
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
|
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
|
|
|
|
|
let result = state.transition_from_public_transaction(&tx);
|
|
|
|
|
|
|
|
|
|
assert!(matches!(result, Err(NssaError::InvalidProgramBehavior)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_program_should_fail_if_transfers_balance_from_non_owned_account() {
|
2025-11-24 17:09:30 +03:00
|
|
|
let initial_data = [(AccountId::new([1; 32]), 100)];
|
2025-09-18 15:59:17 +03:00
|
|
|
let mut state =
|
2025-10-16 16:24:18 -03:00
|
|
|
V02State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs();
|
2025-11-24 17:09:30 +03:00
|
|
|
let sender_account_id = AccountId::new([1; 32]);
|
|
|
|
|
let receiver_account_id = AccountId::new([2; 32]);
|
2025-08-13 01:33:11 -03:00
|
|
|
let balance_to_move: u128 = 1;
|
|
|
|
|
let program_id = Program::simple_balance_transfer().id();
|
|
|
|
|
assert_ne!(
|
2025-11-24 17:09:30 +03:00
|
|
|
state.get_account_by_id(&sender_account_id).program_owner,
|
2025-08-13 01:33:11 -03:00
|
|
|
program_id
|
|
|
|
|
);
|
|
|
|
|
let message = public_transaction::Message::try_new(
|
|
|
|
|
program_id,
|
2025-11-24 17:09:30 +03:00
|
|
|
vec![sender_account_id, receiver_account_id],
|
2025-08-13 01:33:11 -03:00
|
|
|
vec![],
|
|
|
|
|
balance_to_move,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
|
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
|
|
|
|
|
let result = state.transition_from_public_transaction(&tx);
|
|
|
|
|
|
|
|
|
|
assert!(matches!(result, Err(NssaError::InvalidProgramBehavior)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_program_should_fail_if_modifies_data_of_non_owned_account() {
|
|
|
|
|
let initial_data = [];
|
2025-10-16 16:24:18 -03:00
|
|
|
let mut state = V02State::new_with_genesis_accounts(&initial_data, &[])
|
2025-09-12 17:13:37 -03:00
|
|
|
.with_test_programs()
|
|
|
|
|
.with_non_default_accounts_but_default_program_owners();
|
2025-11-24 17:09:30 +03:00
|
|
|
let account_id = AccountId::new([255; 32]);
|
2025-08-13 01:33:11 -03:00
|
|
|
let program_id = Program::data_changer().id();
|
|
|
|
|
|
2025-11-24 17:09:30 +03:00
|
|
|
assert_ne!(state.get_account_by_id(&account_id), Account::default());
|
2025-08-13 01:33:11 -03:00
|
|
|
assert_ne!(
|
2025-11-24 17:09:30 +03:00
|
|
|
state.get_account_by_id(&account_id).program_owner,
|
2025-08-13 01:33:11 -03:00
|
|
|
program_id
|
|
|
|
|
);
|
|
|
|
|
let message =
|
2025-11-24 17:09:30 +03:00
|
|
|
public_transaction::Message::try_new(program_id, vec![account_id], vec![], ()).unwrap();
|
2025-08-13 01:33:11 -03:00
|
|
|
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
|
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
|
|
|
|
|
let result = state.transition_from_public_transaction(&tx);
|
|
|
|
|
|
|
|
|
|
assert!(matches!(result, Err(NssaError::InvalidProgramBehavior)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_program_should_fail_if_does_not_preserve_total_balance_by_minting() {
|
|
|
|
|
let initial_data = [];
|
2025-09-18 15:59:17 +03:00
|
|
|
let mut state =
|
2025-10-16 16:24:18 -03:00
|
|
|
V02State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs();
|
2025-11-24 17:09:30 +03:00
|
|
|
let account_id = AccountId::new([1; 32]);
|
2025-08-13 01:33:11 -03:00
|
|
|
let program_id = Program::minter().id();
|
|
|
|
|
|
|
|
|
|
let message =
|
2025-11-24 17:09:30 +03:00
|
|
|
public_transaction::Message::try_new(program_id, vec![account_id], vec![], ()).unwrap();
|
2025-08-13 01:33:11 -03:00
|
|
|
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
|
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
|
|
|
|
|
let result = state.transition_from_public_transaction(&tx);
|
|
|
|
|
|
|
|
|
|
assert!(matches!(result, Err(NssaError::InvalidProgramBehavior)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_program_should_fail_if_does_not_preserve_total_balance_by_burning() {
|
|
|
|
|
let initial_data = [];
|
2025-10-16 16:24:18 -03:00
|
|
|
let mut state = V02State::new_with_genesis_accounts(&initial_data, &[])
|
2025-08-13 01:33:11 -03:00
|
|
|
.with_test_programs()
|
|
|
|
|
.with_account_owned_by_burner_program();
|
|
|
|
|
let program_id = Program::burner().id();
|
2025-11-24 17:09:30 +03:00
|
|
|
let account_id = AccountId::new([252; 32]);
|
2025-08-13 01:33:11 -03:00
|
|
|
assert_eq!(
|
2025-11-24 17:09:30 +03:00
|
|
|
state.get_account_by_id(&account_id).program_owner,
|
2025-08-13 01:33:11 -03:00
|
|
|
program_id
|
|
|
|
|
);
|
|
|
|
|
let balance_to_burn: u128 = 1;
|
2025-11-24 17:09:30 +03:00
|
|
|
assert!(state.get_account_by_id(&account_id).balance > balance_to_burn);
|
2025-08-13 01:33:11 -03:00
|
|
|
|
|
|
|
|
let message = public_transaction::Message::try_new(
|
|
|
|
|
program_id,
|
2025-11-24 17:09:30 +03:00
|
|
|
vec![account_id],
|
2025-08-13 01:33:11 -03:00
|
|
|
vec![],
|
|
|
|
|
balance_to_burn,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
|
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
let result = state.transition_from_public_transaction(&tx);
|
|
|
|
|
|
|
|
|
|
assert!(matches!(result, Err(NssaError::InvalidProgramBehavior)));
|
|
|
|
|
}
|
2025-08-21 14:32:57 -03:00
|
|
|
|
2025-08-22 13:42:37 -03:00
|
|
|
pub struct TestPublicKeys {
|
|
|
|
|
pub signing_key: PrivateKey,
|
2025-08-22 12:29:45 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TestPublicKeys {
|
2025-11-24 17:09:30 +03:00
|
|
|
pub fn account_id(&self) -> AccountId {
|
|
|
|
|
AccountId::from(&PublicKey::new_from_private_key(&self.signing_key))
|
2025-08-22 12:29:45 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn test_public_account_keys_1() -> TestPublicKeys {
|
|
|
|
|
TestPublicKeys {
|
|
|
|
|
signing_key: PrivateKey::try_new([37; 32]).unwrap(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-22 13:42:37 -03:00
|
|
|
pub struct TestPrivateKeys {
|
|
|
|
|
pub nsk: NullifierSecretKey,
|
2025-09-15 14:04:49 +03:00
|
|
|
pub isk: Scalar,
|
2025-08-22 12:29:45 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TestPrivateKeys {
|
2025-08-22 13:42:37 -03:00
|
|
|
pub fn npk(&self) -> NullifierPublicKey {
|
2025-08-22 12:29:45 -03:00
|
|
|
NullifierPublicKey::from(&self.nsk)
|
|
|
|
|
}
|
2025-08-22 14:59:57 -03:00
|
|
|
|
|
|
|
|
pub fn ivk(&self) -> IncomingViewingPublicKey {
|
|
|
|
|
IncomingViewingPublicKey::from_scalar(self.isk)
|
|
|
|
|
}
|
2025-08-22 12:29:45 -03:00
|
|
|
}
|
|
|
|
|
|
2025-08-22 13:42:37 -03:00
|
|
|
pub fn test_private_account_keys_1() -> TestPrivateKeys {
|
2025-08-22 12:29:45 -03:00
|
|
|
TestPrivateKeys {
|
|
|
|
|
nsk: [13; 32],
|
2025-08-22 14:59:57 -03:00
|
|
|
isk: [31; 32],
|
2025-08-22 12:29:45 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-22 13:42:37 -03:00
|
|
|
pub fn test_private_account_keys_2() -> TestPrivateKeys {
|
2025-08-22 12:29:45 -03:00
|
|
|
TestPrivateKeys {
|
|
|
|
|
nsk: [38; 32],
|
2025-08-22 14:59:57 -03:00
|
|
|
isk: [83; 32],
|
2025-08-22 12:29:45 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-22 10:34:09 -03:00
|
|
|
fn shielded_balance_transfer_for_tests(
|
2025-08-22 12:29:45 -03:00
|
|
|
sender_keys: &TestPublicKeys,
|
|
|
|
|
recipient_keys: &TestPrivateKeys,
|
2025-08-22 10:34:09 -03:00
|
|
|
balance_to_move: u128,
|
2025-10-16 16:24:18 -03:00
|
|
|
state: &V02State,
|
2025-08-22 10:34:09 -03:00
|
|
|
) -> PrivacyPreservingTransaction {
|
2025-09-11 16:37:28 -03:00
|
|
|
let sender = AccountWithMetadata::new(
|
2025-11-24 17:09:30 +03:00
|
|
|
state.get_account_by_id(&sender_keys.account_id()),
|
2025-09-11 16:37:28 -03:00
|
|
|
true,
|
2025-11-24 17:09:30 +03:00
|
|
|
sender_keys.account_id(),
|
2025-09-11 16:37:28 -03:00
|
|
|
);
|
2025-08-25 14:51:46 -03:00
|
|
|
|
2025-08-22 10:34:09 -03:00
|
|
|
let sender_nonce = sender.account.nonce;
|
2025-08-21 15:52:35 -03:00
|
|
|
|
2025-09-11 16:37:28 -03:00
|
|
|
let recipient = AccountWithMetadata::new(Account::default(), false, &recipient_keys.npk());
|
2025-08-21 14:32:57 -03:00
|
|
|
|
2025-08-25 14:51:46 -03:00
|
|
|
let esk = [3; 32];
|
2025-08-26 14:53:02 -03:00
|
|
|
let shared_secret = SharedSecretKey::new(&esk, &recipient_keys.ivk());
|
2025-08-25 14:51:46 -03:00
|
|
|
let epk = EphemeralPublicKey::from_scalar(esk);
|
|
|
|
|
|
2025-08-22 10:34:09 -03:00
|
|
|
let (output, proof) = circuit::execute_and_prove(
|
2025-08-21 14:32:57 -03:00
|
|
|
&[sender, recipient],
|
|
|
|
|
&Program::serialize_instruction(balance_to_move).unwrap(),
|
|
|
|
|
&[0, 2],
|
|
|
|
|
&[0xdeadbeef],
|
2025-08-25 14:51:46 -03:00
|
|
|
&[(recipient_keys.npk(), shared_secret)],
|
2025-08-21 14:32:57 -03:00
|
|
|
&[],
|
|
|
|
|
&Program::authenticated_transfer_program(),
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
2025-08-25 14:51:46 -03:00
|
|
|
let message = Message::try_from_circuit_output(
|
2025-11-24 17:09:30 +03:00
|
|
|
vec![sender_keys.account_id()],
|
2025-08-22 10:34:09 -03:00
|
|
|
vec![sender_nonce],
|
2025-09-01 18:12:13 -03:00
|
|
|
vec![(recipient_keys.npk(), recipient_keys.ivk(), epk)],
|
2025-08-25 14:51:46 -03:00
|
|
|
output,
|
2025-08-26 14:53:02 -03:00
|
|
|
)
|
|
|
|
|
.unwrap();
|
2025-08-21 14:32:57 -03:00
|
|
|
|
2025-08-22 12:29:45 -03:00
|
|
|
let witness_set = WitnessSet::for_message(&message, proof, &[&sender_keys.signing_key]);
|
2025-08-22 10:34:09 -03:00
|
|
|
PrivacyPreservingTransaction::new(message, witness_set)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn private_balance_transfer_for_tests(
|
2025-08-22 12:29:45 -03:00
|
|
|
sender_keys: &TestPrivateKeys,
|
2025-08-22 10:34:09 -03:00
|
|
|
sender_private_account: &Account,
|
2025-08-22 12:29:45 -03:00
|
|
|
recipient_keys: &TestPrivateKeys,
|
2025-08-22 10:34:09 -03:00
|
|
|
balance_to_move: u128,
|
|
|
|
|
new_nonces: [Nonce; 2],
|
2025-10-16 16:24:18 -03:00
|
|
|
state: &V02State,
|
2025-08-22 10:34:09 -03:00
|
|
|
) -> PrivacyPreservingTransaction {
|
|
|
|
|
let program = Program::authenticated_transfer_program();
|
2025-08-27 16:24:20 -03:00
|
|
|
let sender_commitment = Commitment::new(&sender_keys.npk(), sender_private_account);
|
2025-09-11 16:37:28 -03:00
|
|
|
let sender_pre =
|
|
|
|
|
AccountWithMetadata::new(sender_private_account.clone(), true, &sender_keys.npk());
|
|
|
|
|
let recipient_pre =
|
|
|
|
|
AccountWithMetadata::new(Account::default(), false, &recipient_keys.npk());
|
2025-08-21 14:32:57 -03:00
|
|
|
|
2025-08-25 14:51:46 -03:00
|
|
|
let esk_1 = [3; 32];
|
2025-08-26 14:53:02 -03:00
|
|
|
let shared_secret_1 = SharedSecretKey::new(&esk_1, &sender_keys.ivk());
|
2025-08-25 14:51:46 -03:00
|
|
|
let epk_1 = EphemeralPublicKey::from_scalar(esk_1);
|
|
|
|
|
|
|
|
|
|
let esk_2 = [3; 32];
|
2025-08-26 14:53:02 -03:00
|
|
|
let shared_secret_2 = SharedSecretKey::new(&esk_2, &recipient_keys.ivk());
|
2025-08-25 14:51:46 -03:00
|
|
|
let epk_2 = EphemeralPublicKey::from_scalar(esk_2);
|
|
|
|
|
|
2025-08-22 10:34:09 -03:00
|
|
|
let (output, proof) = circuit::execute_and_prove(
|
|
|
|
|
&[sender_pre, recipient_pre],
|
|
|
|
|
&Program::serialize_instruction(balance_to_move).unwrap(),
|
|
|
|
|
&[1, 2],
|
|
|
|
|
&new_nonces,
|
|
|
|
|
&[
|
2025-08-25 14:51:46 -03:00
|
|
|
(sender_keys.npk(), shared_secret_1),
|
|
|
|
|
(recipient_keys.npk(), shared_secret_2),
|
2025-08-22 10:34:09 -03:00
|
|
|
],
|
|
|
|
|
&[(
|
2025-08-22 12:29:45 -03:00
|
|
|
sender_keys.nsk,
|
2025-08-27 16:24:20 -03:00
|
|
|
state.get_proof_for_commitment(&sender_commitment).unwrap(),
|
2025-08-22 10:34:09 -03:00
|
|
|
)],
|
|
|
|
|
&program,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
2025-09-01 18:12:13 -03:00
|
|
|
let message = Message::try_from_circuit_output(
|
|
|
|
|
vec![],
|
|
|
|
|
vec![],
|
|
|
|
|
vec![
|
|
|
|
|
(sender_keys.npk(), sender_keys.ivk(), epk_1),
|
|
|
|
|
(recipient_keys.npk(), recipient_keys.ivk(), epk_2),
|
|
|
|
|
],
|
|
|
|
|
output,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
2025-08-22 10:34:09 -03:00
|
|
|
|
|
|
|
|
let witness_set = WitnessSet::for_message(&message, proof, &[]);
|
|
|
|
|
|
|
|
|
|
PrivacyPreservingTransaction::new(message, witness_set)
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-22 11:03:10 -03:00
|
|
|
fn deshielded_balance_transfer_for_tests(
|
2025-08-22 12:29:45 -03:00
|
|
|
sender_keys: &TestPrivateKeys,
|
2025-08-22 11:03:10 -03:00
|
|
|
sender_private_account: &Account,
|
2025-11-24 17:09:30 +03:00
|
|
|
recipient_account_id: &AccountId,
|
2025-08-22 11:03:10 -03:00
|
|
|
balance_to_move: u128,
|
|
|
|
|
new_nonce: Nonce,
|
2025-10-16 16:24:18 -03:00
|
|
|
state: &V02State,
|
2025-08-22 11:03:10 -03:00
|
|
|
) -> PrivacyPreservingTransaction {
|
|
|
|
|
let program = Program::authenticated_transfer_program();
|
2025-08-27 16:24:20 -03:00
|
|
|
let sender_commitment = Commitment::new(&sender_keys.npk(), sender_private_account);
|
2025-09-11 16:37:28 -03:00
|
|
|
let sender_pre =
|
|
|
|
|
AccountWithMetadata::new(sender_private_account.clone(), true, &sender_keys.npk());
|
|
|
|
|
let recipient_pre = AccountWithMetadata::new(
|
2025-11-24 17:09:30 +03:00
|
|
|
state.get_account_by_id(recipient_account_id),
|
2025-09-11 16:37:28 -03:00
|
|
|
false,
|
2025-11-24 17:09:30 +03:00
|
|
|
*recipient_account_id,
|
2025-09-11 16:37:28 -03:00
|
|
|
);
|
2025-08-22 11:03:10 -03:00
|
|
|
|
2025-08-25 14:51:46 -03:00
|
|
|
let esk = [3; 32];
|
2025-08-26 14:53:02 -03:00
|
|
|
let shared_secret = SharedSecretKey::new(&esk, &sender_keys.ivk());
|
2025-08-25 14:51:46 -03:00
|
|
|
let epk = EphemeralPublicKey::from_scalar(esk);
|
|
|
|
|
|
2025-08-22 11:03:10 -03:00
|
|
|
let (output, proof) = circuit::execute_and_prove(
|
|
|
|
|
&[sender_pre, recipient_pre],
|
|
|
|
|
&Program::serialize_instruction(balance_to_move).unwrap(),
|
|
|
|
|
&[1, 0],
|
|
|
|
|
&[new_nonce],
|
2025-08-25 14:51:46 -03:00
|
|
|
&[(sender_keys.npk(), shared_secret)],
|
2025-08-22 11:03:10 -03:00
|
|
|
&[(
|
2025-08-22 12:29:45 -03:00
|
|
|
sender_keys.nsk,
|
2025-08-27 16:24:20 -03:00
|
|
|
state.get_proof_for_commitment(&sender_commitment).unwrap(),
|
2025-08-22 11:03:10 -03:00
|
|
|
)],
|
|
|
|
|
&program,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
2025-09-01 18:12:13 -03:00
|
|
|
let message = Message::try_from_circuit_output(
|
2025-11-24 17:09:30 +03:00
|
|
|
vec![*recipient_account_id],
|
2025-09-01 18:12:13 -03:00
|
|
|
vec![],
|
|
|
|
|
vec![(sender_keys.npk(), sender_keys.ivk(), epk)],
|
|
|
|
|
output,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
2025-08-22 11:03:10 -03:00
|
|
|
|
|
|
|
|
let witness_set = WitnessSet::for_message(&message, proof, &[]);
|
|
|
|
|
|
|
|
|
|
PrivacyPreservingTransaction::new(message, witness_set)
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-22 10:34:09 -03:00
|
|
|
#[test]
|
|
|
|
|
fn test_transition_from_privacy_preserving_transaction_shielded() {
|
2025-08-22 12:29:45 -03:00
|
|
|
let sender_keys = test_public_account_keys_1();
|
|
|
|
|
let recipient_keys = test_private_account_keys_1();
|
|
|
|
|
|
2025-11-24 17:09:30 +03:00
|
|
|
let mut state =
|
|
|
|
|
V02State::new_with_genesis_accounts(&[(sender_keys.account_id(), 200)], &[]);
|
2025-08-22 12:29:45 -03:00
|
|
|
|
|
|
|
|
let balance_to_move = 37;
|
2025-08-22 10:34:09 -03:00
|
|
|
|
|
|
|
|
let tx = shielded_balance_transfer_for_tests(
|
2025-08-22 12:29:45 -03:00
|
|
|
&sender_keys,
|
|
|
|
|
&recipient_keys,
|
|
|
|
|
balance_to_move,
|
2025-08-22 10:34:09 -03:00
|
|
|
&state,
|
|
|
|
|
);
|
|
|
|
|
|
2025-09-03 15:20:40 -03:00
|
|
|
let expected_sender_post = {
|
2025-11-24 17:09:30 +03:00
|
|
|
let mut this = state.get_account_by_id(&sender_keys.account_id());
|
2025-09-03 15:20:40 -03:00
|
|
|
this.balance -= balance_to_move;
|
|
|
|
|
this.nonce += 1;
|
|
|
|
|
this
|
|
|
|
|
};
|
|
|
|
|
|
2025-08-22 10:34:09 -03:00
|
|
|
let [expected_new_commitment] = tx.message().new_commitments.clone().try_into().unwrap();
|
|
|
|
|
assert!(!state.private_state.0.contains(&expected_new_commitment));
|
2025-08-21 14:32:57 -03:00
|
|
|
|
|
|
|
|
state
|
|
|
|
|
.transition_from_privacy_preserving_transaction(&tx)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
2025-11-24 17:09:30 +03:00
|
|
|
let sender_post = state.get_account_by_id(&sender_keys.account_id());
|
2025-09-03 15:20:40 -03:00
|
|
|
assert_eq!(sender_post, expected_sender_post);
|
2025-08-22 10:34:09 -03:00
|
|
|
assert!(state.private_state.0.contains(&expected_new_commitment));
|
|
|
|
|
|
2025-08-21 14:32:57 -03:00
|
|
|
assert_eq!(
|
2025-11-24 17:09:30 +03:00
|
|
|
state.get_account_by_id(&sender_keys.account_id()).balance,
|
2025-08-22 12:29:45 -03:00
|
|
|
200 - balance_to_move
|
2025-08-21 14:32:57 -03:00
|
|
|
);
|
|
|
|
|
}
|
2025-08-22 10:34:09 -03:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_transition_from_privacy_preserving_transaction_private() {
|
2025-08-22 12:29:45 -03:00
|
|
|
let sender_keys = test_private_account_keys_1();
|
|
|
|
|
let sender_private_account = Account {
|
|
|
|
|
program_owner: Program::authenticated_transfer_program().id(),
|
|
|
|
|
balance: 100,
|
|
|
|
|
nonce: 0xdeadbeef,
|
|
|
|
|
data: vec![],
|
|
|
|
|
};
|
|
|
|
|
let recipient_keys = test_private_account_keys_2();
|
|
|
|
|
|
2025-10-16 16:24:18 -03:00
|
|
|
let mut state = V02State::new_with_genesis_accounts(&[], &[])
|
2025-08-22 12:29:45 -03:00
|
|
|
.with_private_account(&sender_keys, &sender_private_account);
|
|
|
|
|
|
2025-08-22 10:34:09 -03:00
|
|
|
let balance_to_move = 37;
|
|
|
|
|
|
|
|
|
|
let tx = private_balance_transfer_for_tests(
|
2025-08-22 12:29:45 -03:00
|
|
|
&sender_keys,
|
2025-08-22 10:34:09 -03:00
|
|
|
&sender_private_account,
|
2025-08-22 12:29:45 -03:00
|
|
|
&recipient_keys,
|
2025-08-22 10:34:09 -03:00
|
|
|
balance_to_move,
|
2025-08-22 12:29:45 -03:00
|
|
|
[0xcafecafe, 0xfecafeca],
|
2025-08-22 10:34:09 -03:00
|
|
|
&state,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let expected_new_commitment_1 = Commitment::new(
|
2025-08-22 12:29:45 -03:00
|
|
|
&sender_keys.npk(),
|
2025-08-22 10:34:09 -03:00
|
|
|
&Account {
|
|
|
|
|
program_owner: Program::authenticated_transfer_program().id(),
|
2025-08-22 12:29:45 -03:00
|
|
|
nonce: 0xcafecafe,
|
2025-08-22 10:34:09 -03:00
|
|
|
balance: sender_private_account.balance - balance_to_move,
|
2025-08-22 12:29:45 -03:00
|
|
|
data: vec![],
|
2025-08-22 10:34:09 -03:00
|
|
|
},
|
|
|
|
|
);
|
2025-08-22 12:29:45 -03:00
|
|
|
|
|
|
|
|
let sender_pre_commitment = Commitment::new(&sender_keys.npk(), &sender_private_account);
|
2025-10-03 20:44:29 -03:00
|
|
|
let expected_new_nullifier =
|
|
|
|
|
Nullifier::for_account_update(&sender_pre_commitment, &sender_keys.nsk);
|
2025-08-22 10:34:09 -03:00
|
|
|
|
|
|
|
|
let expected_new_commitment_2 = Commitment::new(
|
2025-08-22 12:29:45 -03:00
|
|
|
&recipient_keys.npk(),
|
2025-08-22 10:34:09 -03:00
|
|
|
&Account {
|
|
|
|
|
program_owner: Program::authenticated_transfer_program().id(),
|
2025-08-22 12:29:45 -03:00
|
|
|
nonce: 0xfecafeca,
|
2025-08-22 10:34:09 -03:00
|
|
|
balance: balance_to_move,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let previous_public_state = state.public_state.clone();
|
|
|
|
|
assert!(state.private_state.0.contains(&sender_pre_commitment));
|
|
|
|
|
assert!(!state.private_state.0.contains(&expected_new_commitment_1));
|
|
|
|
|
assert!(!state.private_state.0.contains(&expected_new_commitment_2));
|
|
|
|
|
assert!(!state.private_state.1.contains(&expected_new_nullifier));
|
2025-08-22 11:03:10 -03:00
|
|
|
|
2025-08-22 10:34:09 -03:00
|
|
|
state
|
|
|
|
|
.transition_from_privacy_preserving_transaction(&tx)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
assert_eq!(state.public_state, previous_public_state);
|
|
|
|
|
assert!(state.private_state.0.contains(&sender_pre_commitment));
|
|
|
|
|
assert!(state.private_state.0.contains(&expected_new_commitment_1));
|
|
|
|
|
assert!(state.private_state.0.contains(&expected_new_commitment_2));
|
|
|
|
|
assert!(state.private_state.1.contains(&expected_new_nullifier));
|
|
|
|
|
}
|
2025-08-22 11:03:10 -03:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_transition_from_privacy_preserving_transaction_deshielded() {
|
2025-08-22 12:29:45 -03:00
|
|
|
let sender_keys = test_private_account_keys_1();
|
|
|
|
|
let sender_private_account = Account {
|
|
|
|
|
program_owner: Program::authenticated_transfer_program().id(),
|
|
|
|
|
balance: 100,
|
|
|
|
|
nonce: 0xdeadbeef,
|
|
|
|
|
data: vec![],
|
|
|
|
|
};
|
|
|
|
|
let recipient_keys = test_public_account_keys_1();
|
2025-08-22 11:03:10 -03:00
|
|
|
let recipient_initial_balance = 400;
|
2025-10-16 16:24:18 -03:00
|
|
|
let mut state = V02State::new_with_genesis_accounts(
|
2025-11-24 17:09:30 +03:00
|
|
|
&[(recipient_keys.account_id(), recipient_initial_balance)],
|
2025-09-18 15:59:17 +03:00
|
|
|
&[],
|
|
|
|
|
)
|
2025-08-22 12:29:45 -03:00
|
|
|
.with_private_account(&sender_keys, &sender_private_account);
|
2025-08-22 11:03:10 -03:00
|
|
|
|
|
|
|
|
let balance_to_move = 37;
|
|
|
|
|
|
2025-09-03 15:20:40 -03:00
|
|
|
let expected_recipient_post = {
|
2025-11-24 17:09:30 +03:00
|
|
|
let mut this = state.get_account_by_id(&recipient_keys.account_id());
|
2025-09-03 15:20:40 -03:00
|
|
|
this.balance += balance_to_move;
|
|
|
|
|
this
|
|
|
|
|
};
|
|
|
|
|
|
2025-08-22 11:03:10 -03:00
|
|
|
let tx = deshielded_balance_transfer_for_tests(
|
2025-08-22 12:29:45 -03:00
|
|
|
&sender_keys,
|
2025-08-22 11:03:10 -03:00
|
|
|
&sender_private_account,
|
2025-11-24 17:09:30 +03:00
|
|
|
&recipient_keys.account_id(),
|
2025-08-22 11:03:10 -03:00
|
|
|
balance_to_move,
|
2025-08-22 12:29:45 -03:00
|
|
|
0xcafecafe,
|
2025-08-22 11:03:10 -03:00
|
|
|
&state,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let expected_new_commitment = Commitment::new(
|
2025-08-22 12:29:45 -03:00
|
|
|
&sender_keys.npk(),
|
2025-08-22 11:03:10 -03:00
|
|
|
&Account {
|
|
|
|
|
program_owner: Program::authenticated_transfer_program().id(),
|
2025-08-22 12:29:45 -03:00
|
|
|
nonce: 0xcafecafe,
|
2025-08-22 11:03:10 -03:00
|
|
|
balance: sender_private_account.balance - balance_to_move,
|
2025-08-22 12:29:45 -03:00
|
|
|
data: vec![],
|
2025-08-22 11:03:10 -03:00
|
|
|
},
|
|
|
|
|
);
|
2025-08-22 12:29:45 -03:00
|
|
|
|
|
|
|
|
let sender_pre_commitment = Commitment::new(&sender_keys.npk(), &sender_private_account);
|
2025-10-03 20:44:29 -03:00
|
|
|
let expected_new_nullifier =
|
|
|
|
|
Nullifier::for_account_update(&sender_pre_commitment, &sender_keys.nsk);
|
2025-08-22 11:03:10 -03:00
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
|
|
|
|
|
state
|
|
|
|
|
.transition_from_privacy_preserving_transaction(&tx)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
2025-11-24 17:09:30 +03:00
|
|
|
let recipient_post = state.get_account_by_id(&recipient_keys.account_id());
|
2025-09-03 15:20:40 -03:00
|
|
|
assert_eq!(recipient_post, expected_recipient_post);
|
2025-08-22 11:03:10 -03:00
|
|
|
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));
|
|
|
|
|
assert_eq!(
|
2025-08-22 12:29:45 -03:00
|
|
|
state
|
2025-11-24 17:09:30 +03:00
|
|
|
.get_account_by_id(&recipient_keys.account_id())
|
2025-08-22 12:29:45 -03:00
|
|
|
.balance,
|
2025-08-22 11:03:10 -03:00
|
|
|
recipient_initial_balance + balance_to_move
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-09-02 12:38:31 -03:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_burner_program_should_fail_in_privacy_preserving_circuit() {
|
2025-09-02 14:09:50 -03:00
|
|
|
let program = Program::burner();
|
2025-09-18 17:09:02 -03:00
|
|
|
let public_account = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-02 14:09:50 -03:00
|
|
|
program_owner: program.id(),
|
2025-09-02 12:38:31 -03:00
|
|
|
balance: 100,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
true,
|
|
|
|
|
AccountId::new([0; 32]),
|
|
|
|
|
);
|
2025-09-02 12:38:31 -03:00
|
|
|
|
2025-09-02 14:09:50 -03:00
|
|
|
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();
|
2025-09-18 17:09:02 -03:00
|
|
|
let public_account = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-02 14:09:50 -03:00
|
|
|
program_owner: program.id(),
|
|
|
|
|
balance: 0,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
true,
|
|
|
|
|
AccountId::new([0; 32]),
|
|
|
|
|
);
|
2025-09-02 12:38:31 -03:00
|
|
|
|
|
|
|
|
let result = execute_and_prove(
|
2025-09-02 14:09:50 -03:00
|
|
|
&[public_account],
|
2025-09-02 12:38:31 -03:00
|
|
|
&Program::serialize_instruction(10u128).unwrap(),
|
2025-09-02 14:09:50 -03:00
|
|
|
&[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();
|
2025-09-18 17:09:02 -03:00
|
|
|
let public_account = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-02 14:09:50 -03:00
|
|
|
program_owner: program.id(),
|
|
|
|
|
balance: 0,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
true,
|
|
|
|
|
AccountId::new([0; 32]),
|
|
|
|
|
);
|
2025-09-02 14:09:50 -03:00
|
|
|
|
|
|
|
|
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();
|
2025-09-18 17:09:02 -03:00
|
|
|
let public_account = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-02 14:09:50 -03:00
|
|
|
program_owner: [0, 1, 2, 3, 4, 5, 6, 7],
|
|
|
|
|
balance: 0,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
true,
|
|
|
|
|
AccountId::new([0; 32]),
|
|
|
|
|
);
|
2025-09-02 14:09:50 -03:00
|
|
|
|
|
|
|
|
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();
|
2025-09-18 17:09:02 -03:00
|
|
|
let public_account = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-02 14:09:50 -03:00
|
|
|
program_owner: program.id(),
|
|
|
|
|
balance: 0,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
true,
|
|
|
|
|
AccountId::new([0; 32]),
|
|
|
|
|
);
|
2025-09-02 14:09:50 -03:00
|
|
|
|
|
|
|
|
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();
|
2025-09-18 17:09:02 -03:00
|
|
|
let public_account_1 = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-02 14:09:50 -03:00
|
|
|
program_owner: program.id(),
|
|
|
|
|
balance: 0,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
true,
|
|
|
|
|
AccountId::new([0; 32]),
|
|
|
|
|
);
|
|
|
|
|
let public_account_2 = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-02 14:09:50 -03:00
|
|
|
program_owner: program.id(),
|
|
|
|
|
balance: 0,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
true,
|
|
|
|
|
AccountId::new([1; 32]),
|
|
|
|
|
);
|
2025-09-02 14:09:50 -03:00
|
|
|
|
|
|
|
|
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();
|
2025-09-18 17:09:02 -03:00
|
|
|
let public_account = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-02 14:09:50 -03:00
|
|
|
program_owner: program.id(),
|
|
|
|
|
balance: 0,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
true,
|
|
|
|
|
AccountId::new([0; 32]),
|
|
|
|
|
);
|
2025-09-02 14:09:50 -03:00
|
|
|
|
|
|
|
|
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();
|
2025-09-18 17:09:02 -03:00
|
|
|
let public_account_1 = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-02 14:09:50 -03:00
|
|
|
program_owner: [0, 1, 2, 3, 4, 5, 6, 7],
|
|
|
|
|
balance: 100,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
true,
|
|
|
|
|
AccountId::new([0; 32]),
|
|
|
|
|
);
|
|
|
|
|
let public_account_2 = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-02 14:09:50 -03:00
|
|
|
program_owner: program.id(),
|
|
|
|
|
balance: 0,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
true,
|
|
|
|
|
AccountId::new([1; 32]),
|
|
|
|
|
);
|
2025-09-02 14:09:50 -03:00
|
|
|
|
|
|
|
|
let result = execute_and_prove(
|
|
|
|
|
&[public_account_1, public_account_2],
|
|
|
|
|
&Program::serialize_instruction(10u128).unwrap(),
|
|
|
|
|
&[0, 0],
|
|
|
|
|
&[],
|
|
|
|
|
&[],
|
|
|
|
|
&[],
|
2025-09-02 12:38:31 -03:00
|
|
|
&program,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
|
|
|
|
|
}
|
2025-09-02 14:50:16 -03:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_circuit_fails_if_visibility_masks_have_incorrect_lenght() {
|
|
|
|
|
let program = Program::simple_balance_transfer();
|
2025-09-18 17:09:02 -03:00
|
|
|
let public_account_1 = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-02 14:50:16 -03:00
|
|
|
program_owner: program.id(),
|
|
|
|
|
balance: 100,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
true,
|
|
|
|
|
AccountId::new([0; 32]),
|
|
|
|
|
);
|
|
|
|
|
let public_account_2 = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-02 14:50:16 -03:00
|
|
|
program_owner: program.id(),
|
|
|
|
|
balance: 0,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
true,
|
|
|
|
|
AccountId::new([1; 32]),
|
|
|
|
|
);
|
2025-09-02 14:50:16 -03:00
|
|
|
|
|
|
|
|
// 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(_))));
|
|
|
|
|
}
|
2025-09-03 16:25:02 -03:00
|
|
|
|
|
|
|
|
#[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();
|
2025-09-18 17:09:02 -03:00
|
|
|
let private_account_1 = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-03 16:25:02 -03:00
|
|
|
program_owner: program.id(),
|
|
|
|
|
balance: 100,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
true,
|
2025-10-03 18:31:56 -03:00
|
|
|
&sender_keys.npk(),
|
2025-09-18 17:09:02 -03:00
|
|
|
);
|
|
|
|
|
let private_account_2 =
|
2025-10-03 18:31:56 -03:00
|
|
|
AccountWithMetadata::new(Account::default(), false, &recipient_keys.npk());
|
2025-09-03 16:25:02 -03:00
|
|
|
|
|
|
|
|
// 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();
|
2025-09-18 17:09:02 -03:00
|
|
|
let private_account_1 = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-03 16:25:02 -03:00
|
|
|
program_owner: program.id(),
|
|
|
|
|
balance: 100,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
true,
|
2025-10-03 18:31:56 -03:00
|
|
|
&sender_keys.npk(),
|
2025-09-18 17:09:02 -03:00
|
|
|
);
|
|
|
|
|
let private_account_2 =
|
|
|
|
|
AccountWithMetadata::new(Account::default(), false, AccountId::new([1; 32]));
|
2025-09-03 16:25:02 -03:00
|
|
|
|
|
|
|
|
// 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();
|
2025-09-18 17:09:02 -03:00
|
|
|
let private_account_1 = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-03 16:25:02 -03:00
|
|
|
program_owner: program.id(),
|
|
|
|
|
balance: 100,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
true,
|
2025-10-03 18:31:56 -03:00
|
|
|
&sender_keys.npk(),
|
2025-09-18 17:09:02 -03:00
|
|
|
);
|
|
|
|
|
let private_account_2 =
|
2025-10-03 18:31:56 -03:00
|
|
|
AccountWithMetadata::new(Account::default(), false, &recipient_keys.npk());
|
2025-09-03 16:25:02 -03:00
|
|
|
|
|
|
|
|
// 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();
|
2025-09-18 17:09:02 -03:00
|
|
|
let private_account_1 = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-03 16:25:02 -03:00
|
|
|
program_owner: program.id(),
|
|
|
|
|
balance: 100,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
true,
|
2025-10-03 18:31:56 -03:00
|
|
|
&sender_keys.npk(),
|
2025-09-18 17:09:02 -03:00
|
|
|
);
|
|
|
|
|
let private_account_2 =
|
2025-10-03 18:31:56 -03:00
|
|
|
AccountWithMetadata::new(Account::default(), false, &recipient_keys.npk());
|
2025-09-03 16:25:02 -03:00
|
|
|
|
|
|
|
|
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();
|
2025-09-18 17:09:02 -03:00
|
|
|
let private_account_1 = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-03 16:25:02 -03:00
|
|
|
program_owner: program.id(),
|
|
|
|
|
balance: 100,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
true,
|
2025-10-03 18:31:56 -03:00
|
|
|
&sender_keys.npk(),
|
2025-09-18 17:09:02 -03:00
|
|
|
);
|
|
|
|
|
let private_account_2 = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-03 16:25:02 -03:00
|
|
|
// Non default balance
|
|
|
|
|
balance: 1,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
false,
|
2025-10-03 18:31:56 -03:00
|
|
|
&recipient_keys.npk(),
|
2025-09-18 17:09:02 -03:00
|
|
|
);
|
2025-09-03 16:25:02 -03:00
|
|
|
|
|
|
|
|
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();
|
2025-09-18 17:09:02 -03:00
|
|
|
let private_account_1 = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-03 16:25:02 -03:00
|
|
|
program_owner: program.id(),
|
|
|
|
|
balance: 100,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
true,
|
2025-10-03 18:31:56 -03:00
|
|
|
&sender_keys.npk(),
|
2025-09-18 17:09:02 -03:00
|
|
|
);
|
|
|
|
|
let private_account_2 = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-03 16:25:02 -03:00
|
|
|
// Non default program_owner
|
|
|
|
|
program_owner: [0, 1, 2, 3, 4, 5, 6, 7],
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
false,
|
2025-10-03 18:31:56 -03:00
|
|
|
&recipient_keys.npk(),
|
2025-09-18 17:09:02 -03:00
|
|
|
);
|
2025-09-03 16:25:02 -03:00
|
|
|
|
|
|
|
|
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();
|
2025-09-18 17:09:02 -03:00
|
|
|
let private_account_1 = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-03 16:25:02 -03:00
|
|
|
program_owner: program.id(),
|
|
|
|
|
balance: 100,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
true,
|
2025-10-03 18:31:56 -03:00
|
|
|
&sender_keys.npk(),
|
2025-09-18 17:09:02 -03:00
|
|
|
);
|
|
|
|
|
let private_account_2 = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-03 16:25:02 -03:00
|
|
|
// Non default data
|
|
|
|
|
data: b"hola mundo".to_vec(),
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
false,
|
2025-10-03 18:31:56 -03:00
|
|
|
&recipient_keys.npk(),
|
2025-09-18 17:09:02 -03:00
|
|
|
);
|
2025-09-03 16:25:02 -03:00
|
|
|
|
|
|
|
|
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();
|
2025-09-18 17:09:02 -03:00
|
|
|
let private_account_1 = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-03 16:25:02 -03:00
|
|
|
program_owner: program.id(),
|
|
|
|
|
balance: 100,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
true,
|
2025-10-03 18:31:56 -03:00
|
|
|
&sender_keys.npk(),
|
2025-09-18 17:09:02 -03:00
|
|
|
);
|
|
|
|
|
let private_account_2 = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-03 16:25:02 -03:00
|
|
|
// Non default nonce
|
|
|
|
|
nonce: 0xdeadbeef,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
false,
|
2025-10-03 18:31:56 -03:00
|
|
|
&recipient_keys.npk(),
|
2025-09-18 17:09:02 -03:00
|
|
|
);
|
2025-09-03 16:25:02 -03:00
|
|
|
|
|
|
|
|
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();
|
2025-09-18 17:09:02 -03:00
|
|
|
let private_account_1 = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-03 16:25:02 -03:00
|
|
|
program_owner: program.id(),
|
|
|
|
|
balance: 100,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
true,
|
2025-10-03 18:31:56 -03:00
|
|
|
&sender_keys.npk(),
|
2025-09-18 17:09:02 -03:00
|
|
|
);
|
|
|
|
|
let private_account_2 = AccountWithMetadata::new(
|
|
|
|
|
Account::default(),
|
2025-09-03 16:25:02 -03:00
|
|
|
// This should be set to false in normal circumstances
|
2025-09-18 17:09:02 -03:00
|
|
|
true,
|
2025-10-03 18:31:56 -03:00
|
|
|
&recipient_keys.npk(),
|
2025-09-18 17:09:02 -03:00
|
|
|
);
|
2025-09-03 16:25:02 -03:00
|
|
|
|
|
|
|
|
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();
|
2025-09-18 17:09:02 -03:00
|
|
|
let public_account_1 = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-03 16:25:02 -03:00
|
|
|
program_owner: program.id(),
|
|
|
|
|
balance: 100,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
true,
|
|
|
|
|
AccountId::new([0; 32]),
|
|
|
|
|
);
|
|
|
|
|
let public_account_2 =
|
|
|
|
|
AccountWithMetadata::new(Account::default(), false, AccountId::new([1; 32]));
|
2025-09-03 16:25:02 -03:00
|
|
|
|
|
|
|
|
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(_))));
|
|
|
|
|
}
|
2025-09-03 16:44:55 -03:00
|
|
|
|
|
|
|
|
#[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();
|
2025-09-18 17:09:02 -03:00
|
|
|
let private_account_1 = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-03 16:44:55 -03:00
|
|
|
program_owner: program.id(),
|
|
|
|
|
balance: 100,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
true,
|
2025-10-03 18:31:56 -03:00
|
|
|
&sender_keys.npk(),
|
2025-09-18 17:09:02 -03:00
|
|
|
);
|
|
|
|
|
let private_account_2 =
|
2025-10-03 18:31:56 -03:00
|
|
|
AccountWithMetadata::new(Account::default(), false, &recipient_keys.npk());
|
2025-09-03 16:44:55 -03:00
|
|
|
|
|
|
|
|
// 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();
|
2025-09-18 17:09:02 -03:00
|
|
|
let private_account_1 = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-03 16:44:55 -03:00
|
|
|
program_owner: program.id(),
|
|
|
|
|
balance: 100,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
true,
|
2025-10-03 18:31:56 -03:00
|
|
|
&sender_keys.npk(),
|
2025-09-18 17:09:02 -03:00
|
|
|
);
|
|
|
|
|
let private_account_2 =
|
2025-10-03 18:31:56 -03:00
|
|
|
AccountWithMetadata::new(Account::default(), false, &recipient_keys.npk());
|
2025-09-03 16:44:55 -03:00
|
|
|
|
|
|
|
|
// 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();
|
2025-09-18 17:09:02 -03:00
|
|
|
let private_account_1 = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
2025-09-03 16:44:55 -03:00
|
|
|
program_owner: program.id(),
|
|
|
|
|
balance: 100,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
2025-09-18 17:09:02 -03:00
|
|
|
true,
|
2025-10-03 18:31:56 -03:00
|
|
|
&sender_keys.npk(),
|
2025-09-18 17:09:02 -03:00
|
|
|
);
|
|
|
|
|
let private_account_2 =
|
2025-10-03 18:31:56 -03:00
|
|
|
AccountWithMetadata::new(Account::default(), false, &recipient_keys.npk());
|
2025-09-03 16:44:55 -03:00
|
|
|
|
|
|
|
|
// 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(_))));
|
|
|
|
|
}
|
2025-10-03 20:44:29 -03:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_private_accounts_can_only_be_initialized_once() {
|
|
|
|
|
let sender_keys = test_private_account_keys_1();
|
|
|
|
|
let sender_private_account = Account {
|
|
|
|
|
program_owner: Program::authenticated_transfer_program().id(),
|
|
|
|
|
balance: 100,
|
|
|
|
|
nonce: 0xdeadbeef,
|
|
|
|
|
data: vec![],
|
|
|
|
|
};
|
|
|
|
|
let recipient_keys = test_private_account_keys_2();
|
|
|
|
|
|
2025-10-16 16:24:18 -03:00
|
|
|
let mut state = V02State::new_with_genesis_accounts(&[], &[])
|
2025-10-03 20:44:29 -03:00
|
|
|
.with_private_account(&sender_keys, &sender_private_account);
|
|
|
|
|
|
|
|
|
|
let balance_to_move = 37;
|
|
|
|
|
|
|
|
|
|
let tx = private_balance_transfer_for_tests(
|
|
|
|
|
&sender_keys,
|
|
|
|
|
&sender_private_account,
|
|
|
|
|
&recipient_keys,
|
|
|
|
|
balance_to_move,
|
|
|
|
|
[0xcafecafe, 0xfecafeca],
|
|
|
|
|
&state,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
state
|
|
|
|
|
.transition_from_privacy_preserving_transaction(&tx)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
let sender_private_account = Account {
|
|
|
|
|
program_owner: Program::authenticated_transfer_program().id(),
|
|
|
|
|
balance: 100 - balance_to_move,
|
|
|
|
|
nonce: 0xcafecafe,
|
|
|
|
|
data: vec![],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let tx = private_balance_transfer_for_tests(
|
|
|
|
|
&sender_keys,
|
|
|
|
|
&sender_private_account,
|
|
|
|
|
&recipient_keys,
|
|
|
|
|
balance_to_move,
|
|
|
|
|
[0x1234, 0x5678],
|
|
|
|
|
&state,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let result = state.transition_from_privacy_preserving_transaction(&tx);
|
|
|
|
|
|
|
|
|
|
assert!(matches!(result, Err(NssaError::InvalidInput(_))));
|
|
|
|
|
let NssaError::InvalidInput(error_message) = result.err().unwrap() else {
|
|
|
|
|
panic!("Incorrect message error");
|
|
|
|
|
};
|
|
|
|
|
let expected_error_message = "Nullifier already seen".to_string();
|
|
|
|
|
assert_eq!(error_message, expected_error_message);
|
|
|
|
|
}
|
2025-10-07 12:56:57 -03:00
|
|
|
|
2025-10-03 18:31:56 -03:00
|
|
|
#[test]
|
|
|
|
|
fn test_circuit_should_fail_if_there_are_repeated_ids() {
|
|
|
|
|
let program = Program::simple_balance_transfer();
|
|
|
|
|
let sender_keys = test_private_account_keys_1();
|
|
|
|
|
let private_account_1 = AccountWithMetadata::new(
|
|
|
|
|
Account {
|
|
|
|
|
program_owner: program.id(),
|
|
|
|
|
balance: 100,
|
|
|
|
|
..Account::default()
|
|
|
|
|
},
|
|
|
|
|
true,
|
|
|
|
|
&sender_keys.npk(),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let visibility_mask = [1, 1];
|
|
|
|
|
let private_account_auth = [
|
|
|
|
|
(sender_keys.nsk, (1, vec![])),
|
|
|
|
|
(sender_keys.nsk, (1, vec![])),
|
|
|
|
|
];
|
|
|
|
|
let shared_secret = SharedSecretKey::new(&[55; 32], &sender_keys.ivk());
|
|
|
|
|
let result = execute_and_prove(
|
|
|
|
|
&[private_account_1.clone(), private_account_1],
|
|
|
|
|
&Program::serialize_instruction(100u128).unwrap(),
|
|
|
|
|
&visibility_mask,
|
|
|
|
|
&[0xdeadbeef1, 0xdeadbeef2],
|
|
|
|
|
&[
|
|
|
|
|
(sender_keys.npk(), shared_secret.clone()),
|
|
|
|
|
(sender_keys.npk(), shared_secret),
|
|
|
|
|
],
|
|
|
|
|
&private_account_auth,
|
|
|
|
|
&program,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
|
|
|
|
|
}
|
2025-10-29 15:34:11 -03:00
|
|
|
|
2025-10-30 10:52:31 -03:00
|
|
|
#[test]
|
|
|
|
|
fn test_claiming_mechanism() {
|
|
|
|
|
let program = Program::authenticated_transfer_program();
|
|
|
|
|
let key = PrivateKey::try_new([1; 32]).unwrap();
|
2025-11-24 17:09:30 +03:00
|
|
|
let account_id = AccountId::from(&PublicKey::new_from_private_key(&key));
|
2025-10-30 10:52:31 -03:00
|
|
|
let initial_balance = 100;
|
2025-11-24 17:09:30 +03:00
|
|
|
let initial_data = [(account_id, initial_balance)];
|
2025-10-30 10:52:31 -03:00
|
|
|
let mut state =
|
|
|
|
|
V02State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs();
|
2025-11-24 17:09:30 +03:00
|
|
|
let from = account_id;
|
2025-10-30 10:52:31 -03:00
|
|
|
let from_key = key;
|
2025-11-24 17:09:30 +03:00
|
|
|
let to = AccountId::new([2; 32]);
|
2025-10-30 10:52:31 -03:00
|
|
|
let amount: u128 = 37;
|
|
|
|
|
|
|
|
|
|
// Check the recipient is an uninitialized account
|
2025-11-24 17:09:30 +03:00
|
|
|
assert_eq!(state.get_account_by_id(&to), Account::default());
|
2025-10-30 10:52:31 -03:00
|
|
|
|
|
|
|
|
let expected_recipient_post = Account {
|
|
|
|
|
program_owner: program.id(),
|
|
|
|
|
balance: amount,
|
|
|
|
|
..Account::default()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let message =
|
|
|
|
|
public_transaction::Message::try_new(program.id(), vec![from, to], vec![0], amount)
|
|
|
|
|
.unwrap();
|
|
|
|
|
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]);
|
|
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
|
|
|
|
|
state.transition_from_public_transaction(&tx).unwrap();
|
|
|
|
|
|
2025-11-24 17:09:30 +03:00
|
|
|
let recipient_post = state.get_account_by_id(&to);
|
2025-10-30 10:52:31 -03:00
|
|
|
|
|
|
|
|
assert_eq!(recipient_post, expected_recipient_post);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-29 15:34:11 -03:00
|
|
|
#[test]
|
2025-11-12 19:55:02 -03:00
|
|
|
fn test_chained_call_succeeds() {
|
2025-10-29 15:34:11 -03:00
|
|
|
let program = Program::chain_caller();
|
|
|
|
|
let key = PrivateKey::try_new([1; 32]).unwrap();
|
2025-11-26 16:53:21 -03:00
|
|
|
let from = AccountId::from(&PublicKey::new_from_private_key(&key));
|
|
|
|
|
let to = AccountId::new([2; 32]);
|
2025-10-29 15:34:11 -03:00
|
|
|
let initial_balance = 100;
|
2025-11-26 16:53:21 -03:00
|
|
|
let initial_data = [(from, initial_balance), (to, 0)];
|
2025-10-29 15:34:11 -03:00
|
|
|
let mut state =
|
|
|
|
|
V02State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs();
|
|
|
|
|
let from_key = key;
|
2025-11-17 15:43:01 -03:00
|
|
|
let amount: u128 = 0;
|
2025-11-12 19:55:02 -03:00
|
|
|
let instruction: (u128, ProgramId, u32) =
|
|
|
|
|
(amount, Program::authenticated_transfer_program().id(), 2);
|
2025-10-29 15:34:11 -03:00
|
|
|
|
2025-10-30 10:52:31 -03:00
|
|
|
let expected_to_post = Account {
|
2025-11-17 15:43:01 -03:00
|
|
|
program_owner: Program::authenticated_transfer_program().id(),
|
2025-11-12 19:08:46 -03:00
|
|
|
balance: amount * 2, // The `chain_caller` chains the program twice
|
2025-10-30 10:52:31 -03:00
|
|
|
..Account::default()
|
|
|
|
|
};
|
|
|
|
|
|
2025-10-29 15:34:11 -03:00
|
|
|
let message = public_transaction::Message::try_new(
|
|
|
|
|
program.id(),
|
2025-11-26 16:53:21 -03:00
|
|
|
vec![to, from], // The chain_caller program permutes the account order in the chain
|
2025-11-26 20:13:23 -03:00
|
|
|
// call
|
2025-10-29 15:34:11 -03:00
|
|
|
vec![0],
|
|
|
|
|
instruction,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]);
|
|
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
|
|
|
|
|
state.transition_from_public_transaction(&tx).unwrap();
|
|
|
|
|
|
2025-11-24 17:09:30 +03:00
|
|
|
let from_post = state.get_account_by_id(&from);
|
|
|
|
|
let to_post = state.get_account_by_id(&to);
|
2025-11-12 19:08:46 -03:00
|
|
|
// The `chain_caller` program calls the program twice
|
|
|
|
|
assert_eq!(from_post.balance, initial_balance - 2 * amount);
|
2025-10-30 10:52:31 -03:00
|
|
|
assert_eq!(to_post, expected_to_post);
|
2025-10-29 15:34:11 -03:00
|
|
|
}
|
2025-11-12 19:55:02 -03:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_execution_fails_if_chained_calls_exceeds_depth() {
|
|
|
|
|
let program = Program::chain_caller();
|
|
|
|
|
let key = PrivateKey::try_new([1; 32]).unwrap();
|
2025-11-26 16:53:21 -03:00
|
|
|
let from = AccountId::from(&PublicKey::new_from_private_key(&key));
|
|
|
|
|
let to = AccountId::new([2; 32]);
|
2025-11-12 19:55:02 -03:00
|
|
|
let initial_balance = 100;
|
2025-11-26 16:53:21 -03:00
|
|
|
let initial_data = [(from, initial_balance), (to, 0)];
|
2025-11-12 19:55:02 -03:00
|
|
|
let mut state =
|
|
|
|
|
V02State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs();
|
|
|
|
|
let from_key = key;
|
|
|
|
|
let amount: u128 = 0;
|
|
|
|
|
let instruction: (u128, ProgramId, u32) = (
|
|
|
|
|
amount,
|
|
|
|
|
Program::authenticated_transfer_program().id(),
|
|
|
|
|
MAX_NUMBER_CHAINED_CALLS as u32 + 1,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let message = public_transaction::Message::try_new(
|
|
|
|
|
program.id(),
|
2025-11-26 16:53:21 -03:00
|
|
|
vec![to, from], // The chain_caller program permutes the account order in the chain
|
2025-11-26 20:13:23 -03:00
|
|
|
// call
|
2025-11-12 19:55:02 -03:00
|
|
|
vec![0],
|
|
|
|
|
instruction,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]);
|
|
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
|
|
|
|
|
let result = state.transition_from_public_transaction(&tx);
|
|
|
|
|
assert!(matches!(
|
|
|
|
|
result,
|
|
|
|
|
Err(NssaError::MaxChainedCallsDepthExceeded)
|
|
|
|
|
));
|
|
|
|
|
}
|
2025-11-20 21:02:18 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
|
2025-11-20 21:02:18 -05:00
|
|
|
//TODO: repeated code needs to be cleaned up
|
2025-11-21 19:17:50 -05:00
|
|
|
//from token.rs (also repeated in amm.rs)
|
2025-11-20 21:02:18 -05:00
|
|
|
const TOKEN_DEFINITION_TYPE: u8 = 0;
|
|
|
|
|
const TOKEN_DEFINITION_DATA_SIZE: usize = 23;
|
|
|
|
|
|
|
|
|
|
const TOKEN_HOLDING_TYPE: u8 = 1;
|
|
|
|
|
const TOKEN_HOLDING_DATA_SIZE: usize = 49;
|
|
|
|
|
|
|
|
|
|
struct TokenDefinition {
|
2025-11-21 19:17:50 -05:00
|
|
|
account_type: u8,
|
|
|
|
|
name: [u8; 6],
|
|
|
|
|
total_supply: u128,
|
|
|
|
|
}
|
2025-11-20 21:02:18 -05:00
|
|
|
|
2025-11-21 19:17:50 -05:00
|
|
|
struct TokenHolding {
|
|
|
|
|
account_type: u8,
|
|
|
|
|
definition_id: AccountId,
|
|
|
|
|
balance: u128,
|
2025-11-20 21:02:18 -05:00
|
|
|
}
|
|
|
|
|
|
2025-11-21 19:17:50 -05:00
|
|
|
impl TokenDefinition {
|
|
|
|
|
fn into_data(self) -> Vec<u8> {
|
|
|
|
|
let mut bytes = [0; TOKEN_DEFINITION_DATA_SIZE];
|
|
|
|
|
bytes[0] = self.account_type;
|
|
|
|
|
bytes[1..7].copy_from_slice(&self.name);
|
|
|
|
|
bytes[7..].copy_from_slice(&self.total_supply.to_le_bytes());
|
|
|
|
|
bytes.into()
|
2025-11-20 21:02:18 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-21 19:17:50 -05:00
|
|
|
impl TokenHolding {
|
|
|
|
|
fn new(definition_id: &AccountId) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
account_type: TOKEN_HOLDING_TYPE,
|
|
|
|
|
definition_id: definition_id.clone(),
|
|
|
|
|
balance: 0,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse(data: &[u8]) -> Option<Self> {
|
|
|
|
|
if data.len() != TOKEN_HOLDING_DATA_SIZE || data[0] != TOKEN_HOLDING_TYPE {
|
|
|
|
|
None
|
|
|
|
|
} else {
|
|
|
|
|
let account_type = data[0];
|
|
|
|
|
let definition_id = AccountId::new(data[1..33].try_into().unwrap());
|
|
|
|
|
let balance = u128::from_le_bytes(data[33..].try_into().unwrap());
|
|
|
|
|
Some(Self {
|
|
|
|
|
definition_id,
|
|
|
|
|
balance,
|
|
|
|
|
account_type,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn into_data(self) -> Data {
|
|
|
|
|
let mut bytes = [0; TOKEN_HOLDING_DATA_SIZE];
|
|
|
|
|
bytes[0] = self.account_type;
|
|
|
|
|
bytes[1..33].copy_from_slice(&self.definition_id.to_bytes());
|
|
|
|
|
bytes[33..].copy_from_slice(&self.balance.to_le_bytes());
|
|
|
|
|
bytes.into()
|
2025-11-20 21:02:18 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
const POOL_DEFINITION_DATA_SIZE: usize = 240;
|
2025-11-25 23:06:47 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
struct PoolDefinition {
|
|
|
|
|
definition_token_a_id: AccountId,
|
|
|
|
|
definition_token_b_id: AccountId,
|
|
|
|
|
vault_a_addr: AccountId,
|
|
|
|
|
vault_b_addr: AccountId,
|
|
|
|
|
liquidity_pool_id: AccountId,
|
|
|
|
|
liquidity_pool_cap: u128,
|
|
|
|
|
reserve_a: u128,
|
|
|
|
|
reserve_b: u128,
|
|
|
|
|
token_program_id: ProgramId,
|
|
|
|
|
}
|
2025-11-25 23:06:47 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
impl PoolDefinition {
|
|
|
|
|
fn into_data(self) -> Vec<u8> {
|
|
|
|
|
let u8_token_program_id: [u8; 32] = bytemuck::cast(self.token_program_id);
|
|
|
|
|
|
|
|
|
|
let mut bytes = [0; POOL_DEFINITION_DATA_SIZE];
|
|
|
|
|
bytes[0..32].copy_from_slice(&self.definition_token_a_id.to_bytes());
|
|
|
|
|
bytes[32..64].copy_from_slice(&self.definition_token_b_id.to_bytes());
|
|
|
|
|
bytes[64..96].copy_from_slice(&self.vault_a_addr.to_bytes());
|
|
|
|
|
bytes[96..128].copy_from_slice(&self.vault_b_addr.to_bytes());
|
|
|
|
|
bytes[128..160].copy_from_slice(&self.liquidity_pool_id.to_bytes());
|
|
|
|
|
bytes[160..176].copy_from_slice(&self.liquidity_pool_cap.to_le_bytes());
|
|
|
|
|
bytes[176..192].copy_from_slice(&self.reserve_a.to_le_bytes());
|
|
|
|
|
bytes[192..208].copy_from_slice(&self.reserve_b.to_le_bytes());
|
|
|
|
|
bytes[208..].copy_from_slice(&u8_token_program_id);
|
|
|
|
|
bytes.into()
|
|
|
|
|
}
|
2025-11-25 23:06:47 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
fn parse(data: &[u8]) -> Option<Self> {
|
|
|
|
|
if data.len() != POOL_DEFINITION_DATA_SIZE {
|
|
|
|
|
None
|
|
|
|
|
} else {
|
|
|
|
|
let definition_token_a_id = AccountId::new(data[0..32].try_into().unwrap());
|
|
|
|
|
let definition_token_b_id = AccountId::new(data[32..64].try_into().unwrap());
|
|
|
|
|
let vault_a_addr = AccountId::new(data[64..96].try_into().unwrap());
|
|
|
|
|
let vault_b_addr = AccountId::new(data[96..128].try_into().unwrap());
|
|
|
|
|
let liquidity_pool_id = AccountId::new(data[128..160].try_into().unwrap());
|
|
|
|
|
let liquidity_pool_cap = u128::from_le_bytes(data[160..176].try_into().unwrap());
|
|
|
|
|
let reserve_a = u128::from_le_bytes(data[176..192].try_into().unwrap());
|
|
|
|
|
let reserve_b = u128::from_le_bytes(data[192..208].try_into().unwrap());
|
|
|
|
|
|
|
|
|
|
let token_program_id: &[u32] = bytemuck::cast_slice(&data[208..]);
|
|
|
|
|
let token_program_id: ProgramId = token_program_id[0..8].try_into().unwrap();
|
|
|
|
|
Some(Self {
|
|
|
|
|
definition_token_a_id,
|
|
|
|
|
definition_token_b_id,
|
|
|
|
|
vault_a_addr,
|
|
|
|
|
vault_b_addr,
|
|
|
|
|
liquidity_pool_id,
|
|
|
|
|
liquidity_pool_cap,
|
|
|
|
|
reserve_a,
|
|
|
|
|
reserve_b,
|
|
|
|
|
token_program_id,
|
|
|
|
|
})
|
|
|
|
|
}
|
2025-11-25 23:06:47 -05:00
|
|
|
}
|
2025-11-20 21:02:18 -05:00
|
|
|
}
|
|
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
/// Used for each amm test to initialize
|
|
|
|
|
/// an AMM pool
|
2025-12-02 15:20:16 -05:00
|
|
|
fn initialize_amm() -> (V02State, Vec<PrivateKey>, Vec<AccountId>, Vec<u128>) {
|
2025-11-24 19:44:08 -05:00
|
|
|
let initial_data = [];
|
2025-11-20 21:02:18 -05:00
|
|
|
let mut state =
|
|
|
|
|
V02State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs();
|
|
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let token_a_holding_key = PrivateKey::try_new([1; 32]).unwrap();
|
|
|
|
|
let token_a_definition_key = PrivateKey::try_new([2; 32]).unwrap();
|
2025-12-02 15:20:16 -05:00
|
|
|
let token_a_holding_id =
|
|
|
|
|
AccountId::from(&PublicKey::new_from_private_key(&token_a_holding_key));
|
|
|
|
|
let token_a_definition_id =
|
|
|
|
|
AccountId::from(&PublicKey::new_from_private_key(&token_a_definition_key));
|
2025-11-24 19:44:08 -05:00
|
|
|
let token_a_supply: u128 = 30000;
|
|
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let token_b_holding_key = PrivateKey::try_new([3; 32]).unwrap();
|
|
|
|
|
let token_b_definition_key = PrivateKey::try_new([4; 32]).unwrap();
|
2025-12-02 15:20:16 -05:00
|
|
|
let token_b_holding_id =
|
|
|
|
|
AccountId::from(&PublicKey::new_from_private_key(&token_b_holding_key));
|
|
|
|
|
let token_b_definition_id =
|
|
|
|
|
AccountId::from(&PublicKey::new_from_private_key(&token_b_definition_key));
|
2025-11-24 19:44:08 -05:00
|
|
|
let token_b_supply: u128 = 50000;
|
|
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let pool_lp_holding_key = PrivateKey::try_new([5; 32]).unwrap();
|
|
|
|
|
let pool_lp_definition_key = PrivateKey::try_new([6; 32]).unwrap();
|
2025-12-02 15:20:16 -05:00
|
|
|
let pool_lp_holding_id =
|
|
|
|
|
AccountId::from(&PublicKey::new_from_private_key(&pool_lp_holding_key));
|
|
|
|
|
let pool_lp_definition_id =
|
|
|
|
|
AccountId::from(&PublicKey::new_from_private_key(&pool_lp_definition_key));
|
2025-11-24 19:44:08 -05:00
|
|
|
let token_lp_supply: u128 = 300000;
|
|
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let user_a_holding_key = PrivateKey::try_new([7; 32]).unwrap();
|
2025-12-02 15:20:16 -05:00
|
|
|
let user_a_holding_id =
|
|
|
|
|
AccountId::from(&PublicKey::new_from_private_key(&user_a_holding_key));
|
2025-11-24 19:44:08 -05:00
|
|
|
let user_a_amount: u128 = 10000;
|
|
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let user_b_holding_key = PrivateKey::try_new([8; 32]).unwrap();
|
2025-12-02 15:20:16 -05:00
|
|
|
let user_b_holding_id =
|
|
|
|
|
AccountId::from(&PublicKey::new_from_private_key(&user_b_holding_key));
|
2025-11-24 19:44:08 -05:00
|
|
|
let user_b_amount: u128 = 10000;
|
|
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let vault_a_key = PrivateKey::try_new([9; 32]).unwrap();
|
2025-12-02 15:20:16 -05:00
|
|
|
let vault_a_id = AccountId::from(&PublicKey::new_from_private_key(&vault_a_key));
|
2025-11-24 19:44:08 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let vault_b_key = PrivateKey::try_new([10; 32]).unwrap();
|
2025-12-02 15:20:16 -05:00
|
|
|
let vault_b_id = AccountId::from(&PublicKey::new_from_private_key(&vault_b_key));
|
2025-11-24 19:44:08 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let user_lp_holding_key = PrivateKey::try_new([11; 32]).unwrap();
|
2025-12-02 15:20:16 -05:00
|
|
|
let user_lp_holding_id =
|
|
|
|
|
AccountId::from(&PublicKey::new_from_private_key(&user_lp_holding_key));
|
2025-11-24 19:44:08 -05:00
|
|
|
|
|
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let pool_key = PrivateKey::try_new([13; 32]).unwrap();
|
2025-12-02 15:20:16 -05:00
|
|
|
let pool_id = AccountId::from(&PublicKey::new_from_private_key(&pool_key));
|
2025-11-24 19:44:08 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
//initialize Token A
|
|
|
|
|
let mut instruction: [u8; 23] = [0; 23];
|
2025-11-24 19:44:08 -05:00
|
|
|
instruction[1..17].copy_from_slice(&token_a_supply.to_le_bytes());
|
|
|
|
|
instruction[18] = 0x01; //name is not default.
|
|
|
|
|
instruction[19] = 0x02;
|
2025-11-20 21:02:18 -05:00
|
|
|
|
|
|
|
|
let message = public_transaction::Message::try_new(
|
2025-11-24 19:44:08 -05:00
|
|
|
Program::token().id(),
|
2025-12-02 15:20:16 -05:00
|
|
|
vec![token_a_definition_id, token_a_holding_id],
|
2025-11-24 19:44:08 -05:00
|
|
|
vec![],
|
2025-11-20 21:02:18 -05:00
|
|
|
instruction,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
2025-11-21 19:17:50 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
2025-11-20 21:02:18 -05:00
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
2025-11-24 19:44:08 -05:00
|
|
|
state.transition_from_public_transaction(&tx).unwrap();
|
2025-11-20 21:02:18 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
//initialize Token B
|
2025-11-24 19:44:08 -05:00
|
|
|
instruction[1..17].copy_from_slice(&token_b_supply.to_le_bytes());
|
|
|
|
|
instruction[18] = 0x03; //name is not default.
|
|
|
|
|
instruction[19] = 0x02;
|
2025-11-21 19:17:50 -05:00
|
|
|
|
2025-11-24 19:44:08 -05:00
|
|
|
let message = public_transaction::Message::try_new(
|
|
|
|
|
Program::token().id(),
|
2025-12-02 15:20:16 -05:00
|
|
|
vec![token_b_definition_id, token_b_holding_id],
|
2025-11-24 19:44:08 -05:00
|
|
|
vec![],
|
|
|
|
|
instruction,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
2025-11-21 19:17:50 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
2025-11-24 19:44:08 -05:00
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
state.transition_from_public_transaction(&tx).unwrap();
|
2025-11-21 19:17:50 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
|
|
|
|
|
//initialize Token LP
|
2025-11-24 19:44:08 -05:00
|
|
|
instruction[1..17].copy_from_slice(&token_lp_supply.to_le_bytes());
|
|
|
|
|
instruction[18] = 0x03; //name is not default.
|
|
|
|
|
instruction[19] = 0x04;
|
2025-11-21 19:17:50 -05:00
|
|
|
|
2025-11-24 19:44:08 -05:00
|
|
|
let message = public_transaction::Message::try_new(
|
|
|
|
|
Program::token().id(),
|
2025-12-02 15:20:16 -05:00
|
|
|
vec![pool_lp_definition_id, pool_lp_holding_id],
|
2025-11-24 19:44:08 -05:00
|
|
|
vec![],
|
|
|
|
|
instruction,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
2025-11-21 19:17:50 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
2025-11-24 19:44:08 -05:00
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
state.transition_from_public_transaction(&tx).unwrap();
|
2025-11-21 19:17:50 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
// Initialize User accounts for Token A
|
|
|
|
|
let mut instruction: [u8; 23] = [0; 23];
|
2025-11-24 19:44:08 -05:00
|
|
|
instruction[0] = 1; //transfer
|
|
|
|
|
instruction[1..17].copy_from_slice(&user_a_amount.to_le_bytes());
|
2025-11-21 19:17:50 -05:00
|
|
|
|
2025-11-24 19:44:08 -05:00
|
|
|
let message = public_transaction::Message::try_new(
|
|
|
|
|
Program::token().id(),
|
2025-12-02 15:20:16 -05:00
|
|
|
vec![token_a_holding_id, user_a_holding_id],
|
2025-11-24 19:44:08 -05:00
|
|
|
vec![0],
|
|
|
|
|
instruction,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
2025-11-21 19:17:50 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let witness_set =
|
|
|
|
|
public_transaction::WitnessSet::for_message(&message, &[&token_a_holding_key]);
|
2025-11-24 19:44:08 -05:00
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
state.transition_from_public_transaction(&tx).unwrap();
|
2025-11-21 19:17:50 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
|
|
|
|
|
// Initialize User accounts for Token A
|
2025-11-24 19:44:08 -05:00
|
|
|
instruction[0] = 1; //transfer
|
|
|
|
|
instruction[1..17].copy_from_slice(&user_b_amount.to_le_bytes());
|
2025-11-21 19:17:50 -05:00
|
|
|
|
2025-11-24 19:44:08 -05:00
|
|
|
let message = public_transaction::Message::try_new(
|
|
|
|
|
Program::token().id(),
|
2025-12-02 15:20:16 -05:00
|
|
|
vec![token_b_holding_id, user_b_holding_id],
|
2025-11-24 19:44:08 -05:00
|
|
|
vec![0],
|
|
|
|
|
instruction,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
2025-11-21 19:17:50 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let witness_set =
|
|
|
|
|
public_transaction::WitnessSet::for_message(&message, &[&token_b_holding_key]);
|
2025-11-24 19:44:08 -05:00
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
state.transition_from_public_transaction(&tx).unwrap();
|
2025-11-21 19:17:50 -05:00
|
|
|
|
2025-11-24 19:44:08 -05:00
|
|
|
//TODO: initialize vaults - ideally, we won't need to do this.
|
2025-11-26 21:44:57 -05:00
|
|
|
// Initialize Vault A
|
|
|
|
|
let mut instruction: [u8; 23] = [0; 23];
|
2025-12-04 21:53:58 -05:00
|
|
|
instruction[0] = 2; //initialize
|
|
|
|
|
|
2025-11-24 19:44:08 -05:00
|
|
|
let message = public_transaction::Message::try_new(
|
|
|
|
|
Program::token().id(),
|
2025-12-04 21:53:58 -05:00
|
|
|
vec![token_a_definition_id, vault_a_id],
|
2025-11-24 19:44:08 -05:00
|
|
|
vec![1],
|
|
|
|
|
instruction,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let witness_set =
|
|
|
|
|
public_transaction::WitnessSet::for_message(&message, &[&token_a_holding_key]);
|
2025-11-24 19:44:08 -05:00
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
state.transition_from_public_transaction(&tx).unwrap();
|
|
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
// Initialize Vault B
|
2025-11-21 19:17:50 -05:00
|
|
|
let message = public_transaction::Message::try_new(
|
|
|
|
|
Program::token().id(),
|
2025-12-04 21:53:58 -05:00
|
|
|
vec![token_b_definition_id, vault_b_id],
|
2025-11-24 19:44:08 -05:00
|
|
|
vec![1],
|
2025-11-21 19:17:50 -05:00
|
|
|
instruction,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let witness_set =
|
|
|
|
|
public_transaction::WitnessSet::for_message(&message, &[&token_b_holding_key]);
|
2025-11-21 19:17:50 -05:00
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
2025-11-24 19:44:08 -05:00
|
|
|
state.transition_from_public_transaction(&tx).unwrap();
|
|
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
// Initialize User LP
|
2025-11-24 19:44:08 -05:00
|
|
|
let message = public_transaction::Message::try_new(
|
|
|
|
|
Program::token().id(),
|
2025-12-04 21:53:58 -05:00
|
|
|
vec![pool_lp_definition_id, user_lp_holding_id],
|
2025-11-24 19:44:08 -05:00
|
|
|
vec![0],
|
|
|
|
|
instruction,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let witness_set =
|
|
|
|
|
public_transaction::WitnessSet::for_message(&message, &[&pool_lp_holding_key]);
|
2025-11-24 19:44:08 -05:00
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
2025-11-21 19:17:50 -05:00
|
|
|
state.transition_from_public_transaction(&tx).unwrap();
|
|
|
|
|
|
2025-11-24 19:44:08 -05:00
|
|
|
//Set up instruction to initialize AMM for (Token-A, Token-B)
|
|
|
|
|
let init_balance_a: u128 = 1000;
|
|
|
|
|
let init_balance_b: u128 = 1000;
|
2025-11-26 21:44:57 -05:00
|
|
|
let token_program_u8: [u8; 32] = bytemuck::cast(Program::token().id());
|
|
|
|
|
|
2025-11-24 19:44:08 -05:00
|
|
|
let mut instruction: Vec<u8> = Vec::new();
|
|
|
|
|
instruction.push(0);
|
|
|
|
|
instruction.extend_from_slice(&init_balance_a.to_le_bytes());
|
|
|
|
|
instruction.extend_from_slice(&init_balance_b.to_le_bytes());
|
|
|
|
|
instruction.extend_from_slice(&token_program_u8);
|
2025-11-26 21:44:57 -05:00
|
|
|
|
2025-11-24 19:44:08 -05:00
|
|
|
let message = public_transaction::Message::try_new(
|
|
|
|
|
Program::amm().id(),
|
|
|
|
|
vec![
|
2025-12-02 15:20:16 -05:00
|
|
|
pool_id,
|
|
|
|
|
vault_a_id,
|
|
|
|
|
vault_b_id,
|
|
|
|
|
pool_lp_holding_id,
|
|
|
|
|
user_a_holding_id,
|
|
|
|
|
user_b_holding_id,
|
|
|
|
|
user_lp_holding_id,
|
2025-11-24 19:44:08 -05:00
|
|
|
],
|
2025-11-26 21:44:57 -05:00
|
|
|
vec![0, 1, 0, 0],
|
2025-11-25 23:06:47 -05:00
|
|
|
instruction,
|
2025-11-24 19:44:08 -05:00
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
let witness_set = public_transaction::WitnessSet::for_message(
|
|
|
|
|
&message,
|
|
|
|
|
&[
|
|
|
|
|
&pool_key,
|
|
|
|
|
&pool_lp_holding_key,
|
|
|
|
|
&user_a_holding_key,
|
|
|
|
|
&user_b_holding_key,
|
|
|
|
|
],
|
|
|
|
|
);
|
2025-11-26 21:44:57 -05:00
|
|
|
|
2025-11-24 19:44:08 -05:00
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
state.transition_from_public_transaction(&tx).unwrap();
|
|
|
|
|
|
2025-12-02 15:20:16 -05:00
|
|
|
let mut vec_id = Vec::new();
|
|
|
|
|
vec_id.push(token_a_holding_id);
|
|
|
|
|
vec_id.push(token_a_definition_id);
|
|
|
|
|
vec_id.push(token_b_holding_id);
|
|
|
|
|
vec_id.push(token_b_definition_id);
|
|
|
|
|
vec_id.push(pool_lp_definition_id);
|
|
|
|
|
vec_id.push(user_a_holding_id);
|
|
|
|
|
vec_id.push(user_b_holding_id);
|
|
|
|
|
vec_id.push(vault_a_id);
|
|
|
|
|
vec_id.push(vault_b_id);
|
|
|
|
|
vec_id.push(user_lp_holding_id);
|
|
|
|
|
vec_id.push(pool_id);
|
|
|
|
|
vec_id.push(pool_lp_holding_id);
|
2025-11-26 21:44:57 -05:00
|
|
|
|
|
|
|
|
let mut vec_private_keys = Vec::new();
|
|
|
|
|
vec_private_keys.push(token_a_holding_key);
|
|
|
|
|
vec_private_keys.push(token_a_definition_key);
|
|
|
|
|
vec_private_keys.push(token_b_holding_key);
|
|
|
|
|
vec_private_keys.push(token_b_definition_key);
|
|
|
|
|
vec_private_keys.push(pool_lp_definition_key);
|
|
|
|
|
vec_private_keys.push(user_a_holding_key);
|
|
|
|
|
vec_private_keys.push(user_b_holding_key);
|
|
|
|
|
vec_private_keys.push(vault_a_key);
|
|
|
|
|
vec_private_keys.push(vault_b_key);
|
|
|
|
|
vec_private_keys.push(user_lp_holding_key);
|
|
|
|
|
vec_private_keys.push(pool_key);
|
|
|
|
|
vec_private_keys.push(pool_lp_holding_key);
|
|
|
|
|
|
|
|
|
|
let mut vec_amounts = Vec::new();
|
|
|
|
|
vec_amounts.push(init_balance_a);
|
|
|
|
|
vec_amounts.push(init_balance_b);
|
|
|
|
|
vec_amounts.push(user_a_amount);
|
|
|
|
|
vec_amounts.push(user_b_amount);
|
|
|
|
|
|
2025-12-02 15:20:16 -05:00
|
|
|
(state, vec_private_keys, vec_id, vec_amounts)
|
2025-11-26 21:44:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_simple_amm_initialize() {
|
2025-12-02 15:20:16 -05:00
|
|
|
let (state, _vec_private_keys, vec_id, vec_amounts) = initialize_amm();
|
2025-11-26 21:44:57 -05:00
|
|
|
|
2025-12-04 21:53:58 -05:00
|
|
|
let init_balance_a = vec_amounts[0];
|
|
|
|
|
let init_balance_b = vec_amounts[1];
|
|
|
|
|
let user_a_amount = vec_amounts[2];
|
|
|
|
|
let user_b_amount = vec_amounts[3];
|
2025-11-26 21:44:57 -05:00
|
|
|
|
2025-12-02 15:20:16 -05:00
|
|
|
let token_a_holding_id = vec_id[0];
|
|
|
|
|
let token_a_definition_id = vec_id[1];
|
|
|
|
|
let token_b_holding_id = vec_id[2];
|
|
|
|
|
let token_b_definition_id = vec_id[3];
|
|
|
|
|
let token_lp_definition_id = vec_id[4];
|
|
|
|
|
let user_a_holding_id = vec_id[5];
|
|
|
|
|
let user_b_holding_id = vec_id[6];
|
|
|
|
|
let vault_a_id = vec_id[7];
|
|
|
|
|
let vault_b_id = vec_id[8];
|
|
|
|
|
let user_lp_holding_id = vec_id[9];
|
|
|
|
|
let pool_id = vec_id[10];
|
|
|
|
|
let pool_lp_holding_id = vec_id[11];
|
|
|
|
|
|
|
|
|
|
let pool_post = state.get_account_by_id(&pool_id);
|
|
|
|
|
let vault_a_post = state.get_account_by_id(&vault_a_id);
|
|
|
|
|
let vault_b_post = state.get_account_by_id(&vault_b_id);
|
|
|
|
|
let user_a_post = state.get_account_by_id(&user_a_holding_id);
|
|
|
|
|
let user_b_post = state.get_account_by_id(&user_b_holding_id);
|
|
|
|
|
let user_lp_post = state.get_account_by_id(&user_lp_holding_id);
|
2025-11-25 23:06:47 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let expected_pool = Account {
|
|
|
|
|
program_owner: Program::amm().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: PoolDefinition::into_data(
|
|
|
|
|
PoolDefinition {
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_token_a_id: token_a_definition_id,
|
|
|
|
|
definition_token_b_id: token_b_definition_id,
|
|
|
|
|
vault_a_addr: vault_a_id,
|
|
|
|
|
vault_b_addr: vault_b_id,
|
|
|
|
|
liquidity_pool_id: token_lp_definition_id,
|
2025-11-26 21:44:57 -05:00
|
|
|
liquidity_pool_cap: init_balance_a,
|
|
|
|
|
reserve_a: init_balance_a,
|
|
|
|
|
reserve_b: init_balance_b,
|
|
|
|
|
token_program_id: Program::token().id(),
|
|
|
|
|
}),
|
|
|
|
|
nonce: 1,
|
|
|
|
|
};
|
2025-11-21 19:17:50 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let expected_vault_a = Account {
|
|
|
|
|
program_owner: Program::token().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: TokenHolding::into_data(
|
|
|
|
|
TokenHolding{
|
|
|
|
|
account_type: TOKEN_HOLDING_TYPE,
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_id: token_a_definition_id,
|
2025-12-04 21:53:58 -05:00
|
|
|
balance: init_balance_a,
|
2025-11-26 21:44:57 -05:00
|
|
|
}),
|
|
|
|
|
nonce: 0
|
|
|
|
|
};
|
2025-11-21 19:17:50 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let expected_vault_b = Account {
|
|
|
|
|
program_owner: Program::token().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: TokenHolding::into_data(
|
|
|
|
|
TokenHolding{
|
|
|
|
|
account_type: TOKEN_HOLDING_TYPE,
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_id: token_b_definition_id,
|
2025-12-04 21:53:58 -05:00
|
|
|
balance: init_balance_b,
|
2025-11-26 21:44:57 -05:00
|
|
|
}),
|
|
|
|
|
nonce: 0
|
|
|
|
|
};
|
2025-11-25 23:06:47 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let expected_user_a = Account {
|
|
|
|
|
program_owner: Program::token().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: TokenHolding::into_data(
|
|
|
|
|
TokenHolding{
|
|
|
|
|
account_type: TOKEN_HOLDING_TYPE,
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_id: token_a_definition_id,
|
2025-11-26 21:44:57 -05:00
|
|
|
balance: user_a_amount - init_balance_a,
|
|
|
|
|
}),
|
|
|
|
|
nonce: 1
|
|
|
|
|
};
|
2025-11-25 23:06:47 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let expected_user_b = Account {
|
|
|
|
|
program_owner: Program::token().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: TokenHolding::into_data(
|
|
|
|
|
TokenHolding{
|
|
|
|
|
account_type: TOKEN_HOLDING_TYPE,
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_id: token_b_definition_id,
|
2025-11-26 21:44:57 -05:00
|
|
|
balance: user_b_amount - init_balance_b,
|
|
|
|
|
}),
|
|
|
|
|
nonce: 1
|
|
|
|
|
};
|
2025-11-25 23:06:47 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let expected_user_lp = Account {
|
|
|
|
|
program_owner: Program::token().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: TokenHolding::into_data(
|
|
|
|
|
TokenHolding{
|
|
|
|
|
account_type: TOKEN_HOLDING_TYPE,
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_id: token_lp_definition_id,
|
2025-12-04 21:53:58 -05:00
|
|
|
balance: init_balance_a,
|
2025-11-26 21:44:57 -05:00
|
|
|
}),
|
|
|
|
|
nonce: 0
|
|
|
|
|
};
|
2025-11-25 23:06:47 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
assert!(vault_a_post == expected_vault_a);
|
|
|
|
|
assert!(vault_b_post == expected_vault_b);
|
|
|
|
|
assert!(user_a_post == expected_user_a);
|
|
|
|
|
assert!(user_b_post == expected_user_b);
|
|
|
|
|
assert!(user_lp_post == expected_user_lp);
|
|
|
|
|
assert!(pool_post == expected_pool);
|
|
|
|
|
}
|
2025-11-25 23:06:47 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
#[test]
|
|
|
|
|
fn test_simple_amm_remove() {
|
2025-12-02 15:20:16 -05:00
|
|
|
let (state, vec_private_keys, vec_id, vec_amounts) = initialize_amm();
|
2025-11-26 21:44:57 -05:00
|
|
|
let mut state: V02State = state;
|
|
|
|
|
|
2025-12-04 21:53:58 -05:00
|
|
|
let init_balance_a = vec_amounts[0];
|
|
|
|
|
let init_balance_b = vec_amounts[1];
|
|
|
|
|
let user_a_amount = vec_amounts[2];
|
|
|
|
|
let user_b_amount = vec_amounts[3];
|
2025-11-26 21:44:57 -05:00
|
|
|
|
|
|
|
|
let token_a_holding_key = &vec_private_keys[0];
|
|
|
|
|
let token_a_definition_key = &vec_private_keys[1];
|
|
|
|
|
let token_b_holding_key = &vec_private_keys[2];
|
|
|
|
|
let token_b_definition_key = &vec_private_keys[3];
|
|
|
|
|
let pool_lp_definition_key = &vec_private_keys[4];
|
|
|
|
|
let user_a_holding_key = &vec_private_keys[5];
|
|
|
|
|
let user_b_holding_key = &vec_private_keys[6];
|
|
|
|
|
let vault_a_key = &vec_private_keys[7];
|
|
|
|
|
let vault_b_key = &vec_private_keys[8];
|
|
|
|
|
let user_lp_holding_key = &vec_private_keys[9];
|
|
|
|
|
let pool_key = &vec_private_keys[10];
|
|
|
|
|
let pool_lp_holding_key = &vec_private_keys[11];
|
|
|
|
|
|
2025-12-02 15:20:16 -05:00
|
|
|
let token_a_holding_id = vec_id[0];
|
|
|
|
|
let token_a_definition_id = vec_id[1];
|
|
|
|
|
let token_b_holding_id = vec_id[2];
|
|
|
|
|
let token_b_definition_id = vec_id[3];
|
|
|
|
|
let token_lp_definition_id = vec_id[4];
|
|
|
|
|
let user_a_holding_id = vec_id[5];
|
|
|
|
|
let user_b_holding_id = vec_id[6];
|
|
|
|
|
let vault_a_id = vec_id[7];
|
|
|
|
|
let vault_b_id = vec_id[8];
|
|
|
|
|
let user_lp_holding_id = vec_id[9];
|
|
|
|
|
let pool_id = vec_id[10];
|
|
|
|
|
let pool_lp_holding_id = vec_id[11];
|
2025-11-25 23:06:47 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let mut instruction: Vec<u8> = Vec::new();
|
|
|
|
|
instruction.push(3);
|
2025-11-25 23:06:47 -05:00
|
|
|
|
|
|
|
|
let message = public_transaction::Message::try_new(
|
2025-11-26 21:44:57 -05:00
|
|
|
Program::amm().id(),
|
|
|
|
|
vec![
|
2025-12-02 15:20:16 -05:00
|
|
|
pool_id,
|
|
|
|
|
vault_a_id,
|
|
|
|
|
vault_b_id,
|
|
|
|
|
pool_lp_holding_id,
|
|
|
|
|
user_a_holding_id,
|
|
|
|
|
user_b_holding_id,
|
|
|
|
|
user_lp_holding_id,
|
2025-11-26 21:44:57 -05:00
|
|
|
],
|
|
|
|
|
vec![
|
2025-12-02 15:20:16 -05:00
|
|
|
state.get_account_by_id(&pool_id).nonce,
|
|
|
|
|
state.get_account_by_id(&user_lp_holding_id).nonce,
|
|
|
|
|
state.get_account_by_id(&vault_a_id).nonce,
|
|
|
|
|
state.get_account_by_id(&vault_b_id).nonce,
|
2025-11-26 21:44:57 -05:00
|
|
|
],
|
2025-11-25 23:06:47 -05:00
|
|
|
instruction,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
let witness_set = public_transaction::WitnessSet::for_message(
|
|
|
|
|
&message,
|
2025-11-26 21:44:57 -05:00
|
|
|
&[&pool_key, &user_lp_holding_key, &vault_a_key, &vault_b_key],
|
2025-11-25 23:06:47 -05:00
|
|
|
);
|
2025-11-26 21:44:57 -05:00
|
|
|
|
2025-11-21 19:17:50 -05:00
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
2025-11-25 23:06:47 -05:00
|
|
|
state.transition_from_public_transaction(&tx).unwrap();
|
|
|
|
|
|
2025-12-02 15:20:16 -05:00
|
|
|
let pool_post = state.get_account_by_id(&pool_id);
|
|
|
|
|
let vault_a_post = state.get_account_by_id(&vault_a_id);
|
|
|
|
|
let vault_b_post = state.get_account_by_id(&vault_b_id);
|
|
|
|
|
let user_a_post = state.get_account_by_id(&user_a_holding_id);
|
|
|
|
|
let user_b_post = state.get_account_by_id(&user_b_holding_id);
|
|
|
|
|
let user_lp_post = state.get_account_by_id(&user_lp_holding_id);
|
2025-11-25 23:06:47 -05:00
|
|
|
|
2025-11-27 06:07:28 -05:00
|
|
|
//TODO: this accounts for the initial balance for User_LP
|
2025-12-04 21:53:58 -05:00
|
|
|
let delta_lp : u128 = (init_balance_a*init_balance_a)/init_balance_a;
|
2025-11-26 21:44:57 -05:00
|
|
|
|
|
|
|
|
let expected_pool = Account {
|
|
|
|
|
program_owner: Program::amm().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: PoolDefinition::into_data(
|
|
|
|
|
PoolDefinition {
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_token_a_id: token_a_definition_id,
|
|
|
|
|
definition_token_b_id: token_b_definition_id,
|
|
|
|
|
vault_a_addr: vault_a_id,
|
|
|
|
|
vault_b_addr: vault_b_id,
|
|
|
|
|
liquidity_pool_id: token_lp_definition_id,
|
2025-11-28 08:11:04 -05:00
|
|
|
liquidity_pool_cap: init_balance_a - delta_lp,
|
2025-11-26 21:44:57 -05:00
|
|
|
reserve_a: 0,
|
|
|
|
|
reserve_b: 0,
|
|
|
|
|
token_program_id: Program::token().id(),
|
|
|
|
|
}),
|
|
|
|
|
nonce: 2,
|
|
|
|
|
};
|
2025-11-21 19:17:50 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let expected_vault_a = Account {
|
|
|
|
|
program_owner: Program::token().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: TokenHolding::into_data(
|
|
|
|
|
TokenHolding{
|
|
|
|
|
account_type: TOKEN_HOLDING_TYPE,
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_id: token_a_definition_id,
|
2025-12-04 21:53:58 -05:00
|
|
|
balance: 0,
|
2025-11-26 21:44:57 -05:00
|
|
|
}),
|
|
|
|
|
nonce: 1
|
|
|
|
|
};
|
2025-11-21 19:17:50 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let expected_vault_b = Account {
|
|
|
|
|
program_owner: Program::token().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: TokenHolding::into_data(
|
|
|
|
|
TokenHolding{
|
|
|
|
|
account_type: TOKEN_HOLDING_TYPE,
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_id: token_b_definition_id,
|
2025-12-04 21:53:58 -05:00
|
|
|
balance: 0,
|
2025-11-26 21:44:57 -05:00
|
|
|
}),
|
|
|
|
|
nonce: 1
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let expected_user_a = Account {
|
|
|
|
|
program_owner: Program::token().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: TokenHolding::into_data(
|
|
|
|
|
TokenHolding{
|
|
|
|
|
account_type: TOKEN_HOLDING_TYPE,
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_id: token_a_definition_id,
|
2025-11-26 21:44:57 -05:00
|
|
|
balance: user_a_amount,
|
|
|
|
|
}),
|
|
|
|
|
nonce: 1
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let expected_user_b = Account {
|
|
|
|
|
program_owner: Program::token().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: TokenHolding::into_data(
|
|
|
|
|
TokenHolding{
|
|
|
|
|
account_type: TOKEN_HOLDING_TYPE,
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_id: token_b_definition_id,
|
2025-11-26 21:44:57 -05:00
|
|
|
balance: user_b_amount,
|
|
|
|
|
}),
|
|
|
|
|
nonce: 1
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let expected_user_lp = Account {
|
|
|
|
|
program_owner: Program::token().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: TokenHolding::into_data(
|
|
|
|
|
TokenHolding{
|
|
|
|
|
account_type: TOKEN_HOLDING_TYPE,
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_id: token_lp_definition_id,
|
2025-11-26 21:44:57 -05:00
|
|
|
balance: 0,
|
|
|
|
|
}),
|
|
|
|
|
nonce: 1
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
assert!(vault_a_post == expected_vault_a);
|
|
|
|
|
assert!(vault_b_post == expected_vault_b);
|
|
|
|
|
assert!(user_a_post == expected_user_a);
|
|
|
|
|
assert!(user_b_post == expected_user_b);
|
|
|
|
|
assert!(user_lp_post == expected_user_lp);
|
2025-11-28 08:11:04 -05:00
|
|
|
assert!(pool_post.data == expected_pool.data);
|
2025-11-26 21:44:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_simple_amm_add() {
|
2025-12-02 15:20:16 -05:00
|
|
|
let (state, vec_private_keys, vec_id, vec_amounts) = initialize_amm();
|
2025-11-26 21:44:57 -05:00
|
|
|
let mut state: V02State = state;
|
|
|
|
|
|
2025-12-04 21:53:58 -05:00
|
|
|
let init_balance_a = vec_amounts[0];
|
|
|
|
|
let init_balance_b = vec_amounts[1];
|
|
|
|
|
let user_a_amount = vec_amounts[2];
|
|
|
|
|
let user_b_amount = vec_amounts[3];
|
2025-11-26 21:44:57 -05:00
|
|
|
|
|
|
|
|
let _token_a_holding_key = &vec_private_keys[0];
|
|
|
|
|
let _token_a_definition_key = &vec_private_keys[1];
|
|
|
|
|
let _token_b_holding_key = &vec_private_keys[2];
|
|
|
|
|
let _token_b_definition_key = &vec_private_keys[3];
|
|
|
|
|
let pool_lp_definition_key = &vec_private_keys[4];
|
|
|
|
|
let user_a_holding_key = &vec_private_keys[5];
|
|
|
|
|
let user_b_holding_key = &vec_private_keys[6];
|
|
|
|
|
let vault_a_key = &vec_private_keys[7];
|
|
|
|
|
let vault_b_key = &vec_private_keys[8];
|
|
|
|
|
let user_lp_holding_key = &vec_private_keys[9];
|
|
|
|
|
let pool_key = &vec_private_keys[10];
|
|
|
|
|
let pool_lp_holding_key = &vec_private_keys[11];
|
|
|
|
|
|
2025-12-02 15:20:16 -05:00
|
|
|
let token_a_holding_id = vec_id[0];
|
|
|
|
|
let token_a_definition_id = vec_id[1];
|
|
|
|
|
let token_b_holding_id = vec_id[2];
|
|
|
|
|
let token_b_definition_id = vec_id[3];
|
|
|
|
|
let token_lp_definition_id = vec_id[4];
|
|
|
|
|
let user_a_holding_id = vec_id[5];
|
|
|
|
|
let user_b_holding_id = vec_id[6];
|
|
|
|
|
let vault_a_id = vec_id[7];
|
|
|
|
|
let vault_b_id = vec_id[8];
|
|
|
|
|
let user_lp_holding_id = vec_id[9];
|
|
|
|
|
let pool_id = vec_id[10];
|
|
|
|
|
let pool_lp_holding_id = vec_id[11];
|
2025-11-26 21:44:57 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
let add_a: u128 = 500;
|
|
|
|
|
let add_b: u128 = 500;
|
2025-12-02 15:20:16 -05:00
|
|
|
let main_addr = token_a_definition_id;
|
2025-11-26 21:44:57 -05:00
|
|
|
let mut instruction: Vec<u8> = Vec::new();
|
|
|
|
|
instruction.push(2);
|
|
|
|
|
instruction.extend_from_slice(&add_a.to_le_bytes());
|
|
|
|
|
instruction.extend_from_slice(&add_b.to_le_bytes());
|
|
|
|
|
instruction.extend_from_slice(main_addr.value());
|
2025-11-21 19:17:50 -05:00
|
|
|
|
2025-11-25 23:06:47 -05:00
|
|
|
let message = public_transaction::Message::try_new(
|
2025-11-26 21:44:57 -05:00
|
|
|
Program::amm().id(),
|
|
|
|
|
vec![
|
2025-12-02 15:20:16 -05:00
|
|
|
pool_id,
|
|
|
|
|
vault_a_id,
|
|
|
|
|
vault_b_id,
|
|
|
|
|
pool_lp_holding_id,
|
|
|
|
|
user_a_holding_id,
|
|
|
|
|
user_b_holding_id,
|
|
|
|
|
user_lp_holding_id,
|
2025-11-26 21:44:57 -05:00
|
|
|
],
|
|
|
|
|
vec![
|
2025-12-02 15:20:16 -05:00
|
|
|
state.get_account_by_id(&pool_id).nonce,
|
|
|
|
|
state.get_account_by_id(&pool_lp_holding_id).nonce,
|
|
|
|
|
state.get_account_by_id(&user_a_holding_id).nonce,
|
|
|
|
|
state.get_account_by_id(&user_b_holding_id).nonce,
|
2025-11-26 21:44:57 -05:00
|
|
|
],
|
2025-11-25 23:06:47 -05:00
|
|
|
instruction,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
2025-11-21 19:17:50 -05:00
|
|
|
|
2025-11-25 23:06:47 -05:00
|
|
|
let witness_set = public_transaction::WitnessSet::for_message(
|
|
|
|
|
&message,
|
2025-11-26 21:44:57 -05:00
|
|
|
&[
|
|
|
|
|
&pool_key,
|
|
|
|
|
&pool_lp_holding_key,
|
|
|
|
|
&user_a_holding_key,
|
|
|
|
|
&user_b_holding_key,
|
|
|
|
|
],
|
2025-11-25 23:06:47 -05:00
|
|
|
);
|
2025-11-26 21:44:57 -05:00
|
|
|
|
2025-11-25 23:06:47 -05:00
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
state.transition_from_public_transaction(&tx).unwrap();
|
|
|
|
|
|
2025-12-02 15:20:16 -05:00
|
|
|
let pool_post = state.get_account_by_id(&pool_id);
|
|
|
|
|
let vault_a_post = state.get_account_by_id(&vault_a_id);
|
|
|
|
|
let vault_b_post = state.get_account_by_id(&vault_b_id);
|
|
|
|
|
let user_a_post = state.get_account_by_id(&user_a_holding_id);
|
|
|
|
|
let user_b_post = state.get_account_by_id(&user_b_holding_id);
|
|
|
|
|
let user_lp_post = state.get_account_by_id(&user_lp_holding_id);
|
2025-11-26 21:44:57 -05:00
|
|
|
|
|
|
|
|
let expected_pool = Account {
|
|
|
|
|
program_owner: Program::amm().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: PoolDefinition::into_data(
|
|
|
|
|
PoolDefinition {
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_token_a_id: token_a_definition_id,
|
|
|
|
|
definition_token_b_id: token_b_definition_id,
|
|
|
|
|
vault_a_addr: vault_a_id,
|
|
|
|
|
vault_b_addr: vault_b_id,
|
|
|
|
|
liquidity_pool_id: token_lp_definition_id,
|
2025-11-26 21:44:57 -05:00
|
|
|
liquidity_pool_cap: init_balance_a + add_a,
|
|
|
|
|
reserve_a: init_balance_a + add_a,
|
|
|
|
|
reserve_b: init_balance_b + add_b,
|
|
|
|
|
token_program_id: Program::token().id(),
|
|
|
|
|
}),
|
|
|
|
|
nonce: 2,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let expected_vault_a = Account {
|
|
|
|
|
program_owner: Program::token().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: TokenHolding::into_data(
|
|
|
|
|
TokenHolding{
|
|
|
|
|
account_type: TOKEN_HOLDING_TYPE,
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_id: token_a_definition_id,
|
2025-12-04 21:53:58 -05:00
|
|
|
balance: init_balance_a + add_a,
|
2025-11-26 21:44:57 -05:00
|
|
|
}),
|
|
|
|
|
nonce: 0
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let expected_vault_b = Account {
|
|
|
|
|
program_owner: Program::token().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: TokenHolding::into_data(
|
|
|
|
|
TokenHolding{
|
|
|
|
|
account_type: TOKEN_HOLDING_TYPE,
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_id: token_b_definition_id,
|
2025-12-04 21:53:58 -05:00
|
|
|
balance: init_balance_b + add_b,
|
2025-11-26 21:44:57 -05:00
|
|
|
}),
|
|
|
|
|
nonce: 0
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let expected_user_a = Account {
|
|
|
|
|
program_owner: Program::token().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: TokenHolding::into_data(
|
|
|
|
|
TokenHolding{
|
|
|
|
|
account_type: TOKEN_HOLDING_TYPE,
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_id: token_a_definition_id,
|
2025-11-26 21:44:57 -05:00
|
|
|
balance: user_a_amount - init_balance_a - add_a,
|
|
|
|
|
}),
|
|
|
|
|
nonce: 2
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let expected_user_b = Account {
|
|
|
|
|
program_owner: Program::token().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: TokenHolding::into_data(
|
|
|
|
|
TokenHolding{
|
|
|
|
|
account_type: TOKEN_HOLDING_TYPE,
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_id: token_b_definition_id,
|
2025-11-26 21:44:57 -05:00
|
|
|
balance: user_b_amount - init_balance_b - add_b,
|
|
|
|
|
}),
|
|
|
|
|
nonce: 2
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let expected_user_lp = Account {
|
|
|
|
|
program_owner: Program::token().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: TokenHolding::into_data(
|
|
|
|
|
TokenHolding{
|
|
|
|
|
account_type: TOKEN_HOLDING_TYPE,
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_id: token_lp_definition_id,
|
2025-12-04 21:53:58 -05:00
|
|
|
balance: init_balance_a + add_a,
|
2025-11-26 21:44:57 -05:00
|
|
|
}),
|
|
|
|
|
nonce: 0
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
assert!(vault_a_post == expected_vault_a);
|
|
|
|
|
assert!(vault_b_post == expected_vault_b);
|
|
|
|
|
assert!(user_a_post == expected_user_a);
|
|
|
|
|
assert!(user_b_post == expected_user_b);
|
|
|
|
|
assert!(user_lp_post == expected_user_lp);
|
|
|
|
|
assert!(pool_post == expected_pool);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_simple_amm_swap_1() {
|
2025-12-02 15:20:16 -05:00
|
|
|
let (state, vec_private_keys, vec_id, vec_amounts) = initialize_amm();
|
2025-11-26 21:44:57 -05:00
|
|
|
let mut state: V02State = state;
|
|
|
|
|
|
2025-12-04 21:53:58 -05:00
|
|
|
let init_balance_a = vec_amounts[0];
|
|
|
|
|
let init_balance_b = vec_amounts[1];
|
|
|
|
|
let user_a_amount = vec_amounts[2];
|
|
|
|
|
let user_b_amount = vec_amounts[3];
|
2025-11-26 21:44:57 -05:00
|
|
|
|
|
|
|
|
let token_a_holding_key = &vec_private_keys[0];
|
|
|
|
|
let token_a_definition_key = &vec_private_keys[1];
|
|
|
|
|
let token_b_holding_key = &vec_private_keys[2];
|
|
|
|
|
let token_b_definition_key = &vec_private_keys[3];
|
|
|
|
|
let pool_lp_definition_key = &vec_private_keys[4];
|
|
|
|
|
let user_a_holding_key = &vec_private_keys[5];
|
|
|
|
|
let user_b_holding_key = &vec_private_keys[6];
|
|
|
|
|
let vault_a_key = &vec_private_keys[7];
|
|
|
|
|
let vault_b_key = &vec_private_keys[8];
|
|
|
|
|
let user_lp_holding_key = &vec_private_keys[9];
|
|
|
|
|
let pool_key = &vec_private_keys[10];
|
|
|
|
|
let pool_lp_holding_key = &vec_private_keys[11];
|
|
|
|
|
|
2025-12-02 15:20:16 -05:00
|
|
|
let token_a_holding_id = vec_id[0];
|
|
|
|
|
let token_a_definition_id = vec_id[1];
|
|
|
|
|
let token_b_holding_id = vec_id[2];
|
|
|
|
|
let token_b_definition_id = vec_id[3];
|
|
|
|
|
let token_lp_definition_id = vec_id[4];
|
|
|
|
|
let user_a_holding_id = vec_id[5];
|
|
|
|
|
let user_b_holding_id = vec_id[6];
|
|
|
|
|
let vault_a_id = vec_id[7];
|
|
|
|
|
let vault_b_id = vec_id[8];
|
|
|
|
|
let user_lp_holding_id = vec_id[9];
|
|
|
|
|
let pool_id = vec_id[10];
|
|
|
|
|
let pool_lp_holding_id = vec_id[11];
|
2025-11-25 23:06:47 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
//Initialize swap user accounts
|
|
|
|
|
let swap_user_a_holding_key = PrivateKey::try_new([21; 32]).unwrap();
|
2025-12-02 15:20:16 -05:00
|
|
|
let swap_user_a_holding_id =
|
|
|
|
|
AccountId::from(&PublicKey::new_from_private_key(&swap_user_a_holding_key));
|
2025-11-26 21:44:57 -05:00
|
|
|
let swap_user_a_amount: u128 = 5000;
|
|
|
|
|
|
|
|
|
|
let swap_user_b_holding_key = PrivateKey::try_new([22; 32]).unwrap();
|
2025-12-02 15:20:16 -05:00
|
|
|
let swap_user_b_holding_id =
|
|
|
|
|
AccountId::from(&PublicKey::new_from_private_key(&swap_user_b_holding_key));
|
2025-11-26 21:44:57 -05:00
|
|
|
let swap_user_b_amount: u128 = 5000;
|
|
|
|
|
|
|
|
|
|
// Initialize Swap User account for Token A
|
|
|
|
|
let mut instruction: [u8; 23] = [0; 23];
|
2025-11-25 23:06:47 -05:00
|
|
|
instruction[0] = 1; //transfer
|
2025-11-26 21:44:57 -05:00
|
|
|
instruction[1..17].copy_from_slice(&swap_user_a_amount.to_le_bytes());
|
2025-11-25 23:06:47 -05:00
|
|
|
|
|
|
|
|
let message = public_transaction::Message::try_new(
|
|
|
|
|
Program::token().id(),
|
2025-12-02 15:20:16 -05:00
|
|
|
vec![token_a_holding_id, swap_user_a_holding_id],
|
2025-11-26 21:44:57 -05:00
|
|
|
vec![2],
|
2025-11-25 23:06:47 -05:00
|
|
|
instruction,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let witness_set =
|
|
|
|
|
public_transaction::WitnessSet::for_message(&message, &[&token_a_holding_key]);
|
2025-11-25 23:06:47 -05:00
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
state.transition_from_public_transaction(&tx).unwrap();
|
|
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
// Initialize Swap User account for Token B
|
|
|
|
|
let mut instruction: [u8; 23] = [0; 23];
|
2025-11-25 23:06:47 -05:00
|
|
|
instruction[0] = 1; //transfer
|
2025-11-26 21:44:57 -05:00
|
|
|
instruction[1..17].copy_from_slice(&swap_user_b_amount.to_le_bytes());
|
2025-11-25 23:06:47 -05:00
|
|
|
|
|
|
|
|
let message = public_transaction::Message::try_new(
|
|
|
|
|
Program::token().id(),
|
2025-12-02 15:20:16 -05:00
|
|
|
vec![token_b_holding_id, swap_user_b_holding_id],
|
|
|
|
|
vec![state.get_account_by_id(&token_b_holding_id).nonce],
|
2025-11-25 23:06:47 -05:00
|
|
|
instruction,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let witness_set =
|
|
|
|
|
public_transaction::WitnessSet::for_message(&message, &[&token_b_holding_key]);
|
2025-11-25 23:06:47 -05:00
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
state.transition_from_public_transaction(&tx).unwrap();
|
|
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
// Initialize Swap
|
2025-12-02 15:20:16 -05:00
|
|
|
let main_addr = token_a_definition_id;
|
2025-11-26 21:44:57 -05:00
|
|
|
let swap_a: u128 = 500;
|
2025-11-25 23:06:47 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let mut instruction: Vec<u8> = Vec::new();
|
|
|
|
|
instruction.push(1);
|
|
|
|
|
instruction.extend_from_slice(&swap_a.to_le_bytes());
|
|
|
|
|
instruction.extend_from_slice(main_addr.value());
|
2025-11-25 23:06:47 -05:00
|
|
|
|
|
|
|
|
let message = public_transaction::Message::try_new(
|
|
|
|
|
Program::amm().id(),
|
|
|
|
|
vec![
|
2025-12-02 15:20:16 -05:00
|
|
|
pool_id,
|
|
|
|
|
vault_a_id,
|
|
|
|
|
vault_b_id,
|
|
|
|
|
swap_user_a_holding_id,
|
|
|
|
|
swap_user_b_holding_id,
|
2025-11-25 23:06:47 -05:00
|
|
|
],
|
|
|
|
|
vec![
|
2025-12-02 15:20:16 -05:00
|
|
|
state.get_account_by_id(&pool_id).nonce,
|
|
|
|
|
state.get_account_by_id(&vault_a_id).nonce,
|
|
|
|
|
state.get_account_by_id(&vault_b_id).nonce,
|
2025-11-26 21:44:57 -05:00
|
|
|
state
|
2025-12-02 15:20:16 -05:00
|
|
|
.get_account_by_id(&swap_user_a_holding_id)
|
2025-11-26 21:44:57 -05:00
|
|
|
.nonce,
|
|
|
|
|
state
|
2025-12-02 15:20:16 -05:00
|
|
|
.get_account_by_id(&swap_user_b_holding_id)
|
2025-11-26 21:44:57 -05:00
|
|
|
.nonce,
|
2025-11-25 23:06:47 -05:00
|
|
|
],
|
|
|
|
|
instruction,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
let witness_set = public_transaction::WitnessSet::for_message(
|
|
|
|
|
&message,
|
|
|
|
|
&[
|
|
|
|
|
&pool_key,
|
2025-11-26 21:44:57 -05:00
|
|
|
&vault_a_key,
|
|
|
|
|
&vault_b_key,
|
|
|
|
|
&swap_user_a_holding_key,
|
|
|
|
|
&swap_user_b_holding_key,
|
2025-11-25 23:06:47 -05:00
|
|
|
],
|
|
|
|
|
);
|
2025-11-26 21:44:57 -05:00
|
|
|
|
2025-11-25 23:06:47 -05:00
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
state.transition_from_public_transaction(&tx).unwrap();
|
|
|
|
|
|
|
|
|
|
|
2025-12-02 15:20:16 -05:00
|
|
|
let pool_post = state.get_account_by_id(&pool_id);
|
|
|
|
|
let vault_a_post = state.get_account_by_id(&vault_a_id);
|
|
|
|
|
let vault_b_post = state.get_account_by_id(&vault_b_id);
|
|
|
|
|
let swap_user_a_post = state.get_account_by_id(&swap_user_a_holding_id);
|
|
|
|
|
let swap_user_b_post = state.get_account_by_id(&swap_user_b_holding_id);
|
2025-11-26 21:44:57 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
let withdraw_b = (init_balance_b * swap_a)/(init_balance_a + swap_a);
|
|
|
|
|
|
|
|
|
|
let expected_pool = Account {
|
|
|
|
|
program_owner: Program::amm().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: PoolDefinition::into_data(
|
|
|
|
|
PoolDefinition {
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_token_a_id: token_a_definition_id,
|
|
|
|
|
definition_token_b_id: token_b_definition_id,
|
|
|
|
|
vault_a_addr: vault_a_id,
|
|
|
|
|
vault_b_addr: vault_b_id,
|
|
|
|
|
liquidity_pool_id: token_lp_definition_id,
|
2025-11-26 21:44:57 -05:00
|
|
|
liquidity_pool_cap: init_balance_a,
|
|
|
|
|
reserve_a: init_balance_a + swap_a,
|
|
|
|
|
reserve_b: init_balance_b - withdraw_b,
|
|
|
|
|
token_program_id: Program::token().id(),
|
|
|
|
|
}),
|
|
|
|
|
nonce: 2,
|
|
|
|
|
};
|
2025-11-25 23:06:47 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let expected_vault_a = Account {
|
|
|
|
|
program_owner: Program::token().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: TokenHolding::into_data(
|
|
|
|
|
TokenHolding{
|
|
|
|
|
account_type: TOKEN_HOLDING_TYPE,
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_id: token_a_definition_id,
|
2025-12-04 21:53:58 -05:00
|
|
|
balance: init_balance_a + swap_a,
|
2025-11-26 21:44:57 -05:00
|
|
|
}),
|
|
|
|
|
nonce: 1
|
|
|
|
|
};
|
2025-11-25 23:06:47 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let expected_vault_b = Account {
|
|
|
|
|
program_owner: Program::token().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: TokenHolding::into_data(
|
|
|
|
|
TokenHolding{
|
|
|
|
|
account_type: TOKEN_HOLDING_TYPE,
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_id: token_b_definition_id,
|
2025-12-04 21:53:58 -05:00
|
|
|
balance: init_balance_b - withdraw_b,
|
2025-11-26 21:44:57 -05:00
|
|
|
}),
|
|
|
|
|
nonce: 1
|
|
|
|
|
};
|
2025-11-25 23:06:47 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let expected_swap_user_a = Account {
|
|
|
|
|
program_owner: Program::token().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: TokenHolding::into_data(
|
|
|
|
|
TokenHolding{
|
|
|
|
|
account_type: TOKEN_HOLDING_TYPE,
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_id: token_a_definition_id,
|
2025-11-26 21:44:57 -05:00
|
|
|
balance: swap_user_a_amount - swap_a,
|
|
|
|
|
}),
|
|
|
|
|
nonce: 1
|
|
|
|
|
};
|
2025-11-25 23:06:47 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let expected_swap_user_b = Account {
|
|
|
|
|
program_owner: Program::token().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: TokenHolding::into_data(
|
|
|
|
|
TokenHolding{
|
|
|
|
|
account_type: TOKEN_HOLDING_TYPE,
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_id: token_b_definition_id,
|
2025-11-26 21:44:57 -05:00
|
|
|
balance: swap_user_b_amount + withdraw_b,
|
|
|
|
|
}),
|
|
|
|
|
nonce: 1
|
|
|
|
|
};
|
2025-11-25 23:06:47 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
assert!(vault_a_post == expected_vault_a);
|
|
|
|
|
assert!(vault_b_post == expected_vault_b);
|
|
|
|
|
assert!(swap_user_a_post == expected_swap_user_a);
|
|
|
|
|
assert!(swap_user_b_post == expected_swap_user_b);
|
|
|
|
|
assert!(pool_post == expected_pool);
|
2025-11-25 23:06:47 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_simple_amm_swap_2() {
|
2025-12-02 15:20:16 -05:00
|
|
|
let (state, vec_private_keys, vec_id, vec_amounts) = initialize_amm();
|
2025-11-26 21:44:57 -05:00
|
|
|
let mut state: V02State = state;
|
|
|
|
|
|
2025-12-04 21:53:58 -05:00
|
|
|
let init_balance_a = vec_amounts[0];
|
|
|
|
|
let init_balance_b = vec_amounts[1];
|
|
|
|
|
let user_a_amount = vec_amounts[2];
|
|
|
|
|
let user_b_amount = vec_amounts[3];
|
2025-11-26 21:44:57 -05:00
|
|
|
|
|
|
|
|
let token_a_holding_key = &vec_private_keys[0];
|
|
|
|
|
let token_a_definition_key = &vec_private_keys[1];
|
|
|
|
|
let token_b_holding_key = &vec_private_keys[2];
|
|
|
|
|
let token_b_definition_key = &vec_private_keys[3];
|
|
|
|
|
let pool_lp_definition_key = &vec_private_keys[4];
|
|
|
|
|
let user_a_holding_key = &vec_private_keys[5];
|
|
|
|
|
let user_b_holding_key = &vec_private_keys[6];
|
|
|
|
|
let vault_a_key = &vec_private_keys[7];
|
|
|
|
|
let vault_b_key = &vec_private_keys[8];
|
|
|
|
|
let user_lp_holding_key = &vec_private_keys[9];
|
|
|
|
|
let pool_key = &vec_private_keys[10];
|
|
|
|
|
let pool_lp_holding_key = &vec_private_keys[11];
|
|
|
|
|
|
2025-12-02 15:20:16 -05:00
|
|
|
let token_a_holding_id = vec_id[0];
|
|
|
|
|
let token_a_definition_id = vec_id[1];
|
|
|
|
|
let token_b_holding_id = vec_id[2];
|
|
|
|
|
let token_b_definition_id = vec_id[3];
|
|
|
|
|
let token_lp_definition_id = vec_id[4];
|
|
|
|
|
let user_a_holding_id = vec_id[5];
|
|
|
|
|
let user_b_holding_id = vec_id[6];
|
|
|
|
|
let vault_a_id = vec_id[7];
|
|
|
|
|
let vault_b_id = vec_id[8];
|
|
|
|
|
let user_lp_holding_id = vec_id[9];
|
|
|
|
|
let pool_id = vec_id[10];
|
|
|
|
|
let pool_lp_holding_id = vec_id[11];
|
2025-11-25 23:06:47 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
//Initialize swap user accounts
|
|
|
|
|
let swap_user_a_holding_key = PrivateKey::try_new([21; 32]).unwrap();
|
2025-12-02 15:20:16 -05:00
|
|
|
let swap_user_a_holding_id =
|
|
|
|
|
AccountId::from(&PublicKey::new_from_private_key(&swap_user_a_holding_key));
|
2025-11-26 21:44:57 -05:00
|
|
|
let swap_user_a_amount: u128 = 5000;
|
|
|
|
|
|
|
|
|
|
let swap_user_b_holding_key = PrivateKey::try_new([22; 32]).unwrap();
|
2025-12-02 15:20:16 -05:00
|
|
|
let swap_user_b_holding_id =
|
|
|
|
|
AccountId::from(&PublicKey::new_from_private_key(&swap_user_b_holding_key));
|
2025-11-26 21:44:57 -05:00
|
|
|
let swap_user_b_amount: u128 = 5000;
|
|
|
|
|
|
|
|
|
|
// Initialize Swap User account for Token A
|
|
|
|
|
let mut instruction: [u8; 23] = [0; 23];
|
2025-11-25 23:06:47 -05:00
|
|
|
instruction[0] = 1; //transfer
|
2025-11-26 21:44:57 -05:00
|
|
|
instruction[1..17].copy_from_slice(&swap_user_a_amount.to_le_bytes());
|
2025-11-25 23:06:47 -05:00
|
|
|
|
|
|
|
|
let message = public_transaction::Message::try_new(
|
|
|
|
|
Program::token().id(),
|
2025-12-02 15:20:16 -05:00
|
|
|
vec![token_a_holding_id, swap_user_a_holding_id],
|
2025-11-26 21:44:57 -05:00
|
|
|
vec![2],
|
2025-11-25 23:06:47 -05:00
|
|
|
instruction,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let witness_set =
|
|
|
|
|
public_transaction::WitnessSet::for_message(&message, &[&token_a_holding_key]);
|
2025-11-25 23:06:47 -05:00
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
state.transition_from_public_transaction(&tx).unwrap();
|
|
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
// Initialize Swap User account for Token B
|
|
|
|
|
let mut instruction: [u8; 23] = [0; 23];
|
2025-11-25 23:06:47 -05:00
|
|
|
instruction[0] = 1; //transfer
|
2025-11-26 21:44:57 -05:00
|
|
|
instruction[1..17].copy_from_slice(&swap_user_b_amount.to_le_bytes());
|
2025-11-25 23:06:47 -05:00
|
|
|
|
|
|
|
|
let message = public_transaction::Message::try_new(
|
|
|
|
|
Program::token().id(),
|
2025-12-02 15:20:16 -05:00
|
|
|
vec![token_b_holding_id, swap_user_b_holding_id],
|
|
|
|
|
vec![state.get_account_by_id(&token_b_holding_id).nonce],
|
2025-11-25 23:06:47 -05:00
|
|
|
instruction,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let witness_set =
|
|
|
|
|
public_transaction::WitnessSet::for_message(&message, &[&token_b_holding_key]);
|
2025-11-25 23:06:47 -05:00
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
state.transition_from_public_transaction(&tx).unwrap();
|
|
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
// Swap
|
2025-12-02 15:20:16 -05:00
|
|
|
let main_addr = token_b_definition_id;
|
2025-11-26 21:44:57 -05:00
|
|
|
let swap_b: u128 = 500;
|
2025-11-25 23:06:47 -05:00
|
|
|
|
|
|
|
|
let mut instruction: Vec<u8> = Vec::new();
|
2025-11-26 21:44:57 -05:00
|
|
|
instruction.push(1);
|
|
|
|
|
instruction.extend_from_slice(&swap_b.to_le_bytes());
|
|
|
|
|
instruction.extend_from_slice(main_addr.value());
|
|
|
|
|
|
2025-11-25 23:06:47 -05:00
|
|
|
let message = public_transaction::Message::try_new(
|
|
|
|
|
Program::amm().id(),
|
|
|
|
|
vec![
|
2025-12-02 15:20:16 -05:00
|
|
|
pool_id,
|
|
|
|
|
vault_a_id,
|
|
|
|
|
vault_b_id,
|
|
|
|
|
swap_user_a_holding_id,
|
|
|
|
|
swap_user_b_holding_id,
|
2025-11-26 21:44:57 -05:00
|
|
|
],
|
|
|
|
|
vec![
|
2025-12-02 15:20:16 -05:00
|
|
|
state.get_account_by_id(&pool_id).nonce,
|
|
|
|
|
state.get_account_by_id(&vault_a_id).nonce,
|
|
|
|
|
state.get_account_by_id(&vault_b_id).nonce,
|
2025-11-26 21:44:57 -05:00
|
|
|
state
|
2025-12-02 15:20:16 -05:00
|
|
|
.get_account_by_id(&swap_user_a_holding_id)
|
2025-11-26 21:44:57 -05:00
|
|
|
.nonce,
|
|
|
|
|
state
|
2025-12-02 15:20:16 -05:00
|
|
|
.get_account_by_id(&swap_user_b_holding_id)
|
2025-11-26 21:44:57 -05:00
|
|
|
.nonce,
|
2025-11-25 23:06:47 -05:00
|
|
|
],
|
|
|
|
|
instruction,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
let witness_set = public_transaction::WitnessSet::for_message(
|
|
|
|
|
&message,
|
|
|
|
|
&[
|
|
|
|
|
&pool_key,
|
2025-11-26 21:44:57 -05:00
|
|
|
&vault_a_key,
|
|
|
|
|
&vault_b_key,
|
|
|
|
|
&swap_user_a_holding_key,
|
|
|
|
|
&swap_user_b_holding_key,
|
2025-11-25 23:06:47 -05:00
|
|
|
],
|
|
|
|
|
);
|
2025-11-26 21:44:57 -05:00
|
|
|
|
2025-11-25 23:06:47 -05:00
|
|
|
let tx = PublicTransaction::new(message, witness_set);
|
|
|
|
|
state.transition_from_public_transaction(&tx).unwrap();
|
|
|
|
|
|
2025-12-02 15:20:16 -05:00
|
|
|
let pool_post = state.get_account_by_id(&pool_id);
|
|
|
|
|
let vault_a_post = state.get_account_by_id(&vault_a_id);
|
|
|
|
|
let vault_b_post = state.get_account_by_id(&vault_b_id);
|
|
|
|
|
let swap_user_a_post = state.get_account_by_id(&swap_user_a_holding_id);
|
|
|
|
|
let swap_user_b_post = state.get_account_by_id(&swap_user_b_holding_id);
|
2025-11-26 21:44:57 -05:00
|
|
|
|
|
|
|
|
let withdraw_a = (init_balance_a * swap_b)/(init_balance_b + swap_b);
|
|
|
|
|
|
|
|
|
|
let expected_pool = Account {
|
|
|
|
|
program_owner: Program::amm().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: PoolDefinition::into_data(
|
|
|
|
|
PoolDefinition {
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_token_a_id: token_a_definition_id,
|
|
|
|
|
definition_token_b_id: token_b_definition_id,
|
|
|
|
|
vault_a_addr: vault_a_id,
|
|
|
|
|
vault_b_addr: vault_b_id,
|
|
|
|
|
liquidity_pool_id: token_lp_definition_id,
|
2025-11-26 21:44:57 -05:00
|
|
|
liquidity_pool_cap: init_balance_a,
|
|
|
|
|
reserve_a: init_balance_a - withdraw_a,
|
|
|
|
|
reserve_b: init_balance_b + swap_b,
|
|
|
|
|
token_program_id: Program::token().id(),
|
|
|
|
|
}),
|
|
|
|
|
nonce: 2,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let expected_vault_a = Account {
|
|
|
|
|
program_owner: Program::token().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: TokenHolding::into_data(
|
|
|
|
|
TokenHolding{
|
|
|
|
|
account_type: TOKEN_HOLDING_TYPE,
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_id: token_a_definition_id,
|
2025-12-04 21:53:58 -05:00
|
|
|
balance: init_balance_a - withdraw_a,
|
2025-11-26 21:44:57 -05:00
|
|
|
}),
|
|
|
|
|
nonce: 1
|
|
|
|
|
};
|
2025-11-25 23:06:47 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
let expected_vault_b = Account {
|
|
|
|
|
program_owner: Program::token().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: TokenHolding::into_data(
|
|
|
|
|
TokenHolding{
|
|
|
|
|
account_type: TOKEN_HOLDING_TYPE,
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_id: token_b_definition_id,
|
2025-12-04 21:53:58 -05:00
|
|
|
balance: init_balance_b + swap_b,
|
2025-11-26 21:44:57 -05:00
|
|
|
}),
|
|
|
|
|
nonce: 1
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let expected_swap_user_a = Account {
|
|
|
|
|
program_owner: Program::token().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: TokenHolding::into_data(
|
|
|
|
|
TokenHolding{
|
|
|
|
|
account_type: TOKEN_HOLDING_TYPE,
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_id: token_a_definition_id,
|
2025-11-26 21:44:57 -05:00
|
|
|
balance: swap_user_a_amount + withdraw_a,
|
|
|
|
|
}),
|
|
|
|
|
nonce: 1
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let expected_swap_user_b = Account {
|
|
|
|
|
program_owner: Program::token().id(),
|
|
|
|
|
balance: 0u128,
|
|
|
|
|
data: TokenHolding::into_data(
|
|
|
|
|
TokenHolding{
|
|
|
|
|
account_type: TOKEN_HOLDING_TYPE,
|
2025-12-02 15:20:16 -05:00
|
|
|
definition_id: token_b_definition_id,
|
2025-11-26 21:44:57 -05:00
|
|
|
balance: swap_user_b_amount - swap_b,
|
|
|
|
|
}),
|
|
|
|
|
nonce: 1
|
|
|
|
|
};
|
2025-11-25 23:06:47 -05:00
|
|
|
|
2025-11-26 21:44:57 -05:00
|
|
|
assert!(vault_a_post == expected_vault_a);
|
|
|
|
|
assert!(vault_b_post == expected_vault_b);
|
|
|
|
|
assert!(swap_user_a_post == expected_swap_user_a);
|
|
|
|
|
assert!(swap_user_b_post == expected_swap_user_b);
|
|
|
|
|
assert!(pool_post == expected_pool);
|
|
|
|
|
|
|
|
|
|
}
|
2025-08-13 01:33:11 -03:00
|
|
|
}
|