From c1283d4a0cabde313a7eda67616936d7a1fc7036 Mon Sep 17 00:00:00 2001 From: Pravdyvy Date: Thu, 18 Dec 2025 11:44:38 +0200 Subject: [PATCH] fix: public execution finalized --- integration_tests/src/test_suite_map.rs | 203 +++- wallet/src/cli/programs/amm.rs | 373 +++----- .../src/cli/programs/native_token_transfer.rs | 11 +- wallet/src/cli/programs/pinata.rs | 3 +- wallet/src/cli/programs/token.rs | 19 +- wallet/src/lib.rs | 39 +- wallet/src/privacy_preserving_tx.rs | 11 +- wallet/src/program_facades/amm.rs | 904 +++++------------- wallet/src/program_facades/mod.rs | 135 +++ 9 files changed, 752 insertions(+), 946 deletions(-) diff --git a/integration_tests/src/test_suite_map.rs b/integration_tests/src/test_suite_map.rs index e1102dc..ead73d5 100644 --- a/integration_tests/src/test_suite_map.rs +++ b/integration_tests/src/test_suite_map.rs @@ -2224,30 +2224,18 @@ pub fn prepare_function_map() -> HashMap { .account; assert_eq!( - user_holding_a_acc.data.as_ref(), - &[ - 1, 216, 180, 23, 229, 146, 37, 77, 185, 234, 186, 245, 33, 228, 197, 251, 244, 10, - 137, 115, 157, 2, 246, 137, 52, 234, 64, 155, 199, 101, 15, 43, 83, 4, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ] + u128::from_le_bytes(user_holding_a_acc.data[33..].try_into().unwrap()), + 4 ); assert_eq!( - user_holding_b_acc.data.as_ref(), - &[ - 1, 244, 191, 88, 67, 111, 12, 245, 25, 212, 169, 62, 209, 159, 73, 107, 101, 173, - 88, 13, 55, 71, 91, 113, 88, 208, 91, 136, 222, 139, 2, 97, 110, 4, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ] + u128::from_le_bytes(user_holding_b_acc.data[33..].try_into().unwrap()), + 4 ); assert_eq!( - user_holding_lp_acc.data.as_ref(), - &[ - 1, 16, 61, 24, 200, 168, 141, 91, 149, 164, 35, 114, 25, 6, 40, 204, 181, 95, 39, - 59, 56, 56, 96, 222, 157, 226, 48, 111, 53, 30, 1, 41, 94, 3, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 - ] + u128::from_le_bytes(user_holding_lp_acc.data[33..].try_into().unwrap()), + 3 ); info!("=================== AMM DEFINITION FINISHED ==============="); @@ -2287,30 +2275,173 @@ pub fn prepare_function_map() -> HashMap { .account; assert_eq!( - user_holding_a_acc.data.as_ref(), - &[ - 1, 216, 180, 23, 229, 146, 37, 77, 185, 234, 186, 245, 33, 228, 197, 251, 244, 10, - 137, 115, 157, 2, 246, 137, 52, 234, 64, 155, 199, 101, 15, 43, 83, 2, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ] + u128::from_le_bytes(user_holding_a_acc.data[33..].try_into().unwrap()), + 2 ); assert_eq!( - user_holding_b_acc.data.as_ref(), - &[ - 1, 244, 191, 88, 67, 111, 12, 245, 25, 212, 169, 62, 209, 159, 73, 107, 101, 173, - 88, 13, 55, 71, 91, 113, 88, 208, 91, 136, 222, 139, 2, 97, 110, 5, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ] + u128::from_le_bytes(user_holding_b_acc.data[33..].try_into().unwrap()), + 5 ); assert_eq!( - user_holding_lp_acc.data.as_ref(), - &[ - 1, 16, 61, 24, 200, 168, 141, 91, 149, 164, 35, 114, 25, 6, 40, 204, 181, 95, 39, - 59, 56, 56, 96, 222, 157, 226, 48, 111, 53, 30, 1, 41, 94, 3, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 - ] + u128::from_le_bytes(user_holding_lp_acc.data[33..].try_into().unwrap()), + 3 + ); + + info!("=================== FIRST SWAP FINISHED ==============="); + + // Make swap + + let subcommand = AmmProgramAgnosticSubcommand::Swap { + user_holding_a: make_public_account_input_from_str(&recipient_account_id_1.to_string()), + user_holding_b: make_public_account_input_from_str(&recipient_account_id_2.to_string()), + amount_in: 2, + min_amount_out: 1, + token_definition: definition_account_id_2.to_string(), + }; + + wallet::cli::execute_subcommand(Command::AMM(subcommand)) + .await + .unwrap(); + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let user_holding_a_acc = seq_client + .get_account(recipient_account_id_1.to_string()) + .await + .unwrap() + .account; + + let user_holding_b_acc = seq_client + .get_account(recipient_account_id_2.to_string()) + .await + .unwrap() + .account; + + let user_holding_lp_acc = seq_client + .get_account(user_holding_lp.to_string()) + .await + .unwrap() + .account; + + assert_eq!( + u128::from_le_bytes(user_holding_a_acc.data[33..].try_into().unwrap()), + 4 + ); + + assert_eq!( + u128::from_le_bytes(user_holding_b_acc.data[33..].try_into().unwrap()), + 3 + ); + + assert_eq!( + u128::from_le_bytes(user_holding_lp_acc.data[33..].try_into().unwrap()), + 3 + ); + + info!("=================== SECOND SWAP FINISHED ==============="); + + // Add liquidity + + let subcommand = AmmProgramAgnosticSubcommand::AddLiquidity { + user_holding_a: make_public_account_input_from_str(&recipient_account_id_1.to_string()), + user_holding_b: make_public_account_input_from_str(&recipient_account_id_2.to_string()), + user_holding_lp: make_public_account_input_from_str(&user_holding_lp.to_string()), + min_amount_lp: 1, + max_amount_a: 2, + max_amount_b: 2, + }; + + wallet::cli::execute_subcommand(Command::AMM(subcommand)) + .await + .unwrap(); + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let user_holding_a_acc = seq_client + .get_account(recipient_account_id_1.to_string()) + .await + .unwrap() + .account; + + let user_holding_b_acc = seq_client + .get_account(recipient_account_id_2.to_string()) + .await + .unwrap() + .account; + + let user_holding_lp_acc = seq_client + .get_account(user_holding_lp.to_string()) + .await + .unwrap() + .account; + + assert_eq!( + u128::from_le_bytes(user_holding_a_acc.data[33..].try_into().unwrap()), + 3 + ); + + assert_eq!( + u128::from_le_bytes(user_holding_b_acc.data[33..].try_into().unwrap()), + 1 + ); + + assert_eq!( + u128::from_le_bytes(user_holding_lp_acc.data[33..].try_into().unwrap()), + 4 + ); + + info!("=================== ADD LIQ FINISHED ==============="); + + // Remove liquidity + + let subcommand = AmmProgramAgnosticSubcommand::RemoveLiquidity { + user_holding_a: make_public_account_input_from_str(&recipient_account_id_1.to_string()), + user_holding_b: make_public_account_input_from_str(&recipient_account_id_2.to_string()), + user_holding_lp: make_public_account_input_from_str(&user_holding_lp.to_string()), + balance_lp: 2, + max_amount_a: 1, + max_amount_b: 1, + }; + + wallet::cli::execute_subcommand(Command::AMM(subcommand)) + .await + .unwrap(); + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let user_holding_a_acc = seq_client + .get_account(recipient_account_id_1.to_string()) + .await + .unwrap() + .account; + + let user_holding_b_acc = seq_client + .get_account(recipient_account_id_2.to_string()) + .await + .unwrap() + .account; + + let user_holding_lp_acc = seq_client + .get_account(user_holding_lp.to_string()) + .await + .unwrap() + .account; + + assert_eq!( + u128::from_le_bytes(user_holding_a_acc.data[33..].try_into().unwrap()), + 5 + ); + + assert_eq!( + u128::from_le_bytes(user_holding_b_acc.data[33..].try_into().unwrap()), + 4 + ); + + assert_eq!( + u128::from_le_bytes(user_holding_lp_acc.data[33..].try_into().unwrap()), + 2 ); info!("Success!"); diff --git a/wallet/src/cli/programs/amm.rs b/wallet/src/cli/programs/amm.rs index 69e3a01..1104e0d 100644 --- a/wallet/src/cli/programs/amm.rs +++ b/wallet/src/cli/programs/amm.rs @@ -1,10 +1,11 @@ use anyhow::Result; use clap::Subcommand; +use nssa::AccountId; use crate::{ - PrivacyPreservingAccount, WalletCore, + WalletCore, cli::{SubcommandReturnValue, WalletSubcommand}, - helperfunctions::parse_addr_with_privacy_prefix, + helperfunctions::{AccountPrivacyKind, parse_addr_with_privacy_prefix}, program_facades::amm::AMM, }; @@ -14,6 +15,8 @@ pub enum AmmProgramAgnosticSubcommand { /// Produce a new token /// /// user_holding_a and user_holding_b must be owned. + /// + /// Only public execution allowed New { /// user_holding_a - valid 32 byte base58 string with privacy prefix #[arg(long)] @@ -32,6 +35,8 @@ pub enum AmmProgramAgnosticSubcommand { /// Swap with variable privacy /// /// The account associated with swapping token must be owned + /// + /// Only public execution allowed Swap { /// user_holding_a - valid 32 byte base58 string with privacy prefix #[arg(long)] @@ -50,19 +55,9 @@ pub enum AmmProgramAgnosticSubcommand { /// Add liquidity with variable privacy /// /// user_holding_a and user_holding_b must be owned. + /// + /// Only public execution allowed AddLiquidity { - /// amm_pool - valid 32 byte base58 string with privacy prefix - #[arg(long)] - amm_pool: String, - /// vault_holding_a - valid 32 byte base58 string with privacy prefix - #[arg(long)] - vault_holding_a: String, - /// vault_holding_b - valid 32 byte base58 string with privacy prefix - #[arg(long)] - vault_holding_b: String, - /// pool_lp - valid 32 byte base58 string with privacy prefix - #[arg(long)] - pool_lp: String, /// user_holding_a - valid 32 byte base58 string with privacy prefix #[arg(long)] user_holding_a: String, @@ -82,19 +77,9 @@ pub enum AmmProgramAgnosticSubcommand { /// Remove liquidity with variable privacy /// /// user_holding_lp must be owned. + /// + /// Only public execution allowed RemoveLiquidity { - /// amm_pool - valid 32 byte base58 string with privacy prefix - #[arg(long)] - amm_pool: String, - /// vault_holding_a - valid 32 byte base58 string with privacy prefix - #[arg(long)] - vault_holding_a: String, - /// vault_holding_b - valid 32 byte base58 string with privacy prefix - #[arg(long)] - vault_holding_b: String, - /// pool_lp - valid 32 byte base58 string with privacy prefix - #[arg(long)] - pool_lp: String, /// user_holding_a - valid 32 byte base58 string with privacy prefix #[arg(long)] user_holding_a: String, @@ -126,43 +111,43 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { balance_a, balance_b, } => { - let user_holding_a = PrivacyPreservingAccount::parse_with_privacy( - parse_addr_with_privacy_prefix(&user_holding_a)?, - )?; - let user_holding_b = PrivacyPreservingAccount::parse_with_privacy( - parse_addr_with_privacy_prefix(&user_holding_b)?, - )?; - let user_holding_lp = PrivacyPreservingAccount::parse_with_privacy( - parse_addr_with_privacy_prefix(&user_holding_lp)?, - )?; + let (user_holding_a, user_holding_a_privacy) = + parse_addr_with_privacy_prefix(&user_holding_a)?; + let (user_holding_b, user_holding_b_privacy) = + parse_addr_with_privacy_prefix(&user_holding_b)?; + let (user_holding_lp, user_holding_lp_privacy) = + parse_addr_with_privacy_prefix(&user_holding_lp)?; - let is_public_tx = [&user_holding_a, &user_holding_b, &user_holding_lp] - .into_iter() - .all(|acc| acc.is_public()); + let user_holding_a: AccountId = user_holding_a.parse()?; + let user_holding_b: AccountId = user_holding_b.parse()?; + let user_holding_lp: AccountId = user_holding_lp.parse()?; - if is_public_tx { - AMM(wallet_core) - .send_new_amm_definition( - user_holding_a, - user_holding_b, - user_holding_lp, - balance_a, - balance_b, - ) - .await?; - Ok(SubcommandReturnValue::Empty) - } else { - AMM(wallet_core) - .send_new_amm_definition_privacy_preserving( - user_holding_a, - user_holding_b, - user_holding_lp, - balance_a, - balance_b, - ) - .await?; - // ToDo: change into correct return value - Ok(SubcommandReturnValue::Empty) + match ( + user_holding_a_privacy, + user_holding_b_privacy, + user_holding_lp_privacy, + ) { + ( + AccountPrivacyKind::Public, + AccountPrivacyKind::Public, + AccountPrivacyKind::Public, + ) => { + AMM(wallet_core) + .send_new_amm_definition( + user_holding_a, + user_holding_b, + user_holding_lp, + balance_a, + balance_b, + ) + .await?; + + Ok(SubcommandReturnValue::Empty) + } + _ => { + // ToDo: Implement after private multi-chain calls is available + anyhow::bail!("Only public execution allowed for AMM calls"); + } } } AmmProgramAgnosticSubcommand::Swap { @@ -172,47 +157,35 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { min_amount_out, token_definition, } => { - let user_holding_a = PrivacyPreservingAccount::parse_with_privacy( - parse_addr_with_privacy_prefix(&user_holding_a)?, - )?; - let user_holding_b = PrivacyPreservingAccount::parse_with_privacy( - parse_addr_with_privacy_prefix(&user_holding_b)?, - )?; + let (user_holding_a, user_holding_a_privacy) = + parse_addr_with_privacy_prefix(&user_holding_a)?; + let (user_holding_b, user_holding_b_privacy) = + parse_addr_with_privacy_prefix(&user_holding_b)?; - let is_public_tx = [&user_holding_a, &user_holding_b] - .into_iter() - .all(|acc| acc.is_public()); + let user_holding_a: AccountId = user_holding_a.parse()?; + let user_holding_b: AccountId = user_holding_b.parse()?; - if is_public_tx { - AMM(wallet_core) - .send_swap( - user_holding_a, - user_holding_b, - amount_in, - min_amount_out, - token_definition.parse()?, - ) - .await?; - Ok(SubcommandReturnValue::Empty) - } else { - AMM(wallet_core) - .send_swap_privacy_preserving( - user_holding_a, - user_holding_b, - amount_in, - min_amount_out, - token_definition.parse()?, - ) - .await?; - // ToDo: change into correct return value - Ok(SubcommandReturnValue::Empty) + match (user_holding_a_privacy, user_holding_b_privacy) { + (AccountPrivacyKind::Public, AccountPrivacyKind::Public) => { + AMM(wallet_core) + .send_swap( + user_holding_a, + user_holding_b, + amount_in, + min_amount_out, + token_definition.parse()?, + ) + .await?; + + Ok(SubcommandReturnValue::Empty) + } + _ => { + // ToDo: Implement after private multi-chain calls is available + anyhow::bail!("Only public execution allowed for AMM calls"); + } } } AmmProgramAgnosticSubcommand::AddLiquidity { - amm_pool, - vault_holding_a, - vault_holding_b, - pool_lp, user_holding_a, user_holding_b, user_holding_lp, @@ -220,80 +193,47 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { max_amount_a, max_amount_b, } => { - let amm_pool = PrivacyPreservingAccount::parse_with_privacy( - parse_addr_with_privacy_prefix(&amm_pool)?, - )?; - let vault_holding_a = PrivacyPreservingAccount::parse_with_privacy( - parse_addr_with_privacy_prefix(&vault_holding_a)?, - )?; - let vault_holding_b = PrivacyPreservingAccount::parse_with_privacy( - parse_addr_with_privacy_prefix(&vault_holding_b)?, - )?; - let pool_lp = PrivacyPreservingAccount::parse_with_privacy( - parse_addr_with_privacy_prefix(&pool_lp)?, - )?; - let user_holding_a = PrivacyPreservingAccount::parse_with_privacy( - parse_addr_with_privacy_prefix(&user_holding_a)?, - )?; - let user_holding_b = PrivacyPreservingAccount::parse_with_privacy( - parse_addr_with_privacy_prefix(&user_holding_b)?, - )?; - let user_holding_lp = PrivacyPreservingAccount::parse_with_privacy( - parse_addr_with_privacy_prefix(&user_holding_lp)?, - )?; + let (user_holding_a, user_holding_a_privacy) = + parse_addr_with_privacy_prefix(&user_holding_a)?; + let (user_holding_b, user_holding_b_privacy) = + parse_addr_with_privacy_prefix(&user_holding_b)?; + let (user_holding_lp, user_holding_lp_privacy) = + parse_addr_with_privacy_prefix(&user_holding_lp)?; - let is_public_tx = [ - &amm_pool, - &vault_holding_a, - &vault_holding_b, - &pool_lp, - &user_holding_a, - &user_holding_b, - &user_holding_lp, - ] - .into_iter() - .all(|acc| acc.is_public()); + let user_holding_a: AccountId = user_holding_a.parse()?; + let user_holding_b: AccountId = user_holding_b.parse()?; + let user_holding_lp: AccountId = user_holding_lp.parse()?; - if is_public_tx { - AMM(wallet_core) - .send_add_liq( - amm_pool, - vault_holding_a, - vault_holding_b, - pool_lp, - user_holding_a, - user_holding_b, - user_holding_lp, - min_amount_lp, - max_amount_a, - max_amount_b, - ) - .await?; - Ok(SubcommandReturnValue::Empty) - } else { - AMM(wallet_core) - .send_add_liq_privacy_preserving( - amm_pool, - vault_holding_a, - vault_holding_b, - pool_lp, - user_holding_a, - user_holding_b, - user_holding_lp, - min_amount_lp, - max_amount_a, - max_amount_b, - ) - .await?; - // ToDo: change into correct return value - Ok(SubcommandReturnValue::Empty) + match ( + user_holding_a_privacy, + user_holding_b_privacy, + user_holding_lp_privacy, + ) { + ( + AccountPrivacyKind::Public, + AccountPrivacyKind::Public, + AccountPrivacyKind::Public, + ) => { + AMM(wallet_core) + .send_add_liq( + user_holding_a, + user_holding_b, + user_holding_lp, + min_amount_lp, + max_amount_a, + max_amount_b, + ) + .await?; + + Ok(SubcommandReturnValue::Empty) + } + _ => { + // ToDo: Implement after private multi-chain calls is available + anyhow::bail!("Only public execution allowed for AMM calls"); + } } } AmmProgramAgnosticSubcommand::RemoveLiquidity { - amm_pool, - vault_holding_a, - vault_holding_b, - pool_lp, user_holding_a, user_holding_b, user_holding_lp, @@ -301,73 +241,44 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { max_amount_a, max_amount_b, } => { - let amm_pool = PrivacyPreservingAccount::parse_with_privacy( - parse_addr_with_privacy_prefix(&amm_pool)?, - )?; - let vault_holding_a = PrivacyPreservingAccount::parse_with_privacy( - parse_addr_with_privacy_prefix(&vault_holding_a)?, - )?; - let vault_holding_b = PrivacyPreservingAccount::parse_with_privacy( - parse_addr_with_privacy_prefix(&vault_holding_b)?, - )?; - let pool_lp = PrivacyPreservingAccount::parse_with_privacy( - parse_addr_with_privacy_prefix(&pool_lp)?, - )?; - let user_holding_a = PrivacyPreservingAccount::parse_with_privacy( - parse_addr_with_privacy_prefix(&user_holding_a)?, - )?; - let user_holding_b = PrivacyPreservingAccount::parse_with_privacy( - parse_addr_with_privacy_prefix(&user_holding_b)?, - )?; - let user_holding_lp = PrivacyPreservingAccount::parse_with_privacy( - parse_addr_with_privacy_prefix(&user_holding_lp)?, - )?; + let (user_holding_a, user_holding_a_privacy) = + parse_addr_with_privacy_prefix(&user_holding_a)?; + let (user_holding_b, user_holding_b_privacy) = + parse_addr_with_privacy_prefix(&user_holding_b)?; + let (user_holding_lp, user_holding_lp_privacy) = + parse_addr_with_privacy_prefix(&user_holding_lp)?; - let is_public_tx = [ - &amm_pool, - &vault_holding_a, - &vault_holding_b, - &pool_lp, - &user_holding_a, - &user_holding_b, - &user_holding_lp, - ] - .into_iter() - .all(|acc| acc.is_public()); + let user_holding_a: AccountId = user_holding_a.parse()?; + let user_holding_b: AccountId = user_holding_b.parse()?; + let user_holding_lp: AccountId = user_holding_lp.parse()?; - if is_public_tx { - AMM(wallet_core) - .send_remove_liq( - amm_pool, - vault_holding_a, - vault_holding_b, - pool_lp, - user_holding_a, - user_holding_b, - user_holding_lp, - balance_lp, - max_amount_a, - max_amount_b, - ) - .await?; - Ok(SubcommandReturnValue::Empty) - } else { - AMM(wallet_core) - .send_remove_liq_privacy_preserving( - amm_pool, - vault_holding_a, - vault_holding_b, - pool_lp, - user_holding_a, - user_holding_b, - user_holding_lp, - balance_lp, - max_amount_a, - max_amount_b, - ) - .await?; - // ToDo: change into correct return value - Ok(SubcommandReturnValue::Empty) + match ( + user_holding_a_privacy, + user_holding_b_privacy, + user_holding_lp_privacy, + ) { + ( + AccountPrivacyKind::Public, + AccountPrivacyKind::Public, + AccountPrivacyKind::Public, + ) => { + AMM(wallet_core) + .send_remove_liq( + user_holding_a, + user_holding_b, + user_holding_lp, + balance_lp, + max_amount_a, + max_amount_b, + ) + .await?; + + Ok(SubcommandReturnValue::Empty) + } + _ => { + // ToDo: Implement after private multi-chain calls is available + anyhow::bail!("Only public execution allowed for AMM calls"); + } } } } diff --git a/wallet/src/cli/programs/native_token_transfer.rs b/wallet/src/cli/programs/native_token_transfer.rs index 9dc72ae..7868a7c 100644 --- a/wallet/src/cli/programs/native_token_transfer.rs +++ b/wallet/src/cli/programs/native_token_transfer.rs @@ -4,6 +4,7 @@ use common::transaction::NSSATransaction; use nssa::AccountId; use crate::{ + AccDecodeData::Decode, WalletCore, cli::{SubcommandReturnValue, WalletSubcommand}, helperfunctions::{AccountPrivacyKind, parse_addr_with_privacy_prefix}, @@ -87,7 +88,7 @@ impl WalletSubcommand for AuthTransferSubcommand { .await?; if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { - let acc_decode_data = vec![(secret, account_id)]; + let acc_decode_data = vec![Decode(secret, account_id)]; wallet_core.decode_insert_privacy_preserving_transaction_results( tx, @@ -328,7 +329,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate { .await?; if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { - let acc_decode_data = vec![(secret_from, from), (secret_to, to)]; + let acc_decode_data = vec![Decode(secret_from, from), Decode(secret_to, to)]; wallet_core.decode_insert_privacy_preserving_transaction_results( tx, @@ -372,7 +373,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate { .await?; if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { - let acc_decode_data = vec![(secret_from, from)]; + let acc_decode_data = vec![Decode(secret_from, from)]; wallet_core.decode_insert_privacy_preserving_transaction_results( tx, @@ -412,7 +413,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded { .await?; if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { - let acc_decode_data = vec![(secret, to)]; + let acc_decode_data = vec![Decode(secret, to)]; wallet_core.decode_insert_privacy_preserving_transaction_results( tx, @@ -491,7 +492,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand { .await?; if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { - let acc_decode_data = vec![(secret, from)]; + let acc_decode_data = vec![Decode(secret, from)]; wallet_core.decode_insert_privacy_preserving_transaction_results( tx, diff --git a/wallet/src/cli/programs/pinata.rs b/wallet/src/cli/programs/pinata.rs index 7712a7c..e0b6570 100644 --- a/wallet/src/cli/programs/pinata.rs +++ b/wallet/src/cli/programs/pinata.rs @@ -3,6 +3,7 @@ use clap::Subcommand; use common::{PINATA_BASE58, transaction::NSSATransaction}; use crate::{ + AccDecodeData::Decode, WalletCore, cli::{SubcommandReturnValue, WalletSubcommand}, helperfunctions::{AccountPrivacyKind, parse_addr_with_privacy_prefix}, @@ -159,7 +160,7 @@ impl WalletSubcommand for PinataProgramSubcommandPrivate { println!("Transaction data is {transfer_tx:?}"); if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { - let acc_decode_data = vec![(secret_winner, winner_account_id)]; + let acc_decode_data = vec![Decode(secret_winner, winner_account_id)]; wallet_core.decode_insert_privacy_preserving_transaction_results( tx, diff --git a/wallet/src/cli/programs/token.rs b/wallet/src/cli/programs/token.rs index 4480a1e..880c2c1 100644 --- a/wallet/src/cli/programs/token.rs +++ b/wallet/src/cli/programs/token.rs @@ -4,6 +4,7 @@ use common::transaction::NSSATransaction; use nssa::AccountId; use crate::{ + AccDecodeData::Decode, WalletCore, cli::{SubcommandReturnValue, WalletSubcommand}, helperfunctions::{AccountPrivacyKind, parse_addr_with_privacy_prefix}, @@ -421,8 +422,8 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate { if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { let acc_decode_data = vec![ - (secret_sender, sender_account_id), - (secret_recipient, recipient_account_id), + Decode(secret_sender, sender_account_id), + Decode(secret_recipient, recipient_account_id), ]; wallet_core.decode_insert_privacy_preserving_transaction_results( @@ -473,7 +474,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate { .await?; if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { - let acc_decode_data = vec![(secret_sender, sender_account_id)]; + let acc_decode_data = vec![Decode(secret_sender, sender_account_id)]; wallet_core.decode_insert_privacy_preserving_transaction_results( tx, @@ -521,7 +522,7 @@ impl WalletSubcommand for TokenProgramSubcommandDeshielded { .await?; if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { - let acc_decode_data = vec![(secret_sender, sender_account_id)]; + let acc_decode_data = vec![Decode(secret_sender, sender_account_id)]; wallet_core.decode_insert_privacy_preserving_transaction_results( tx, @@ -614,7 +615,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded { .await?; if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { - let acc_decode_data = vec![(secret_recipient, recipient_account_id)]; + let acc_decode_data = vec![Decode(secret_recipient, recipient_account_id)]; wallet_core.decode_insert_privacy_preserving_transaction_results( tx, @@ -673,8 +674,8 @@ impl WalletSubcommand for CreateNewTokenProgramSubcommand { if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { let acc_decode_data = vec![ - (secret_definition, definition_account_id), - (secret_supply, supply_account_id), + Decode(secret_definition, definition_account_id), + Decode(secret_supply, supply_account_id), ]; wallet_core.decode_insert_privacy_preserving_transaction_results( @@ -723,7 +724,7 @@ impl WalletSubcommand for CreateNewTokenProgramSubcommand { .await?; if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { - let acc_decode_data = vec![(secret_definition, definition_account_id)]; + let acc_decode_data = vec![Decode(secret_definition, definition_account_id)]; wallet_core.decode_insert_privacy_preserving_transaction_results( tx, @@ -771,7 +772,7 @@ impl WalletSubcommand for CreateNewTokenProgramSubcommand { .await?; if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { - let acc_decode_data = vec![(secret_supply, supply_account_id)]; + let acc_decode_data = vec![Decode(secret_supply, supply_account_id)]; wallet_core.decode_insert_privacy_preserving_transaction_results( tx, diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 1218d25..3dfc10b 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -38,6 +38,11 @@ pub mod poller; mod privacy_preserving_tx; pub mod program_facades; +pub enum AccDecodeData { + Skip, + Decode(nssa_core::SharedSecretKey, AccountId), +} + pub struct WalletCore { pub storage: WalletChainStore, pub poller: TxPoller, @@ -218,28 +223,32 @@ impl WalletCore { pub fn decode_insert_privacy_preserving_transaction_results( &mut self, tx: nssa::privacy_preserving_transaction::PrivacyPreservingTransaction, - acc_decode_data: &[(nssa_core::SharedSecretKey, AccountId)], + acc_decode_mask: &[AccDecodeData], ) -> Result<()> { - for (output_index, (secret, acc_account_id)) in acc_decode_data.iter().enumerate() { - let acc_ead = tx.message.encrypted_private_post_states[output_index].clone(); - let acc_comm = tx.message.new_commitments[output_index].clone(); + for (output_index, acc_decode_data) in acc_decode_mask.iter().enumerate() { + match acc_decode_data { + AccDecodeData::Decode(secret, acc_account_id) => { + let acc_ead = tx.message.encrypted_private_post_states[output_index].clone(); + let acc_comm = tx.message.new_commitments[output_index].clone(); - let res_acc = nssa_core::EncryptionScheme::decrypt( - &acc_ead.ciphertext, - secret, - &acc_comm, - output_index as u32, - ) - .unwrap(); + let res_acc = nssa_core::EncryptionScheme::decrypt( + &acc_ead.ciphertext, + secret, + &acc_comm, + output_index as u32, + ) + .unwrap(); - println!("Received new acc {res_acc:#?}"); + println!("Received new acc {res_acc:#?}"); - self.storage - .insert_private_account_data(*acc_account_id, res_acc); + self.storage + .insert_private_account_data(*acc_account_id, res_acc); + } + AccDecodeData::Skip => {} + } } println!("Transaction data is {:?}", tx.message); - Ok(()) } diff --git a/wallet/src/privacy_preserving_tx.rs b/wallet/src/privacy_preserving_tx.rs index d0d6629..a243e5d 100644 --- a/wallet/src/privacy_preserving_tx.rs +++ b/wallet/src/privacy_preserving_tx.rs @@ -8,7 +8,7 @@ use nssa_core::{ encryption::{EphemeralPublicKey, IncomingViewingPublicKey}, }; -use crate::{WalletCore, helperfunctions::AccountPrivacyKind}; +use crate::WalletCore; #[derive(Clone)] pub enum PrivacyPreservingAccount { @@ -21,15 +21,6 @@ pub enum PrivacyPreservingAccount { } impl PrivacyPreservingAccount { - pub fn parse_with_privacy(input: (String, AccountPrivacyKind)) -> Result { - let acc_id: AccountId = input.0.parse()?; - - match input.1 { - AccountPrivacyKind::Public => Ok(Self::Public(acc_id)), - AccountPrivacyKind::Private => Ok(Self::PrivateOwned(acc_id)), - } - } - pub fn is_public(&self) -> bool { matches!(&self, Self::Public(_)) } diff --git a/wallet/src/program_facades/amm.rs b/wallet/src/program_facades/amm.rs index a0691f6..d33ad47 100644 --- a/wallet/src/program_facades/amm.rs +++ b/wallet/src/program_facades/amm.rs @@ -1,279 +1,108 @@ use common::{error::ExecutionFailureKind, rpc_primitives::requests::SendTxResponse}; -use nssa::{AccountId, ProgramId, program::Program}; -use nssa_core::{SharedSecretKey, program::PdaSeed}; -use serde::{Serialize, ser::SerializeSeq}; +use nssa::{AccountId, program::Program}; -use crate::{PrivacyPreservingAccount, WalletCore, cli::account::TokenHolding}; - -fn compute_pool_pda( - amm_program_id: ProgramId, - definition_token_a_id: AccountId, - definition_token_b_id: AccountId, -) -> AccountId { - AccountId::from(( - &amm_program_id, - &compute_pool_pda_seed(definition_token_a_id, definition_token_b_id), - )) -} - -fn compute_pool_pda_seed( - definition_token_a_id: AccountId, - definition_token_b_id: AccountId, -) -> PdaSeed { - use risc0_zkvm::sha::{Impl, Sha256}; - - let mut i: usize = 0; - let (token_1, token_2) = loop { - if definition_token_a_id.value()[i] > definition_token_b_id.value()[i] { - let token_1 = definition_token_a_id; - let token_2 = definition_token_b_id; - break (token_1, token_2); - } else if definition_token_a_id.value()[i] < definition_token_b_id.value()[i] { - let token_1 = definition_token_b_id; - let token_2 = definition_token_a_id; - break (token_1, token_2); - } - - if i == 32 { - panic!("Definitions match"); - } else { - i += 1; - } - }; - - let mut bytes = [0; 64]; - bytes[0..32].copy_from_slice(&token_1.to_bytes()); - bytes[32..].copy_from_slice(&token_2.to_bytes()); - - PdaSeed::new( - Impl::hash_bytes(&bytes) - .as_bytes() - .try_into() - .expect("Hash output must be exactly 32 bytes long"), - ) -} - -fn compute_vault_pda( - amm_program_id: ProgramId, - pool_id: AccountId, - definition_token_id: AccountId, -) -> AccountId { - AccountId::from(( - &amm_program_id, - &compute_vault_pda_seed(pool_id, definition_token_id), - )) -} - -fn compute_vault_pda_seed(pool_id: AccountId, definition_token_id: AccountId) -> PdaSeed { - use risc0_zkvm::sha::{Impl, Sha256}; - - let mut bytes = [0; 64]; - bytes[0..32].copy_from_slice(&pool_id.to_bytes()); - bytes[32..].copy_from_slice(&definition_token_id.to_bytes()); - - PdaSeed::new( - Impl::hash_bytes(&bytes) - .as_bytes() - .try_into() - .expect("Hash output must be exactly 32 bytes long"), - ) -} - -fn compute_liquidity_token_pda(amm_program_id: ProgramId, pool_id: AccountId) -> AccountId { - AccountId::from((&amm_program_id, &compute_liquidity_token_pda_seed(pool_id))) -} - -fn compute_liquidity_token_pda_seed(pool_id: AccountId) -> PdaSeed { - use risc0_zkvm::sha::{Impl, Sha256}; - - let mut bytes = [0; 64]; - bytes[0..32].copy_from_slice(&pool_id.to_bytes()); - bytes[32..].copy_from_slice(&[0; 32]); - - PdaSeed::new( - Impl::hash_bytes(&bytes) - .as_bytes() - .try_into() - .expect("Hash output must be exactly 32 bytes long"), - ) -} - -struct OrphanHack65BytesInput([u32; 65]); - -impl OrphanHack65BytesInput { - fn expand(orig: [u8; 65]) -> Self { - let mut res = [0u32; 65]; - - for (idx, val) in orig.into_iter().enumerate() { - res[idx] = val as u32; - } - - Self(res) - } -} - -impl Serialize for OrphanHack65BytesInput { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let mut seq = serializer.serialize_seq(Some(65))?; - for word in self.0 { - seq.serialize_element(&word)?; - } - seq.end() - } -} - -struct OrphanHack49BytesInput([u32; 49]); - -impl OrphanHack49BytesInput { - fn expand(orig: [u8; 49]) -> Self { - let mut res = [0u32; 49]; - - for (idx, val) in orig.into_iter().enumerate() { - res[idx] = val as u32; - } - - Self(res) - } - - fn words(&self) -> Vec { - self.0.to_vec() - } -} - -impl Serialize for OrphanHack49BytesInput { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let mut seq = serializer.serialize_seq(Some(49))?; - for word in self.0 { - seq.serialize_element(&word)?; - } - seq.end() - } -} +use crate::{ + WalletCore, + cli::account::TokenHolding, + program_facades::{ + OrphanHack49BytesInput, OrphanHack65BytesInput, compute_liquidity_token_pda, + compute_pool_pda, compute_vault_pda, + }, +}; pub struct AMM<'w>(pub &'w WalletCore); impl AMM<'_> { - #[allow(clippy::too_many_arguments)] pub async fn send_new_amm_definition( &self, - user_holding_a: PrivacyPreservingAccount, - user_holding_b: PrivacyPreservingAccount, - user_holding_lp: PrivacyPreservingAccount, + user_holding_a: AccountId, + user_holding_b: AccountId, + user_holding_lp: AccountId, balance_a: u128, balance_b: u128, ) -> Result { let (instruction, program) = amm_program_preparation_definition(balance_a, balance_b); - match (user_holding_a, user_holding_b, user_holding_lp) { - ( - PrivacyPreservingAccount::Public(user_holding_a), - PrivacyPreservingAccount::Public(user_holding_b), - PrivacyPreservingAccount::Public(user_holding_lp), - ) => { - let amm_program_id = Program::amm().id(); + let amm_program_id = Program::amm().id(); - let Ok(user_a_acc) = self.0.get_account_public(user_holding_a).await else { - return Err(ExecutionFailureKind::SequencerError); - }; - let Ok(user_b_acc) = self.0.get_account_public(user_holding_b).await else { - return Err(ExecutionFailureKind::SequencerError); - }; + let Ok(user_a_acc) = self.0.get_account_public(user_holding_a).await else { + return Err(ExecutionFailureKind::SequencerError); + }; + let Ok(user_b_acc) = self.0.get_account_public(user_holding_b).await else { + return Err(ExecutionFailureKind::SequencerError); + }; - let definition_token_a_id = TokenHolding::parse(&user_a_acc.data) - .ok_or(ExecutionFailureKind::AccountDataError(user_holding_a))? - .definition_id; - let definition_token_b_id = TokenHolding::parse(&user_b_acc.data) - .ok_or(ExecutionFailureKind::AccountDataError(user_holding_a))? - .definition_id; + let definition_token_a_id = TokenHolding::parse(&user_a_acc.data) + .ok_or(ExecutionFailureKind::AccountDataError(user_holding_a))? + .definition_id; + let definition_token_b_id = TokenHolding::parse(&user_b_acc.data) + .ok_or(ExecutionFailureKind::AccountDataError(user_holding_a))? + .definition_id; - let amm_pool = - compute_pool_pda(amm_program_id, definition_token_a_id, definition_token_b_id); - let vault_holding_a = - compute_vault_pda(amm_program_id, amm_pool, definition_token_a_id); - let vault_holding_b = - compute_vault_pda(amm_program_id, amm_pool, definition_token_b_id); - let pool_lp = compute_liquidity_token_pda(amm_program_id, amm_pool); + let amm_pool = + compute_pool_pda(amm_program_id, definition_token_a_id, definition_token_b_id); + let vault_holding_a = compute_vault_pda(amm_program_id, amm_pool, definition_token_a_id); + let vault_holding_b = compute_vault_pda(amm_program_id, amm_pool, definition_token_b_id); + let pool_lp = compute_liquidity_token_pda(amm_program_id, amm_pool); - let account_ids = vec![ - amm_pool, - vault_holding_a, - vault_holding_b, - pool_lp, - user_holding_a, - user_holding_b, - user_holding_lp, - ]; + let account_ids = vec![ + amm_pool, + vault_holding_a, + vault_holding_b, + pool_lp, + user_holding_a, + user_holding_b, + user_holding_lp, + ]; - let Ok(nonces) = self - .0 - .get_accounts_nonces(vec![user_holding_a, user_holding_b]) - .await - else { - return Err(ExecutionFailureKind::SequencerError); - }; + let Ok(nonces) = self + .0 + .get_accounts_nonces(vec![user_holding_a, user_holding_b]) + .await + else { + return Err(ExecutionFailureKind::SequencerError); + }; - let Some(signing_key_a) = self - .0 - .storage - .user_data - .get_pub_account_signing_key(&user_holding_a) - else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; + let Some(signing_key_a) = self + .0 + .storage + .user_data + .get_pub_account_signing_key(&user_holding_a) + else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; - let Some(signing_key_b) = self - .0 - .storage - .user_data - .get_pub_account_signing_key(&user_holding_b) - else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; + let Some(signing_key_b) = self + .0 + .storage + .user_data + .get_pub_account_signing_key(&user_holding_b) + else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; - let message = nssa::public_transaction::Message::try_new( - program.id(), - account_ids, - nonces, - instruction, - ) - .unwrap(); + let message = nssa::public_transaction::Message::try_new( + program.id(), + account_ids, + nonces, + instruction, + ) + .unwrap(); - let witness_set = nssa::public_transaction::WitnessSet::for_message( - &message, - &[signing_key_a, signing_key_b], - ); + let witness_set = nssa::public_transaction::WitnessSet::for_message( + &message, + &[signing_key_a, signing_key_b], + ); - let tx = nssa::PublicTransaction::new(message, witness_set); + let tx = nssa::PublicTransaction::new(message, witness_set); - Ok(self.0.sequencer_client.send_tx_public(tx).await?) - } - _ => unreachable!(), - } + Ok(self.0.sequencer_client.send_tx_public(tx).await?) } - #[allow(clippy::too_many_arguments)] - pub async fn send_new_amm_definition_privacy_preserving( - &self, - _user_holding_a: PrivacyPreservingAccount, - _user_holding_b: PrivacyPreservingAccount, - _user_holding_lp: PrivacyPreservingAccount, - _balance_a: u128, - _balance_b: u128, - ) -> Result<(SendTxResponse, [Option; 3]), ExecutionFailureKind> { - todo!() - } - - #[allow(clippy::too_many_arguments)] pub async fn send_swap( &self, - user_holding_a: PrivacyPreservingAccount, - user_holding_b: PrivacyPreservingAccount, + user_holding_a: AccountId, + user_holding_b: AccountId, amount_in: u128, min_amount_out: u128, token_definition_id: AccountId, @@ -281,162 +110,96 @@ impl AMM<'_> { let (instruction, program) = amm_program_preparation_swap(amount_in, min_amount_out, token_definition_id); - match (user_holding_a, user_holding_b) { - ( - PrivacyPreservingAccount::Public(user_holding_a), - PrivacyPreservingAccount::Public(user_holding_b), - ) => { - let amm_program_id = Program::amm().id(); + let amm_program_id = Program::amm().id(); - let Ok(user_a_acc) = self.0.get_account_public(user_holding_a).await else { - return Err(ExecutionFailureKind::SequencerError); - }; - let Ok(user_b_acc) = self.0.get_account_public(user_holding_b).await else { - return Err(ExecutionFailureKind::SequencerError); - }; + let Ok(user_a_acc) = self.0.get_account_public(user_holding_a).await else { + return Err(ExecutionFailureKind::SequencerError); + }; + let Ok(user_b_acc) = self.0.get_account_public(user_holding_b).await else { + return Err(ExecutionFailureKind::SequencerError); + }; - let definition_token_a_id = TokenHolding::parse(&user_a_acc.data) - .ok_or(ExecutionFailureKind::AccountDataError(user_holding_a))? - .definition_id; - let definition_token_b_id = TokenHolding::parse(&user_b_acc.data) - .ok_or(ExecutionFailureKind::AccountDataError(user_holding_a))? - .definition_id; + let definition_token_a_id = TokenHolding::parse(&user_a_acc.data) + .ok_or(ExecutionFailureKind::AccountDataError(user_holding_a))? + .definition_id; + let definition_token_b_id = TokenHolding::parse(&user_b_acc.data) + .ok_or(ExecutionFailureKind::AccountDataError(user_holding_a))? + .definition_id; - let amm_pool = - compute_pool_pda(amm_program_id, definition_token_a_id, definition_token_b_id); - let vault_holding_a = - compute_vault_pda(amm_program_id, amm_pool, definition_token_a_id); - let vault_holding_b = - compute_vault_pda(amm_program_id, amm_pool, definition_token_b_id); + let amm_pool = + compute_pool_pda(amm_program_id, definition_token_a_id, definition_token_b_id); + let vault_holding_a = compute_vault_pda(amm_program_id, amm_pool, definition_token_a_id); + let vault_holding_b = compute_vault_pda(amm_program_id, amm_pool, definition_token_b_id); - let account_ids = vec![ - amm_pool, - vault_holding_a, - vault_holding_b, - user_holding_a, - user_holding_b, - ]; + let account_ids = vec![ + amm_pool, + vault_holding_a, + vault_holding_b, + user_holding_a, + user_holding_b, + ]; - let account_id_auth; + let account_id_auth; - // Checking, which account are associated with TokenDefinition - let token_holder_acc_a = self - .0 - .get_account_public(user_holding_a) - .await - .map_err(|_| ExecutionFailureKind::SequencerError)?; - let token_holder_acc_b = self - .0 - .get_account_public(user_holding_b) - .await - .map_err(|_| ExecutionFailureKind::SequencerError)?; + // Checking, which account are associated with TokenDefinition + let token_holder_acc_a = self + .0 + .get_account_public(user_holding_a) + .await + .map_err(|_| ExecutionFailureKind::SequencerError)?; + let token_holder_acc_b = self + .0 + .get_account_public(user_holding_b) + .await + .map_err(|_| ExecutionFailureKind::SequencerError)?; - let token_holder_a = TokenHolding::parse(&token_holder_acc_a.data) - .ok_or(ExecutionFailureKind::AccountDataError(user_holding_a))?; - let token_holder_b = TokenHolding::parse(&token_holder_acc_b.data) - .ok_or(ExecutionFailureKind::AccountDataError(user_holding_b))?; + let token_holder_a = TokenHolding::parse(&token_holder_acc_a.data) + .ok_or(ExecutionFailureKind::AccountDataError(user_holding_a))?; + let token_holder_b = TokenHolding::parse(&token_holder_acc_b.data) + .ok_or(ExecutionFailureKind::AccountDataError(user_holding_b))?; - if token_holder_a.definition_id == token_definition_id { - account_id_auth = user_holding_a; - } else if token_holder_b.definition_id == token_definition_id { - account_id_auth = user_holding_b; - } else { - return Err(ExecutionFailureKind::AccountDataError(token_definition_id)); - } - - let Ok(nonces) = self.0.get_accounts_nonces(vec![account_id_auth]).await else { - return Err(ExecutionFailureKind::SequencerError); - }; - - let Some(signing_key) = self - .0 - .storage - .user_data - .get_pub_account_signing_key(&account_id_auth) - else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - - let message = nssa::public_transaction::Message::try_new( - program.id(), - account_ids, - nonces, - instruction, - ) - .unwrap(); - - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]); - - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self.0.sequencer_client.send_tx_public(tx).await?) - } - _ => unreachable!(), + if token_holder_a.definition_id == token_definition_id { + account_id_auth = user_holding_a; + } else if token_holder_b.definition_id == token_definition_id { + account_id_auth = user_holding_b; + } else { + return Err(ExecutionFailureKind::AccountDataError(token_definition_id)); } + + let Ok(nonces) = self.0.get_accounts_nonces(vec![account_id_auth]).await else { + return Err(ExecutionFailureKind::SequencerError); + }; + + let Some(signing_key) = self + .0 + .storage + .user_data + .get_pub_account_signing_key(&account_id_auth) + else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + let message = nssa::public_transaction::Message::try_new( + program.id(), + account_ids, + nonces, + instruction, + ) + .unwrap(); + + let witness_set = + nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]); + + let tx = nssa::PublicTransaction::new(message, witness_set); + + Ok(self.0.sequencer_client.send_tx_public(tx).await?) } - #[allow(clippy::too_many_arguments)] - pub async fn send_swap_privacy_preserving( - &self, - _user_holding_a: PrivacyPreservingAccount, - _user_holding_b: PrivacyPreservingAccount, - _amount_in: u128, - _min_amount_out: u128, - _token_definition_id: AccountId, - ) -> Result<(SendTxResponse, [Option; 5]), ExecutionFailureKind> { - todo!() - // let (instruction_data, program) = - // amm_program_preparation_swap(amount_in, min_amount_out, token_definition_id); - - // self.0 - // .send_privacy_preserving_tx( - // vec![ - // amm_pool.clone(), - // vault_holding_1.clone(), - // vault_holding_2.clone(), - // user_holding_a.clone(), - // user_holding_b.clone(), - // ], - // &instruction_data.words(), - // &program, - // ) - // .await - // .map(|(resp, secrets)| { - // let mut secrets = secrets.into_iter(); - // let mut secrets_res = [None; 5]; - - // for acc_id in [ - // amm_pool, - // vault_holding_1, - // vault_holding_2, - // user_holding_a, - // user_holding_b, - // ] - // .iter() - // .enumerate() - // { - // if acc_id.1.is_private() { - // let secret = secrets.next().expect("expected next secret"); - - // secrets_res[acc_id.0] = Some(secret); - // } - // } - - // (resp, secrets_res) - // }) - } - - #[allow(clippy::too_many_arguments)] pub async fn send_add_liq( &self, - amm_pool: PrivacyPreservingAccount, - vault_holding_a: PrivacyPreservingAccount, - vault_holding_b: PrivacyPreservingAccount, - pool_lp: PrivacyPreservingAccount, - user_holding_a: PrivacyPreservingAccount, - user_holding_b: PrivacyPreservingAccount, - user_holding_lp: PrivacyPreservingAccount, + user_holding_a: AccountId, + user_holding_b: AccountId, + user_holding_lp: AccountId, min_amount_lp: u128, max_amount_a: u128, max_amount_b: u128, @@ -444,7 +207,29 @@ impl AMM<'_> { let (instruction, program) = amm_program_preparation_add_liq(min_amount_lp, max_amount_a, max_amount_b); - match ( + let amm_program_id = Program::amm().id(); + + let Ok(user_a_acc) = self.0.get_account_public(user_holding_a).await else { + return Err(ExecutionFailureKind::SequencerError); + }; + let Ok(user_b_acc) = self.0.get_account_public(user_holding_b).await else { + return Err(ExecutionFailureKind::SequencerError); + }; + + let definition_token_a_id = TokenHolding::parse(&user_a_acc.data) + .ok_or(ExecutionFailureKind::AccountDataError(user_holding_a))? + .definition_id; + let definition_token_b_id = TokenHolding::parse(&user_b_acc.data) + .ok_or(ExecutionFailureKind::AccountDataError(user_holding_a))? + .definition_id; + + let amm_pool = + compute_pool_pda(amm_program_id, definition_token_a_id, definition_token_b_id); + let vault_holding_a = compute_vault_pda(amm_program_id, amm_pool, definition_token_a_id); + let vault_holding_b = compute_vault_pda(amm_program_id, amm_pool, definition_token_b_id); + let pool_lp = compute_liquidity_token_pda(amm_program_id, amm_pool); + + let account_ids = vec![ amm_pool, vault_holding_a, vault_holding_b, @@ -452,142 +237,57 @@ impl AMM<'_> { user_holding_a, user_holding_b, user_holding_lp, - ) { - ( - PrivacyPreservingAccount::Public(amm_pool), - PrivacyPreservingAccount::Public(vault_holding_a), - PrivacyPreservingAccount::Public(vault_holding_b), - PrivacyPreservingAccount::Public(pool_lp), - PrivacyPreservingAccount::Public(user_holding_a), - PrivacyPreservingAccount::Public(user_holding_b), - PrivacyPreservingAccount::Public(user_holding_lp), - ) => { - let account_ids = vec![ - amm_pool, - vault_holding_a, - vault_holding_b, - pool_lp, - user_holding_a, - user_holding_b, - user_holding_lp, - ]; + ]; - let Ok(nonces) = self - .0 - .get_accounts_nonces(vec![user_holding_a, user_holding_b]) - .await - else { - return Err(ExecutionFailureKind::SequencerError); - }; - - let Some(signing_key_a) = self - .0 - .storage - .user_data - .get_pub_account_signing_key(&user_holding_a) - else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - - let Some(signing_key_b) = self - .0 - .storage - .user_data - .get_pub_account_signing_key(&user_holding_b) - else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - - let message = nssa::public_transaction::Message::try_new( - program.id(), - account_ids, - nonces, - instruction, - ) - .unwrap(); - - let witness_set = nssa::public_transaction::WitnessSet::for_message( - &message, - &[signing_key_a, signing_key_b], - ); - - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self.0.sequencer_client.send_tx_public(tx).await?) - } - _ => unreachable!(), - } - } - - #[allow(clippy::too_many_arguments)] - pub async fn send_add_liq_privacy_preserving( - &self, - amm_pool: PrivacyPreservingAccount, - vault_holding_a: PrivacyPreservingAccount, - vault_holding_b: PrivacyPreservingAccount, - pool_lp: PrivacyPreservingAccount, - user_holding_a: PrivacyPreservingAccount, - user_holding_b: PrivacyPreservingAccount, - user_holding_lp: PrivacyPreservingAccount, - min_amount_lp: u128, - max_amount_a: u128, - max_amount_b: u128, - ) -> Result<(SendTxResponse, [Option; 7]), ExecutionFailureKind> { - let (instruction_data, program) = - amm_program_preparation_add_liq(min_amount_lp, max_amount_a, max_amount_b); - - self.0 - .send_privacy_preserving_tx( - vec![ - amm_pool.clone(), - vault_holding_a.clone(), - vault_holding_b.clone(), - pool_lp.clone(), - user_holding_a.clone(), - user_holding_b.clone(), - user_holding_lp.clone(), - ], - &instruction_data.words(), - &program, - ) + let Ok(nonces) = self + .0 + .get_accounts_nonces(vec![user_holding_a, user_holding_b]) .await - .map(|(resp, secrets)| { - let mut secrets = secrets.into_iter(); - let mut secrets_res = [None; 7]; + else { + return Err(ExecutionFailureKind::SequencerError); + }; - for acc_id in [ - amm_pool, - vault_holding_a, - vault_holding_b, - pool_lp, - user_holding_a, - user_holding_b, - user_holding_lp, - ] - .iter() - .enumerate() - { - if acc_id.1.is_private() { - let secret = secrets.next().expect("expected next secret"); + let Some(signing_key_a) = self + .0 + .storage + .user_data + .get_pub_account_signing_key(&user_holding_a) + else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; - secrets_res[acc_id.0] = Some(secret); - } - } + let Some(signing_key_b) = self + .0 + .storage + .user_data + .get_pub_account_signing_key(&user_holding_b) + else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; - (resp, secrets_res) - }) + let message = nssa::public_transaction::Message::try_new( + program.id(), + account_ids, + nonces, + instruction, + ) + .unwrap(); + + let witness_set = nssa::public_transaction::WitnessSet::for_message( + &message, + &[signing_key_a, signing_key_b], + ); + + let tx = nssa::PublicTransaction::new(message, witness_set); + + Ok(self.0.sequencer_client.send_tx_public(tx).await?) } - #[allow(clippy::too_many_arguments)] pub async fn send_remove_liq( &self, - amm_pool: PrivacyPreservingAccount, - vault_holding_a: PrivacyPreservingAccount, - vault_holding_b: PrivacyPreservingAccount, - pool_lp: PrivacyPreservingAccount, - user_holding_a: PrivacyPreservingAccount, - user_holding_b: PrivacyPreservingAccount, - user_holding_lp: PrivacyPreservingAccount, + user_holding_a: AccountId, + user_holding_b: AccountId, + user_holding_lp: AccountId, balance_lp: u128, max_amount_a: u128, max_amount_b: u128, @@ -595,7 +295,29 @@ impl AMM<'_> { let (instruction, program) = amm_program_preparation_remove_liq(balance_lp, max_amount_a, max_amount_b); - match ( + let amm_program_id = Program::amm().id(); + + let Ok(user_a_acc) = self.0.get_account_public(user_holding_a).await else { + return Err(ExecutionFailureKind::SequencerError); + }; + let Ok(user_b_acc) = self.0.get_account_public(user_holding_b).await else { + return Err(ExecutionFailureKind::SequencerError); + }; + + let definition_token_a_id = TokenHolding::parse(&user_a_acc.data) + .ok_or(ExecutionFailureKind::AccountDataError(user_holding_a))? + .definition_id; + let definition_token_b_id = TokenHolding::parse(&user_b_acc.data) + .ok_or(ExecutionFailureKind::AccountDataError(user_holding_a))? + .definition_id; + + let amm_pool = + compute_pool_pda(amm_program_id, definition_token_a_id, definition_token_b_id); + let vault_holding_a = compute_vault_pda(amm_program_id, amm_pool, definition_token_a_id); + let vault_holding_b = compute_vault_pda(amm_program_id, amm_pool, definition_token_b_id); + let pool_lp = compute_liquidity_token_pda(amm_program_id, amm_pool); + + let account_ids = vec![ amm_pool, vault_holding_a, vault_holding_b, @@ -603,134 +325,38 @@ impl AMM<'_> { user_holding_a, user_holding_b, user_holding_lp, - ) { - ( - PrivacyPreservingAccount::Public(amm_pool), - PrivacyPreservingAccount::Public(vault_holding_a), - PrivacyPreservingAccount::Public(vault_holding_b), - PrivacyPreservingAccount::Public(pool_lp), - PrivacyPreservingAccount::Public(user_holding_a), - PrivacyPreservingAccount::Public(user_holding_b), - PrivacyPreservingAccount::Public(user_holding_lp), - ) => { - let account_ids = vec![ - amm_pool, - vault_holding_a, - vault_holding_b, - pool_lp, - user_holding_a, - user_holding_b, - user_holding_lp, - ]; + ]; - let Ok(nonces) = self - .0 - .get_accounts_nonces(vec![user_holding_a, user_holding_b]) - .await - else { - return Err(ExecutionFailureKind::SequencerError); - }; + let Ok(nonces) = self.0.get_accounts_nonces(vec![user_holding_lp]).await else { + return Err(ExecutionFailureKind::SequencerError); + }; - let Some(signing_key_a) = self - .0 - .storage - .user_data - .get_pub_account_signing_key(&user_holding_a) - else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; + let Some(signing_key_lp) = self + .0 + .storage + .user_data + .get_pub_account_signing_key(&user_holding_lp) + else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; - let Some(signing_key_b) = self - .0 - .storage - .user_data - .get_pub_account_signing_key(&user_holding_b) - else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; + let message = nssa::public_transaction::Message::try_new( + program.id(), + account_ids, + nonces, + instruction, + ) + .unwrap(); - let message = nssa::public_transaction::Message::try_new( - program.id(), - account_ids, - nonces, - instruction, - ) - .unwrap(); + let witness_set = + nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key_lp]); - let witness_set = nssa::public_transaction::WitnessSet::for_message( - &message, - &[signing_key_a, signing_key_b], - ); + let tx = nssa::PublicTransaction::new(message, witness_set); - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self.0.sequencer_client.send_tx_public(tx).await?) - } - _ => unreachable!(), - } - } - - #[allow(clippy::too_many_arguments)] - pub async fn send_remove_liq_privacy_preserving( - &self, - amm_pool: PrivacyPreservingAccount, - vault_holding_a: PrivacyPreservingAccount, - vault_holding_b: PrivacyPreservingAccount, - pool_lp: PrivacyPreservingAccount, - user_holding_a: PrivacyPreservingAccount, - user_holding_b: PrivacyPreservingAccount, - user_holding_lp: PrivacyPreservingAccount, - balance_lp: u128, - max_amount_a: u128, - max_amount_b: u128, - ) -> Result<(SendTxResponse, [Option; 7]), ExecutionFailureKind> { - let (instruction_data, program) = - amm_program_preparation_remove_liq(balance_lp, max_amount_a, max_amount_b); - - self.0 - .send_privacy_preserving_tx( - vec![ - amm_pool.clone(), - vault_holding_a.clone(), - vault_holding_b.clone(), - pool_lp.clone(), - user_holding_a.clone(), - user_holding_b.clone(), - user_holding_lp.clone(), - ], - &instruction_data.words(), - &program, - ) - .await - .map(|(resp, secrets)| { - let mut secrets = secrets.into_iter(); - let mut secrets_res = [None; 7]; - - for acc_id in [ - amm_pool, - vault_holding_a, - vault_holding_b, - pool_lp, - user_holding_a, - user_holding_b, - user_holding_lp, - ] - .iter() - .enumerate() - { - if acc_id.1.is_private() { - let secret = secrets.next().expect("expected next secret"); - - secrets_res[acc_id.0] = Some(secret); - } - } - - (resp, secrets_res) - }) + Ok(self.0.sequencer_client.send_tx_public(tx).await?) } } -#[allow(unused)] fn amm_program_preparation_definition( balance_a: u128, balance_b: u128, diff --git a/wallet/src/program_facades/mod.rs b/wallet/src/program_facades/mod.rs index 5fdcdb3..777a634 100644 --- a/wallet/src/program_facades/mod.rs +++ b/wallet/src/program_facades/mod.rs @@ -1,7 +1,142 @@ //! This module contains [`WalletCore`](crate::WalletCore) facades for interacting with various //! on-chain programs. +use nssa::{AccountId, ProgramId}; +use nssa_core::program::PdaSeed; +use serde::{Serialize, ser::SerializeSeq}; + pub mod amm; pub mod native_token_transfer; pub mod pinata; pub mod token; + +fn compute_pool_pda( + amm_program_id: ProgramId, + definition_token_a_id: AccountId, + definition_token_b_id: AccountId, +) -> AccountId { + AccountId::from(( + &amm_program_id, + &compute_pool_pda_seed(definition_token_a_id, definition_token_b_id), + )) +} + +fn compute_pool_pda_seed( + definition_token_a_id: AccountId, + definition_token_b_id: AccountId, +) -> PdaSeed { + use risc0_zkvm::sha::{Impl, Sha256}; + + let mut i: usize = 0; + let (token_1, token_2) = loop { + if definition_token_a_id.value()[i] > definition_token_b_id.value()[i] { + let token_1 = definition_token_a_id; + let token_2 = definition_token_b_id; + break (token_1, token_2); + } else if definition_token_a_id.value()[i] < definition_token_b_id.value()[i] { + let token_1 = definition_token_b_id; + let token_2 = definition_token_a_id; + break (token_1, token_2); + } + + if i == 32 { + panic!("Definitions match"); + } else { + i += 1; + } + }; + + let mut bytes = [0; 64]; + bytes[0..32].copy_from_slice(&token_1.to_bytes()); + bytes[32..].copy_from_slice(&token_2.to_bytes()); + + PdaSeed::new( + Impl::hash_bytes(&bytes) + .as_bytes() + .try_into() + .expect("Hash output must be exactly 32 bytes long"), + ) +} + +fn compute_vault_pda( + amm_program_id: ProgramId, + pool_id: AccountId, + definition_token_id: AccountId, +) -> AccountId { + AccountId::from(( + &amm_program_id, + &compute_vault_pda_seed(pool_id, definition_token_id), + )) +} + +fn compute_vault_pda_seed(pool_id: AccountId, definition_token_id: AccountId) -> PdaSeed { + use risc0_zkvm::sha::{Impl, Sha256}; + + let mut bytes = [0; 64]; + bytes[0..32].copy_from_slice(&pool_id.to_bytes()); + bytes[32..].copy_from_slice(&definition_token_id.to_bytes()); + + PdaSeed::new( + Impl::hash_bytes(&bytes) + .as_bytes() + .try_into() + .expect("Hash output must be exactly 32 bytes long"), + ) +} + +fn compute_liquidity_token_pda(amm_program_id: ProgramId, pool_id: AccountId) -> AccountId { + AccountId::from((&amm_program_id, &compute_liquidity_token_pda_seed(pool_id))) +} + +fn compute_liquidity_token_pda_seed(pool_id: AccountId) -> PdaSeed { + use risc0_zkvm::sha::{Impl, Sha256}; + + let mut bytes = [0; 64]; + bytes[0..32].copy_from_slice(&pool_id.to_bytes()); + bytes[32..].copy_from_slice(&[0; 32]); + + PdaSeed::new( + Impl::hash_bytes(&bytes) + .as_bytes() + .try_into() + .expect("Hash output must be exactly 32 bytes long"), + ) +} + +/// Why it is necessary: +/// +/// Serialize implemented only for `[u8; N]` where `N<=32` and orphan rules would disallow custom +/// Serialize impls for them. +/// +/// Additionally, RISC0 splits instructions into words of 4-byte size which glues bytes for custom +/// structs so we need to expand each byte into `u32` to preserve shape, because AMM awaits +/// `Vec` as instruction. +struct OrphanHackNBytesInput([u32; N]); + +impl OrphanHackNBytesInput { + fn expand(orig: [u8; N]) -> Self { + let mut res = [0u32; N]; + + for (idx, val) in orig.into_iter().enumerate() { + res[idx] = val as u32; + } + + Self(res) + } +} + +impl Serialize for OrphanHackNBytesInput { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut seq = serializer.serialize_seq(Some(N))?; + for word in self.0 { + seq.serialize_element(&word)?; + } + seq.end() + } +} + +type OrphanHack65BytesInput = OrphanHackNBytesInput<65>; +type OrphanHack49BytesInput = OrphanHackNBytesInput<49>;