Merge pull request #127 from vacp2p/Pravdyvy/various-updates

Wallet updates
This commit is contained in:
Sergio Chouhy 2025-10-21 09:10:06 -03:00 committed by GitHub
commit 04f7e67384
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 1523 additions and 765 deletions

View File

@ -12,8 +12,9 @@ use serde_json::Value;
use crate::rpc_primitives::requests::{
GetAccountRequest, GetAccountResponse, GetAccountsNoncesRequest, GetAccountsNoncesResponse,
GetProgramIdsRequest, GetProgramIdsResponse, GetProofForCommitmentRequest,
GetProofForCommitmentResponse, GetTransactionByHashRequest, GetTransactionByHashResponse,
GetLastBlockRequest, GetLastBlockResponse, GetProgramIdsRequest, GetProgramIdsResponse,
GetProofForCommitmentRequest, GetProofForCommitmentResponse, GetTransactionByHashRequest,
GetTransactionByHashResponse,
};
use crate::sequencer_client::json::AccountInitialData;
use crate::transaction::{EncodedTransaction, NSSATransaction};
@ -77,6 +78,19 @@ impl SequencerClient {
Ok(resp_deser)
}
///Get last known `blokc_id` from sequencer
pub async fn get_last_block(&self) -> Result<GetLastBlockResponse, SequencerClientError> {
let block_req = GetLastBlockRequest {};
let req = serde_json::to_value(block_req)?;
let resp = self.call_method_with_payload("get_last_block", req).await?;
let resp_deser = serde_json::from_value(resp)?;
Ok(resp_deser)
}
///Get account public balance for `address`. `address` must be a valid hex-string for 32 bytes.
pub async fn get_account_balance(
&self,

View File

@ -19,9 +19,20 @@ use tempfile::TempDir;
use tokio::task::JoinHandle;
use wallet::{
Command, SubcommandReturnValue, WalletCore,
cli::token_program::{
TokenProgramSubcommand, TokenProgramSubcommandDeshielded, TokenProgramSubcommandPrivate,
TokenProgramSubcommandPublic, TokenProgramSubcommandShielded,
cli::{
account::{AccountSubcommand, FetchSubcommand, RegisterSubcommand},
native_token_transfer_program::{
NativeTokenTransferProgramSubcommand, NativeTokenTransferProgramSubcommandPrivate,
NativeTokenTransferProgramSubcommandShielded,
},
pinata_program::{
PinataProgramSubcommand, PinataProgramSubcommandPrivate, PinataProgramSubcommandPublic,
},
token_program::{
TokenProgramSubcommand, TokenProgramSubcommandDeshielded,
TokenProgramSubcommandPrivate, TokenProgramSubcommandPublic,
TokenProgramSubcommandShielded,
},
},
config::PersistentAccountData,
helperfunctions::{fetch_config, fetch_persistent_accounts},
@ -101,13 +112,13 @@ pub async fn post_test(residual: (ServerHandle, JoinHandle<Result<()>>, TempDir)
pub async fn test_success() {
info!("test_success");
let command = Command::SendNativeTokenTransferPublic {
let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Public {
from: ACC_SENDER.to_string(),
to: ACC_RECEIVER.to_string(),
amount: 100,
};
});
let wallet_config = fetch_config().unwrap();
let wallet_config = fetch_config().await.unwrap();
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
@ -137,15 +148,15 @@ pub async fn test_success() {
pub async fn test_success_move_to_another_account() {
info!("test_success_move_to_another_account");
let command = Command::RegisterAccountPublic {};
let command = Command::Account(AccountSubcommand::Register(RegisterSubcommand::Public {}));
let wallet_config = fetch_config().unwrap();
let wallet_config = fetch_config().await.unwrap();
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
wallet::execute_subcommand(command).await.unwrap();
let persistent_accounts = fetch_persistent_accounts().unwrap();
let persistent_accounts = fetch_persistent_accounts().await.unwrap();
let mut new_persistent_account_addr = String::new();
@ -161,11 +172,11 @@ pub async fn test_success_move_to_another_account() {
panic!("Failed to produce new account, not present in persistent accounts");
}
let command = Command::SendNativeTokenTransferPublic {
let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Public {
from: ACC_SENDER.to_string(),
to: new_persistent_account_addr.clone(),
amount: 100,
};
});
wallet::execute_subcommand(command).await.unwrap();
@ -193,13 +204,13 @@ pub async fn test_success_move_to_another_account() {
pub async fn test_failure() {
info!("test_failure");
let command = Command::SendNativeTokenTransferPublic {
let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Public {
from: ACC_SENDER.to_string(),
to: ACC_RECEIVER.to_string(),
amount: 1000000,
};
});
let wallet_config = fetch_config().unwrap();
let wallet_config = fetch_config().await.unwrap();
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
@ -231,13 +242,13 @@ pub async fn test_failure() {
pub async fn test_success_two_transactions() {
info!("test_success_two_transactions");
let command = Command::SendNativeTokenTransferPublic {
let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Public {
from: ACC_SENDER.to_string(),
to: ACC_RECEIVER.to_string(),
amount: 100,
};
});
let wallet_config = fetch_config().unwrap();
let wallet_config = fetch_config().await.unwrap();
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
@ -264,11 +275,11 @@ pub async fn test_success_two_transactions() {
info!("First TX Success!");
let command = Command::SendNativeTokenTransferPublic {
let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Public {
from: ACC_SENDER.to_string(),
to: ACC_RECEIVER.to_string(),
amount: 100,
};
});
wallet::execute_subcommand(command).await.unwrap();
@ -296,7 +307,7 @@ pub async fn test_success_two_transactions() {
pub async fn test_get_account() {
info!("test_get_account");
let wallet_config = fetch_config().unwrap();
let wallet_config = fetch_config().await.unwrap();
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
let account = seq_client
@ -317,22 +328,28 @@ pub async fn test_get_account() {
/// This test creates a new token using the token program. After creating the token, the test executes a
/// token transfer to a new account.
pub async fn test_success_token_program() {
let wallet_config = fetch_config().unwrap();
let wallet_config = fetch_config().await.unwrap();
// Create new account for the token definition
wallet::execute_subcommand(Command::RegisterAccountPublic {})
.await
.unwrap();
wallet::execute_subcommand(Command::Account(AccountSubcommand::Register(
RegisterSubcommand::Public {},
)))
.await
.unwrap();
// Create new account for the token supply holder
wallet::execute_subcommand(Command::RegisterAccountPublic {})
.await
.unwrap();
wallet::execute_subcommand(Command::Account(AccountSubcommand::Register(
RegisterSubcommand::Public {},
)))
.await
.unwrap();
// Create new account for receiving a token transaction
wallet::execute_subcommand(Command::RegisterAccountPublic {})
.await
.unwrap();
wallet::execute_subcommand(Command::Account(AccountSubcommand::Register(
RegisterSubcommand::Public {},
)))
.await
.unwrap();
let persistent_accounts = fetch_persistent_accounts().unwrap();
let persistent_accounts = fetch_persistent_accounts().await.unwrap();
let mut new_persistent_accounts_addr = Vec::new();
@ -458,31 +475,35 @@ pub async fn test_success_token_program() {
/// This test creates a new private token using the token program. After creating the token, the test executes a
/// private token transfer to a new account. All accounts are owned except definition.
pub async fn test_success_token_program_private_owned() {
let wallet_config = fetch_config().unwrap();
let wallet_config = fetch_config().await.unwrap();
// Create new account for the token definition (public)
let SubcommandReturnValue::RegisterAccount {
addr: definition_addr,
} = wallet::execute_subcommand(Command::RegisterAccountPublic {})
.await
.unwrap()
} = wallet::execute_subcommand(Command::Account(AccountSubcommand::Register(
RegisterSubcommand::Public {},
)))
.await
.unwrap()
else {
panic!("invalid subcommand return value");
};
// Create new account for the token supply holder (private)
let SubcommandReturnValue::RegisterAccount { addr: supply_addr } =
wallet::execute_subcommand(Command::RegisterAccountPrivate {})
.await
.unwrap()
else {
let SubcommandReturnValue::RegisterAccount { addr: supply_addr } = wallet::execute_subcommand(
Command::Account(AccountSubcommand::Register(RegisterSubcommand::Private {})),
)
.await
.unwrap() else {
panic!("invalid subcommand return value");
};
// Create new account for receiving a token transaction
let SubcommandReturnValue::RegisterAccount {
addr: recipient_addr,
} = wallet::execute_subcommand(Command::RegisterAccountPrivate {})
.await
.unwrap()
} = wallet::execute_subcommand(Command::Account(AccountSubcommand::Register(
RegisterSubcommand::Private {},
)))
.await
.unwrap()
else {
panic!("invalid subcommand return value");
};
@ -523,8 +544,10 @@ pub async fn test_success_token_program_private_owned() {
]
);
let wallet_config = fetch_config().unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap();
let wallet_config = fetch_config().await.unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config)
.await
.unwrap();
let new_commitment1 = wallet_storage
.get_private_account_commitment(&supply_addr)
@ -546,8 +569,10 @@ pub async fn test_success_token_program_private_owned() {
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let wallet_config = fetch_config().unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap();
let wallet_config = fetch_config().await.unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config)
.await
.unwrap();
let new_commitment1 = wallet_storage
.get_private_account_commitment(&supply_addr)
@ -574,8 +599,10 @@ pub async fn test_success_token_program_private_owned() {
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let wallet_config = fetch_config().unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap();
let wallet_config = fetch_config().await.unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config)
.await
.unwrap();
let new_commitment1 = wallet_storage
.get_private_account_commitment(&supply_addr)
@ -591,31 +618,35 @@ pub async fn test_success_token_program_private_owned() {
/// This test creates a new private token using the token program. After creating the token, the test executes a
/// private token transfer to a new account.
pub async fn test_success_token_program_private_claiming_path() {
let wallet_config = fetch_config().unwrap();
let wallet_config = fetch_config().await.unwrap();
// Create new account for the token definition (public)
let SubcommandReturnValue::RegisterAccount {
addr: definition_addr,
} = wallet::execute_subcommand(Command::RegisterAccountPublic {})
.await
.unwrap()
} = wallet::execute_subcommand(Command::Account(AccountSubcommand::Register(
RegisterSubcommand::Public {},
)))
.await
.unwrap()
else {
panic!("invalid subcommand return value");
};
// Create new account for the token supply holder (private)
let SubcommandReturnValue::RegisterAccount { addr: supply_addr } =
wallet::execute_subcommand(Command::RegisterAccountPrivate {})
.await
.unwrap()
else {
let SubcommandReturnValue::RegisterAccount { addr: supply_addr } = wallet::execute_subcommand(
Command::Account(AccountSubcommand::Register(RegisterSubcommand::Private {})),
)
.await
.unwrap() else {
panic!("invalid subcommand return value");
};
// Create new account for receiving a token transaction
let SubcommandReturnValue::RegisterAccount {
addr: recipient_addr,
} = wallet::execute_subcommand(Command::RegisterAccountPrivate {})
.await
.unwrap()
} = wallet::execute_subcommand(Command::Account(AccountSubcommand::Register(
RegisterSubcommand::Private {},
)))
.await
.unwrap()
else {
panic!("invalid subcommand return value");
};
@ -656,8 +687,10 @@ pub async fn test_success_token_program_private_claiming_path() {
]
);
let wallet_config = fetch_config().unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap();
let wallet_config = fetch_config().await.unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config)
.await
.unwrap();
let new_commitment1 = wallet_storage
.get_private_account_commitment(&supply_addr)
@ -691,16 +724,18 @@ pub async fn test_success_token_program_private_claiming_path() {
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let command = Command::FetchPrivateAccount {
let command = Command::Account(AccountSubcommand::Fetch(FetchSubcommand::PrivateAccount {
tx_hash,
acc_addr: recipient_addr.to_string(),
output_id: 1,
};
}));
wallet::execute_subcommand(command).await.unwrap();
let wallet_config = fetch_config().unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap();
let wallet_config = fetch_config().await.unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config)
.await
.unwrap();
let new_commitment1 = wallet_storage
.get_private_account_commitment(&supply_addr)
@ -716,31 +751,35 @@ pub async fn test_success_token_program_private_claiming_path() {
/// This test creates a new public token using the token program. After creating the token, the test executes a
/// shielded token transfer to a new account. All accounts are owned except definition.
pub async fn test_success_token_program_shielded_owned() {
let wallet_config = fetch_config().unwrap();
let wallet_config = fetch_config().await.unwrap();
// Create new account for the token definition (public)
let SubcommandReturnValue::RegisterAccount {
addr: definition_addr,
} = wallet::execute_subcommand(Command::RegisterAccountPublic {})
.await
.unwrap()
} = wallet::execute_subcommand(Command::Account(AccountSubcommand::Register(
RegisterSubcommand::Public {},
)))
.await
.unwrap()
else {
panic!("invalid subcommand return value");
};
// Create new account for the token supply holder (private)
let SubcommandReturnValue::RegisterAccount { addr: supply_addr } =
wallet::execute_subcommand(Command::RegisterAccountPublic {})
.await
.unwrap()
else {
// Create new account for the token supply holder (public)
let SubcommandReturnValue::RegisterAccount { addr: supply_addr } = wallet::execute_subcommand(
Command::Account(AccountSubcommand::Register(RegisterSubcommand::Public {})),
)
.await
.unwrap() else {
panic!("invalid subcommand return value");
};
// Create new account for receiving a token transaction
let SubcommandReturnValue::RegisterAccount {
addr: recipient_addr,
} = wallet::execute_subcommand(Command::RegisterAccountPrivate {})
.await
.unwrap()
} = wallet::execute_subcommand(Command::Account(AccountSubcommand::Register(
RegisterSubcommand::Private {},
)))
.await
.unwrap()
else {
panic!("invalid subcommand return value");
};
@ -795,8 +834,10 @@ pub async fn test_success_token_program_shielded_owned() {
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let wallet_config = fetch_config().unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap();
let wallet_config = fetch_config().await.unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config)
.await
.unwrap();
let new_commitment2 = wallet_storage
.get_private_account_commitment(&recipient_addr)
@ -819,8 +860,10 @@ pub async fn test_success_token_program_shielded_owned() {
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let wallet_config = fetch_config().unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap();
let wallet_config = fetch_config().await.unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config)
.await
.unwrap();
let new_commitment2 = wallet_storage
.get_private_account_commitment(&recipient_addr)
@ -831,31 +874,35 @@ pub async fn test_success_token_program_shielded_owned() {
/// This test creates a new private token using the token program. After creating the token, the test executes a
/// deshielded token transfer to a new account. All accounts are owned except definition.
pub async fn test_success_token_program_deshielded_owned() {
let wallet_config = fetch_config().unwrap();
let wallet_config = fetch_config().await.unwrap();
// Create new account for the token definition (public)
let SubcommandReturnValue::RegisterAccount {
addr: definition_addr,
} = wallet::execute_subcommand(Command::RegisterAccountPublic {})
.await
.unwrap()
} = wallet::execute_subcommand(Command::Account(AccountSubcommand::Register(
RegisterSubcommand::Public {},
)))
.await
.unwrap()
else {
panic!("invalid subcommand return value");
};
// Create new account for the token supply holder (private)
let SubcommandReturnValue::RegisterAccount { addr: supply_addr } =
wallet::execute_subcommand(Command::RegisterAccountPrivate {})
.await
.unwrap()
else {
let SubcommandReturnValue::RegisterAccount { addr: supply_addr } = wallet::execute_subcommand(
Command::Account(AccountSubcommand::Register(RegisterSubcommand::Private {})),
)
.await
.unwrap() else {
panic!("invalid subcommand return value");
};
// Create new account for receiving a token transaction
let SubcommandReturnValue::RegisterAccount {
addr: recipient_addr,
} = wallet::execute_subcommand(Command::RegisterAccountPublic {})
.await
.unwrap()
} = wallet::execute_subcommand(Command::Account(AccountSubcommand::Register(
RegisterSubcommand::Public {},
)))
.await
.unwrap()
else {
panic!("invalid subcommand return value");
};
@ -896,8 +943,10 @@ pub async fn test_success_token_program_deshielded_owned() {
]
);
let wallet_config = fetch_config().unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap();
let wallet_config = fetch_config().await.unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config)
.await
.unwrap();
let new_commitment1 = wallet_storage
.get_private_account_commitment(&supply_addr)
@ -920,8 +969,10 @@ pub async fn test_success_token_program_deshielded_owned() {
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let wallet_config = fetch_config().unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap();
let wallet_config = fetch_config().await.unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config)
.await
.unwrap();
let new_commitment1 = wallet_storage
.get_private_account_commitment(&supply_addr)
@ -944,8 +995,10 @@ pub async fn test_success_token_program_deshielded_owned() {
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let wallet_config = fetch_config().unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap();
let wallet_config = fetch_config().await.unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config)
.await
.unwrap();
let new_commitment1 = wallet_storage
.get_private_account_commitment(&supply_addr)
@ -958,20 +1011,24 @@ pub async fn test_success_private_transfer_to_another_owned_account() {
let from: Address = ACC_SENDER_PRIVATE.parse().unwrap();
let to: Address = ACC_RECEIVER_PRIVATE.parse().unwrap();
let command = Command::SendNativeTokenTransferPrivateOwnedAccount {
from: from.to_string(),
to: to.to_string(),
amount: 100,
};
let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Private(
NativeTokenTransferProgramSubcommandPrivate::PrivateOwned {
from: from.to_string(),
to: to.to_string(),
amount: 100,
},
));
wallet::execute_subcommand(command).await.unwrap();
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let wallet_config = fetch_config().unwrap();
let wallet_config = fetch_config().await.unwrap();
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config)
.await
.unwrap();
let new_commitment1 = wallet_storage
.get_private_account_commitment(&from)
@ -991,12 +1048,14 @@ pub async fn test_success_private_transfer_to_another_foreign_account() {
let to_npk_string = hex::encode(to_npk.0);
let to_ipk = Secp256k1Point::from_scalar(to_npk.0);
let command = Command::SendNativeTokenTransferPrivateForeignAccount {
from: from.to_string(),
to_npk: to_npk_string,
to_ipk: hex::encode(to_ipk.0),
amount: 100,
};
let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Private(
NativeTokenTransferProgramSubcommandPrivate::PrivateForeign {
from: from.to_string(),
to_npk: to_npk_string,
to_ipk: hex::encode(to_ipk.0),
amount: 100,
},
));
let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } =
wallet::execute_subcommand(command).await.unwrap()
@ -1007,9 +1066,11 @@ pub async fn test_success_private_transfer_to_another_foreign_account() {
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let wallet_config = fetch_config().unwrap();
let wallet_config = fetch_config().await.unwrap();
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config)
.await
.unwrap();
let new_commitment1 = wallet_storage
.get_private_account_commitment(&from)
@ -1030,16 +1091,18 @@ pub async fn test_success_private_transfer_to_another_owned_account_claiming_pat
info!("test_success_private_transfer_to_another_owned_account_claiming_path");
let from: Address = ACC_SENDER_PRIVATE.parse().unwrap();
let command = Command::RegisterAccountPrivate {};
let command = Command::Account(AccountSubcommand::Register(RegisterSubcommand::Private {}));
let sub_ret = wallet::execute_subcommand(command).await.unwrap();
let SubcommandReturnValue::RegisterAccount { addr: to_addr } = sub_ret else {
panic!("FAILED TO REGISTER ACCOUNT");
};
let wallet_config = fetch_config().unwrap();
let wallet_config = fetch_config().await.unwrap();
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config.clone()).unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config.clone())
.await
.unwrap();
let (to_keys, _) = wallet_storage
.storage
@ -1049,12 +1112,14 @@ pub async fn test_success_private_transfer_to_another_owned_account_claiming_pat
.cloned()
.unwrap();
let command = Command::SendNativeTokenTransferPrivateForeignAccount {
from: from.to_string(),
to_npk: hex::encode(to_keys.nullifer_public_key.0),
to_ipk: hex::encode(to_keys.incoming_viewing_public_key.0),
amount: 100,
};
let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Private(
NativeTokenTransferProgramSubcommandPrivate::PrivateForeign {
from: from.to_string(),
to_npk: hex::encode(to_keys.nullifer_public_key.0),
to_ipk: hex::encode(to_keys.incoming_viewing_public_key.0),
amount: 100,
},
));
let sub_ret = wallet::execute_subcommand(command).await.unwrap();
let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = sub_ret else {
@ -1063,13 +1128,15 @@ pub async fn test_success_private_transfer_to_another_owned_account_claiming_pat
let tx = fetch_privacy_preserving_tx(&seq_client, tx_hash.clone()).await;
let command = Command::FetchPrivateAccount {
let command = Command::Account(AccountSubcommand::Fetch(FetchSubcommand::PrivateAccount {
tx_hash,
acc_addr: to_addr.to_string(),
output_id: 1,
};
}));
wallet::execute_subcommand(command).await.unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config)
.await
.unwrap();
let new_commitment1 = wallet_storage
.get_private_account_commitment(&from)
@ -1088,19 +1155,91 @@ pub async fn test_success_private_transfer_to_another_owned_account_claiming_pat
info!("Success!");
}
pub async fn test_success_private_transfer_to_another_owned_account_cont_run_path() {
info!("test_success_private_transfer_to_another_owned_account_cont_run_path");
let continious_run_handle = tokio::spawn(wallet::execute_continious_run());
let from: Address = ACC_SENDER_PRIVATE.parse().unwrap();
let command = Command::Account(AccountSubcommand::Register(RegisterSubcommand::Private {}));
let sub_ret = wallet::execute_subcommand(command).await.unwrap();
let SubcommandReturnValue::RegisterAccount { addr: to_addr } = sub_ret else {
panic!("FAILED TO REGISTER ACCOUNT");
};
let wallet_config = fetch_config().await.unwrap();
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config.clone())
.await
.unwrap();
let (to_keys, _) = wallet_storage
.storage
.user_data
.user_private_accounts
.get(&to_addr)
.cloned()
.unwrap();
let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Private(
NativeTokenTransferProgramSubcommandPrivate::PrivateForeign {
from: from.to_string(),
to_npk: hex::encode(to_keys.nullifer_public_key.0),
to_ipk: hex::encode(to_keys.incoming_viewing_public_key.0),
amount: 100,
},
));
let sub_ret = wallet::execute_subcommand(command).await.unwrap();
let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = sub_ret else {
panic!("FAILED TO SEND TX");
};
let tx = fetch_privacy_preserving_tx(&seq_client, tx_hash.clone()).await;
println!("Waiting for next blocks to check if continoius run fetch account");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config)
.await
.unwrap();
let new_commitment1 = wallet_storage
.get_private_account_commitment(&from)
.unwrap();
assert_eq!(tx.message.new_commitments[0], new_commitment1);
assert_eq!(tx.message.new_commitments.len(), 2);
for commitment in tx.message.new_commitments.into_iter() {
assert!(verify_commitment_is_in_state(commitment, &seq_client).await);
}
let to_res_acc = wallet_storage.get_account_private(&to_addr).unwrap();
assert_eq!(to_res_acc.balance, 100);
continious_run_handle.abort();
info!("Success!");
}
pub async fn test_success_deshielded_transfer_to_another_account() {
info!("test_success_deshielded_transfer_to_another_account");
let from: Address = ACC_SENDER_PRIVATE.parse().unwrap();
let to: Address = ACC_RECEIVER.parse().unwrap();
let command = Command::SendNativeTokenTransferDeshielded {
let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Deshielded {
from: from.to_string(),
to: to.to_string(),
amount: 100,
};
});
let wallet_config = fetch_config().unwrap();
let wallet_config = fetch_config().await.unwrap();
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config.clone()).unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config.clone())
.await
.unwrap();
let from_acc = wallet_storage.get_account_private(&from).unwrap();
assert_eq!(from_acc.balance, 10000);
@ -1110,7 +1249,9 @@ pub async fn test_success_deshielded_transfer_to_another_account() {
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config)
.await
.unwrap();
let from_acc = wallet_storage.get_account_private(&from).unwrap();
let new_commitment = wallet_storage
@ -1133,13 +1274,15 @@ pub async fn test_success_shielded_transfer_to_another_owned_account() {
info!("test_success_shielded_transfer_to_another_owned_account");
let from: Address = ACC_SENDER.parse().unwrap();
let to: Address = ACC_RECEIVER_PRIVATE.parse().unwrap();
let command = Command::SendNativeTokenTransferShielded {
from: from.to_string(),
to: to.to_string(),
amount: 100,
};
let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Shielded(
NativeTokenTransferProgramSubcommandShielded::ShieldedOwned {
from: from.to_string(),
to: to.to_string(),
amount: 100,
},
));
let wallet_config = fetch_config().unwrap();
let wallet_config = fetch_config().await.unwrap();
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
wallet::execute_subcommand(command).await.unwrap();
@ -1147,8 +1290,10 @@ pub async fn test_success_shielded_transfer_to_another_owned_account() {
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let wallet_config = fetch_config().unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap();
let wallet_config = fetch_config().await.unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config)
.await
.unwrap();
let acc_to = wallet_storage.get_account_private(&to).unwrap();
let new_commitment = wallet_storage.get_private_account_commitment(&to).unwrap();
@ -1172,14 +1317,16 @@ pub async fn test_success_shielded_transfer_to_another_foreign_account() {
let to_ipk = Secp256k1Point::from_scalar(to_npk.0);
let from: Address = ACC_SENDER.parse().unwrap();
let command = Command::SendNativeTokenTransferShieldedForeignAccount {
from: from.to_string(),
to_npk: to_npk_string,
to_ipk: hex::encode(to_ipk.0),
amount: 100,
};
let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Shielded(
NativeTokenTransferProgramSubcommandShielded::ShieldedForeign {
from: from.to_string(),
to_npk: to_npk_string,
to_ipk: hex::encode(to_ipk.0),
amount: 100,
},
));
let wallet_config = fetch_config().unwrap();
let wallet_config = fetch_config().await.unwrap();
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
@ -1213,13 +1360,15 @@ pub async fn test_pinata() {
let pinata_addr = "cafe".repeat(16);
let pinata_prize = 150;
let solution = 989106;
let command = Command::ClaimPinata {
pinata_addr: pinata_addr.clone(),
winner_addr: ACC_SENDER.to_string(),
solution,
};
let command = Command::PinataProgram(PinataProgramSubcommand::Public(
PinataProgramSubcommandPublic::Claim {
pinata_addr: pinata_addr.clone(),
winner_addr: ACC_SENDER.to_string(),
solution,
},
));
let wallet_config = fetch_config().unwrap();
let wallet_config = fetch_config().await.unwrap();
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
@ -1264,7 +1413,7 @@ pub async fn test_authenticated_transfer_initialize_function() {
};
info!("Checking correct execution");
let wallet_config = fetch_config().unwrap();
let wallet_config = fetch_config().await.unwrap();
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
let account = seq_client
.get_account(addr.to_string())
@ -1289,13 +1438,15 @@ pub async fn test_pinata_private_receiver() {
let pinata_prize = 150;
let solution = 989106;
let command = Command::ClaimPinataPrivateReceiverOwned {
pinata_addr: pinata_addr.clone(),
winner_addr: ACC_SENDER_PRIVATE.to_string(),
solution,
};
let command = Command::PinataProgram(PinataProgramSubcommand::Private(
PinataProgramSubcommandPrivate::ClaimPrivateOwned {
pinata_addr: pinata_addr.clone(),
winner_addr: ACC_SENDER_PRIVATE.to_string(),
solution,
},
));
let wallet_config = fetch_config().unwrap();
let wallet_config = fetch_config().await.unwrap();
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
@ -1321,16 +1472,18 @@ pub async fn test_pinata_private_receiver() {
.unwrap()
.balance;
let command = Command::FetchPrivateAccount {
let command = Command::Account(AccountSubcommand::Fetch(FetchSubcommand::PrivateAccount {
tx_hash: tx_hash.clone(),
acc_addr: ACC_SENDER_PRIVATE.to_string(),
output_id: 0,
};
}));
wallet::execute_subcommand(command).await.unwrap();
let wallet_config = fetch_config().unwrap();
let wallet_config = fetch_config().await.unwrap();
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config)
.await
.unwrap();
let new_commitment1 = wallet_storage
.get_private_account_commitment(&ACC_SENDER_PRIVATE.parse().unwrap())
@ -1349,21 +1502,23 @@ pub async fn test_pinata_private_receiver_new_account() {
let solution = 989106;
// Create new account for the token supply holder (private)
let SubcommandReturnValue::RegisterAccount { addr: winner_addr } =
wallet::execute_subcommand(Command::RegisterAccountPrivate {})
.await
.unwrap()
else {
let SubcommandReturnValue::RegisterAccount { addr: winner_addr } = wallet::execute_subcommand(
Command::Account(AccountSubcommand::Register(RegisterSubcommand::Private {})),
)
.await
.unwrap() else {
panic!("invalid subcommand return value");
};
let command = Command::ClaimPinataPrivateReceiverOwned {
pinata_addr: pinata_addr.clone(),
winner_addr: winner_addr.to_string(),
solution,
};
let command = Command::PinataProgram(PinataProgramSubcommand::Private(
PinataProgramSubcommandPrivate::ClaimPrivateOwned {
pinata_addr: pinata_addr.clone(),
winner_addr: winner_addr.to_string(),
solution,
},
));
let wallet_config = fetch_config().unwrap();
let wallet_config = fetch_config().await.unwrap();
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
@ -1385,9 +1540,11 @@ pub async fn test_pinata_private_receiver_new_account() {
.unwrap()
.balance;
let wallet_config = fetch_config().unwrap();
let wallet_config = fetch_config().await.unwrap();
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config)
.await
.unwrap();
let new_commitment1 = wallet_storage
.get_private_account_commitment(&winner_addr)
@ -1494,6 +1651,12 @@ pub async fn main_tests_runner() -> Result<()> {
"test_pinata_private_receiver_new_account" => {
test_cleanup_wrap!(home_dir, test_pinata_private_receiver_new_account);
}
"test_success_private_transfer_to_another_owned_account_cont_run_path" => {
test_cleanup_wrap!(
home_dir,
test_success_private_transfer_to_another_owned_account_cont_run_path
);
}
"test_success_token_program_shielded_owned" => {
test_cleanup_wrap!(home_dir, test_success_token_program_shielded_owned);
}
@ -1539,6 +1702,10 @@ pub async fn main_tests_runner() -> Result<()> {
test_cleanup_wrap!(home_dir, test_success_token_program_deshielded_owned);
test_cleanup_wrap!(home_dir, test_success_token_program_private_claiming_path);
test_cleanup_wrap!(home_dir, test_pinata_private_receiver_new_account);
test_cleanup_wrap!(
home_dir,
test_success_private_transfer_to_another_owned_account_cont_run_path
);
}
"all_private" => {
test_cleanup_wrap!(
@ -1569,6 +1736,10 @@ pub async fn main_tests_runner() -> Result<()> {
test_cleanup_wrap!(home_dir, test_success_token_program_private_owned);
test_cleanup_wrap!(home_dir, test_success_token_program_private_claiming_path);
test_cleanup_wrap!(home_dir, test_pinata_private_receiver_new_account);
test_cleanup_wrap!(
home_dir,
test_success_private_transfer_to_another_owned_account_cont_run_path
);
}
_ => {
anyhow::bail!("Unknown test name");

253
wallet/src/cli/account.rs Normal file
View File

@ -0,0 +1,253 @@
use std::str::FromStr;
use anyhow::Result;
use clap::Subcommand;
use common::transaction::NSSATransaction;
use nssa::Address;
use crate::{
SubcommandReturnValue, WalletCore, cli::WalletSubcommand, helperfunctions::HumanReadableAccount,
};
///Represents generic chain CLI subcommand
#[derive(Subcommand, Debug, Clone)]
pub enum AccountSubcommand {
///Get
#[command(subcommand)]
Get(GetSubcommand),
///Fetch
#[command(subcommand)]
Fetch(FetchSubcommand),
///Register
#[command(subcommand)]
Register(RegisterSubcommand),
}
///Represents generic getter CLI subcommand
#[derive(Subcommand, Debug, Clone)]
pub enum GetSubcommand {
///Get account `addr` balance
PublicAccountBalance {
#[arg(short, long)]
addr: String,
},
///Get account `addr` nonce
PublicAccountNonce {
#[arg(short, long)]
addr: String,
},
///Get account at address `addr`
PublicAccount {
#[arg(short, long)]
addr: String,
},
///Get private account with `addr` from storage
PrivateAccount {
#[arg(short, long)]
addr: String,
},
}
///Represents generic getter CLI subcommand
#[derive(Subcommand, Debug, Clone)]
pub enum FetchSubcommand {
///Fetch transaction by `hash`
Tx {
#[arg(short, long)]
tx_hash: String,
},
///Claim account `acc_addr` generated in transaction `tx_hash`, using secret `sh_secret` at ciphertext id `ciph_id`
PrivateAccount {
///tx_hash - valid 32 byte hex string
#[arg(long)]
tx_hash: String,
///acc_addr - valid 32 byte hex string
#[arg(long)]
acc_addr: String,
///output_id - id of the output in the transaction
#[arg(long)]
output_id: usize,
},
}
///Represents generic register CLI subcommand
#[derive(Subcommand, Debug, Clone)]
pub enum RegisterSubcommand {
///Register new public account
Public {},
///Register new private account
Private {},
}
impl WalletSubcommand for GetSubcommand {
async fn handle_subcommand(
self,
wallet_core: &mut WalletCore,
) -> Result<SubcommandReturnValue> {
match self {
GetSubcommand::PublicAccountBalance { addr } => {
let addr = Address::from_str(&addr)?;
let balance = wallet_core.get_account_balance(addr).await?;
println!("Accounts {addr} balance is {balance}");
Ok(SubcommandReturnValue::Empty)
}
GetSubcommand::PublicAccountNonce { addr } => {
let addr = Address::from_str(&addr)?;
let nonce = wallet_core.get_accounts_nonces(vec![addr]).await?[0];
println!("Accounts {addr} nonce is {nonce}");
Ok(SubcommandReturnValue::Empty)
}
GetSubcommand::PublicAccount { addr } => {
let addr: Address = addr.parse()?;
let account = wallet_core.get_account_public(addr).await?;
let account_hr: HumanReadableAccount = account.clone().into();
println!("{}", serde_json::to_string(&account_hr).unwrap());
Ok(SubcommandReturnValue::Account(account))
}
GetSubcommand::PrivateAccount { addr } => {
let addr: Address = addr.parse()?;
if let Some(account) = wallet_core.get_account_private(&addr) {
println!("{}", serde_json::to_string(&account).unwrap());
} else {
println!("Private account not found.");
}
Ok(SubcommandReturnValue::Empty)
}
}
}
}
impl WalletSubcommand for FetchSubcommand {
async fn handle_subcommand(
self,
wallet_core: &mut WalletCore,
) -> Result<SubcommandReturnValue> {
match self {
FetchSubcommand::Tx { tx_hash } => {
let tx_obj = wallet_core
.sequencer_client
.get_transaction_by_hash(tx_hash)
.await?;
println!("Transaction object {tx_obj:#?}");
Ok(SubcommandReturnValue::Empty)
}
FetchSubcommand::PrivateAccount {
tx_hash,
acc_addr,
output_id: ciph_id,
} => {
let acc_addr: Address = acc_addr.parse().unwrap();
let account_key_chain = wallet_core
.storage
.user_data
.user_private_accounts
.get(&acc_addr);
let Some((account_key_chain, _)) = account_key_chain else {
anyhow::bail!("Account not found");
};
let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?;
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
let to_ebc = tx.message.encrypted_private_post_states[ciph_id].clone();
let to_comm = tx.message.new_commitments[ciph_id].clone();
let shared_secret =
account_key_chain.calculate_shared_secret_receiver(to_ebc.epk);
let res_acc_to = nssa_core::EncryptionScheme::decrypt(
&to_ebc.ciphertext,
&shared_secret,
&to_comm,
ciph_id as u32,
)
.unwrap();
println!("RES acc to {res_acc_to:#?}");
println!("Transaction data is {:?}", tx.message);
wallet_core
.storage
.insert_private_account_data(acc_addr, res_acc_to);
}
let path = wallet_core.store_persistent_accounts().await?;
println!("Stored persistent accounts at {path:#?}");
Ok(SubcommandReturnValue::Empty)
}
}
}
}
impl WalletSubcommand for RegisterSubcommand {
async fn handle_subcommand(
self,
wallet_core: &mut WalletCore,
) -> Result<SubcommandReturnValue> {
match self {
RegisterSubcommand::Public {} => {
let addr = wallet_core.create_new_account_public();
println!("Generated new account with addr {addr}");
let path = wallet_core.store_persistent_accounts().await?;
println!("Stored persistent accounts at {path:#?}");
Ok(SubcommandReturnValue::RegisterAccount { addr })
}
RegisterSubcommand::Private {} => {
let addr = wallet_core.create_new_account_private();
let (key, _) = wallet_core
.storage
.user_data
.get_private_account(&addr)
.unwrap();
println!("Generated new account with addr {addr}");
println!("With npk {}", hex::encode(&key.nullifer_public_key));
println!(
"With ipk {}",
hex::encode(key.incoming_viewing_public_key.to_bytes())
);
let path = wallet_core.store_persistent_accounts().await?;
println!("Stored persistent accounts at {path:#?}");
Ok(SubcommandReturnValue::RegisterAccount { addr })
}
}
}
}
impl WalletSubcommand for AccountSubcommand {
async fn handle_subcommand(
self,
wallet_core: &mut WalletCore,
) -> Result<SubcommandReturnValue> {
match self {
AccountSubcommand::Get(get_subcommand) => {
get_subcommand.handle_subcommand(wallet_core).await
}
AccountSubcommand::Fetch(fetch_subcommand) => {
fetch_subcommand.handle_subcommand(wallet_core).await
}
AccountSubcommand::Register(register_subcommand) => {
register_subcommand.handle_subcommand(wallet_core).await
}
}
}
}

47
wallet/src/cli/chain.rs Normal file
View File

@ -0,0 +1,47 @@
use anyhow::Result;
use clap::Subcommand;
use crate::{SubcommandReturnValue, WalletCore, cli::WalletSubcommand};
///Represents generic chain CLI subcommand
#[derive(Subcommand, Debug, Clone)]
pub enum ChainSubcommand {
GetLatestBlockId {},
GetBlockAtId {
#[arg(short, long)]
id: u64,
},
GetTransactionAtHash {
#[arg(short, long)]
hash: String,
},
}
impl WalletSubcommand for ChainSubcommand {
async fn handle_subcommand(
self,
wallet_core: &mut WalletCore,
) -> Result<SubcommandReturnValue> {
match self {
ChainSubcommand::GetLatestBlockId {} => {
let latest_block_res = wallet_core.sequencer_client.get_last_block().await?;
println!("Last block id is {}", latest_block_res.last_block);
}
ChainSubcommand::GetBlockAtId { id } => {
let block_res = wallet_core.sequencer_client.get_block(id).await?;
println!("Last block id is {:#?}", block_res.block);
}
ChainSubcommand::GetTransactionAtHash { hash } => {
let tx_res = wallet_core
.sequencer_client
.get_transaction_by_hash(hash)
.await?;
println!("Last block id is {:#?}", tx_res.transaction);
}
}
Ok(SubcommandReturnValue::Empty)
}
}

View File

@ -2,6 +2,10 @@ use anyhow::Result;
use crate::{SubcommandReturnValue, WalletCore};
pub mod account;
pub mod chain;
pub mod native_token_transfer_program;
pub mod pinata_program;
pub mod token_program;
pub(crate) trait WalletSubcommand {

View File

@ -0,0 +1,362 @@
use anyhow::Result;
use clap::Subcommand;
use common::transaction::NSSATransaction;
use nssa::Address;
use crate::{SubcommandReturnValue, WalletCore, cli::WalletSubcommand};
///Represents generic CLI subcommand for a wallet working with native token transfer program
#[derive(Subcommand, Debug, Clone)]
pub enum NativeTokenTransferProgramSubcommand {
///Send native token transfer from `from` to `to` for `amount`
///
/// Public operation
Public {
///from - valid 32 byte hex string
#[arg(long)]
from: String,
///to - valid 32 byte hex string
#[arg(long)]
to: String,
///amount - amount of balance to move
#[arg(long)]
amount: u128,
},
///Private execution
#[command(subcommand)]
Private(NativeTokenTransferProgramSubcommandPrivate),
///Send native token transfer from `from` to `to` for `amount`
///
/// Deshielded operation
Deshielded {
///from - valid 32 byte hex string
#[arg(long)]
from: String,
///to - valid 32 byte hex string
#[arg(long)]
to: String,
///amount - amount of balance to move
#[arg(long)]
amount: u128,
},
///Shielded execution
#[command(subcommand)]
Shielded(NativeTokenTransferProgramSubcommandShielded),
}
///Represents generic shielded CLI subcommand for a wallet working with native token transfer program
#[derive(Subcommand, Debug, Clone)]
pub enum NativeTokenTransferProgramSubcommandShielded {
///Send native token transfer from `from` to `to` for `amount`
///
/// Shielded operation
ShieldedOwned {
///from - valid 32 byte hex string
#[arg(long)]
from: String,
///to - valid 32 byte hex string
#[arg(long)]
to: String,
///amount - amount of balance to move
#[arg(long)]
amount: u128,
},
///Send native token transfer from `from` to `to` for `amount`
///
/// Shielded operation
ShieldedForeign {
///from - valid 32 byte hex string
#[arg(long)]
from: String,
///to_npk - valid 32 byte hex string
#[arg(long)]
to_npk: String,
///to_ipk - valid 33 byte hex string
#[arg(long)]
to_ipk: String,
///amount - amount of balance to move
#[arg(long)]
amount: u128,
},
}
///Represents generic private CLI subcommand for a wallet working with native token transfer program
#[derive(Subcommand, Debug, Clone)]
pub enum NativeTokenTransferProgramSubcommandPrivate {
///Send native token transfer from `from` to `to` for `amount`
///
/// Private operation
PrivateOwned {
///from - valid 32 byte hex string
#[arg(long)]
from: String,
///to - valid 32 byte hex string
#[arg(long)]
to: String,
///amount - amount of balance to move
#[arg(long)]
amount: u128,
},
///Send native token transfer from `from` to `to` for `amount`
///
/// Private operation
PrivateForeign {
///from - valid 32 byte hex string
#[arg(long)]
from: String,
///to_npk - valid 32 byte hex string
#[arg(long)]
to_npk: String,
///to_ipk - valid 33 byte hex string
#[arg(long)]
to_ipk: String,
///amount - amount of balance to move
#[arg(long)]
amount: u128,
},
}
impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate {
async fn handle_subcommand(
self,
wallet_core: &mut WalletCore,
) -> Result<SubcommandReturnValue> {
match self {
NativeTokenTransferProgramSubcommandPrivate::PrivateOwned { from, to, amount } => {
let from: Address = from.parse().unwrap();
let to: Address = to.parse().unwrap();
let to_initialization = wallet_core.check_private_account_initialized(&to).await?;
let (res, [secret_from, secret_to]) = if let Some(to_proof) = to_initialization {
wallet_core
.send_private_native_token_transfer_owned_account_already_initialized(
from, to, amount, to_proof,
)
.await?
} else {
wallet_core
.send_private_native_token_transfer_owned_account_not_initialized(
from, to, amount,
)
.await?
};
println!("Results of tx send is {res:#?}");
let tx_hash = res.tx_hash;
let transfer_tx = wallet_core
.poll_native_token_transfer(tx_hash.clone())
.await?;
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
let acc_decode_data = vec![(secret_from, from), (secret_to, to)];
wallet_core.decode_insert_privacy_preserving_transaction_results(
tx,
&acc_decode_data,
)?;
}
let path = wallet_core.store_persistent_accounts().await?;
println!("Stored persistent accounts at {path:#?}");
Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash })
}
NativeTokenTransferProgramSubcommandPrivate::PrivateForeign {
from,
to_npk,
to_ipk,
amount,
} => {
let from: Address = from.parse().unwrap();
let to_npk_res = hex::decode(to_npk)?;
let mut to_npk = [0; 32];
to_npk.copy_from_slice(&to_npk_res);
let to_npk = nssa_core::NullifierPublicKey(to_npk);
let to_ipk_res = hex::decode(to_ipk)?;
let mut to_ipk = [0u8; 33];
to_ipk.copy_from_slice(&to_ipk_res);
let to_ipk =
nssa_core::encryption::shared_key_derivation::Secp256k1Point(to_ipk.to_vec());
let (res, [secret_from, _]) = wallet_core
.send_private_native_token_transfer_outer_account(from, to_npk, to_ipk, amount)
.await?;
println!("Results of tx send is {res:#?}");
let tx_hash = res.tx_hash;
let transfer_tx = wallet_core
.poll_native_token_transfer(tx_hash.clone())
.await?;
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
let acc_decode_data = vec![(secret_from, from)];
wallet_core.decode_insert_privacy_preserving_transaction_results(
tx,
&acc_decode_data,
)?;
}
let path = wallet_core.store_persistent_accounts().await?;
println!("Stored persistent accounts at {path:#?}");
Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash })
}
}
}
}
impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded {
async fn handle_subcommand(
self,
wallet_core: &mut WalletCore,
) -> Result<SubcommandReturnValue> {
match self {
NativeTokenTransferProgramSubcommandShielded::ShieldedOwned { from, to, amount } => {
let from: Address = from.parse().unwrap();
let to: Address = to.parse().unwrap();
let to_initialization = wallet_core.check_private_account_initialized(&to).await?;
let (res, secret) = if let Some(to_proof) = to_initialization {
wallet_core
.send_shielded_native_token_transfer_already_initialized(
from, to, amount, to_proof,
)
.await?
} else {
wallet_core
.send_shielded_native_token_transfer_not_initialized(from, to, amount)
.await?
};
println!("Results of tx send is {res:#?}");
let tx_hash = res.tx_hash;
let transfer_tx = wallet_core
.poll_native_token_transfer(tx_hash.clone())
.await?;
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
let acc_decode_data = vec![(secret, to)];
wallet_core.decode_insert_privacy_preserving_transaction_results(
tx,
&acc_decode_data,
)?;
}
let path = wallet_core.store_persistent_accounts().await?;
println!("Stored persistent accounts at {path:#?}");
Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash })
}
NativeTokenTransferProgramSubcommandShielded::ShieldedForeign {
from,
to_npk,
to_ipk,
amount,
} => {
let from: Address = from.parse().unwrap();
let to_npk_res = hex::decode(to_npk)?;
let mut to_npk = [0; 32];
to_npk.copy_from_slice(&to_npk_res);
let to_npk = nssa_core::NullifierPublicKey(to_npk);
let to_ipk_res = hex::decode(to_ipk)?;
let mut to_ipk = [0u8; 33];
to_ipk.copy_from_slice(&to_ipk_res);
let to_ipk =
nssa_core::encryption::shared_key_derivation::Secp256k1Point(to_ipk.to_vec());
let (res, _) = wallet_core
.send_shielded_native_token_transfer_outer_account(from, to_npk, to_ipk, amount)
.await?;
println!("Results of tx send is {res:#?}");
let tx_hash = res.tx_hash;
let path = wallet_core.store_persistent_accounts().await?;
println!("Stored persistent accounts at {path:#?}");
Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash })
}
}
}
}
impl WalletSubcommand for NativeTokenTransferProgramSubcommand {
async fn handle_subcommand(
self,
wallet_core: &mut WalletCore,
) -> Result<SubcommandReturnValue> {
match self {
NativeTokenTransferProgramSubcommand::Private(private_subcommand) => {
private_subcommand.handle_subcommand(wallet_core).await
}
NativeTokenTransferProgramSubcommand::Shielded(shielded_subcommand) => {
shielded_subcommand.handle_subcommand(wallet_core).await
}
NativeTokenTransferProgramSubcommand::Deshielded { from, to, amount } => {
let from: Address = from.parse().unwrap();
let to: Address = to.parse().unwrap();
let (res, secret) = wallet_core
.send_deshielded_native_token_transfer(from, to, amount)
.await?;
println!("Results of tx send is {res:#?}");
let tx_hash = res.tx_hash;
let transfer_tx = wallet_core
.poll_native_token_transfer(tx_hash.clone())
.await?;
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
let acc_decode_data = vec![(secret, from)];
wallet_core.decode_insert_privacy_preserving_transaction_results(
tx,
&acc_decode_data,
)?;
}
let path = wallet_core.store_persistent_accounts().await?;
println!("Stored persistent accounts at {path:#?}");
Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash })
}
NativeTokenTransferProgramSubcommand::Public { from, to, amount } => {
let from: Address = from.parse().unwrap();
let to: Address = to.parse().unwrap();
let res = wallet_core
.send_public_native_token_transfer(from, to, amount)
.await?;
println!("Results of tx send is {res:#?}");
let transfer_tx = wallet_core.poll_native_token_transfer(res.tx_hash).await?;
println!("Transaction data is {transfer_tx:?}");
let path = wallet_core.store_persistent_accounts().await?;
println!("Stored persistent accounts at {path:#?}");
Ok(SubcommandReturnValue::Empty)
}
}
}
}

View File

@ -0,0 +1,158 @@
use anyhow::Result;
use clap::Subcommand;
use common::transaction::NSSATransaction;
use log::info;
use crate::{SubcommandReturnValue, WalletCore, cli::WalletSubcommand};
///Represents generic CLI subcommand for a wallet working with pinata program
#[derive(Subcommand, Debug, Clone)]
pub enum PinataProgramSubcommand {
///Public execution
#[command(subcommand)]
Public(PinataProgramSubcommandPublic),
///Private execution
#[command(subcommand)]
Private(PinataProgramSubcommandPrivate),
}
///Represents generic public CLI subcommand for a wallet working with pinata program
#[derive(Subcommand, Debug, Clone)]
pub enum PinataProgramSubcommandPublic {
// TODO: Testnet only. Refactor to prevent compilation on mainnet.
// Claim piñata prize
Claim {
///pinata_addr - valid 32 byte hex string
#[arg(long)]
pinata_addr: String,
///winner_addr - valid 32 byte hex string
#[arg(long)]
winner_addr: String,
///solution - solution to pinata challenge
#[arg(long)]
solution: u128,
},
}
///Represents generic private CLI subcommand for a wallet working with pinata program
#[derive(Subcommand, Debug, Clone)]
pub enum PinataProgramSubcommandPrivate {
// TODO: Testnet only. Refactor to prevent compilation on mainnet.
// Claim piñata prize
ClaimPrivateOwned {
///pinata_addr - valid 32 byte hex string
#[arg(long)]
pinata_addr: String,
///winner_addr - valid 32 byte hex string
#[arg(long)]
winner_addr: String,
///solution - solution to pinata challenge
#[arg(long)]
solution: u128,
},
}
impl WalletSubcommand for PinataProgramSubcommandPublic {
async fn handle_subcommand(
self,
wallet_core: &mut WalletCore,
) -> Result<SubcommandReturnValue> {
match self {
PinataProgramSubcommandPublic::Claim {
pinata_addr,
winner_addr,
solution,
} => {
let res = wallet_core
.claim_pinata(
pinata_addr.parse().unwrap(),
winner_addr.parse().unwrap(),
solution,
)
.await?;
info!("Results of tx send is {res:#?}");
Ok(SubcommandReturnValue::Empty)
}
}
}
}
impl WalletSubcommand for PinataProgramSubcommandPrivate {
async fn handle_subcommand(
self,
wallet_core: &mut WalletCore,
) -> Result<SubcommandReturnValue> {
match self {
PinataProgramSubcommandPrivate::ClaimPrivateOwned {
pinata_addr,
winner_addr,
solution,
} => {
let pinata_addr = pinata_addr.parse().unwrap();
let winner_addr = winner_addr.parse().unwrap();
let winner_initialization = wallet_core
.check_private_account_initialized(&winner_addr)
.await?;
let (res, [secret_winner]) = if let Some(winner_proof) = winner_initialization {
wallet_core
.claim_pinata_private_owned_account_already_initialized(
pinata_addr,
winner_addr,
solution,
winner_proof,
)
.await?
} else {
wallet_core
.claim_pinata_private_owned_account_not_initialized(
pinata_addr,
winner_addr,
solution,
)
.await?
};
info!("Results of tx send is {res:#?}");
let tx_hash = res.tx_hash;
let transfer_tx = wallet_core
.poll_native_token_transfer(tx_hash.clone())
.await?;
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
let acc_decode_data = vec![(secret_winner, winner_addr)];
wallet_core.decode_insert_privacy_preserving_transaction_results(
tx,
&acc_decode_data,
)?;
}
let path = wallet_core.store_persistent_accounts().await?;
println!("Stored persistent accounts at {path:#?}");
Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash })
}
}
}
}
impl WalletSubcommand for PinataProgramSubcommand {
async fn handle_subcommand(
self,
wallet_core: &mut WalletCore,
) -> Result<SubcommandReturnValue> {
match self {
PinataProgramSubcommand::Private(private_subcommand) => {
private_subcommand.handle_subcommand(wallet_core).await
}
PinataProgramSubcommand::Public(public_subcommand) => {
public_subcommand.handle_subcommand(wallet_core).await
}
}
}
}

View File

@ -221,7 +221,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
)?;
}
let path = wallet_core.store_persistent_accounts()?;
let path = wallet_core.store_persistent_accounts().await?;
println!("Stored persistent accounts at {path:#?}");
@ -278,7 +278,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
)?;
}
let path = wallet_core.store_persistent_accounts()?;
let path = wallet_core.store_persistent_accounts().await?;
println!("Stored persistent accounts at {path:#?}");
@ -328,7 +328,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
)?;
}
let path = wallet_core.store_persistent_accounts()?;
let path = wallet_core.store_persistent_accounts().await?;
println!("Stored persistent accounts at {path:#?}");
@ -376,7 +376,7 @@ impl WalletSubcommand for TokenProgramSubcommandDeshielded {
)?;
}
let path = wallet_core.store_persistent_accounts()?;
let path = wallet_core.store_persistent_accounts().await?;
println!("Stored persistent accounts at {path:#?}");
@ -431,7 +431,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded {
println!("Transaction data is {:?}", tx.message);
}
let path = wallet_core.store_persistent_accounts()?;
let path = wallet_core.store_persistent_accounts().await?;
println!("Stored persistent accounts at {path:#?}");
@ -485,7 +485,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded {
)?;
}
let path = wallet_core.store_persistent_accounts()?;
let path = wallet_core.store_persistent_accounts().await?;
println!("Stored persistent accounts at {path:#?}");

View File

@ -1,7 +1,8 @@
use base64::{Engine, engine::general_purpose::STANDARD as BASE64};
use nssa_core::account::Nonce;
use rand::{RngCore, rngs::OsRng};
use std::{fs::File, io::BufReader, path::PathBuf, str::FromStr};
use std::{path::PathBuf, str::FromStr};
use tokio::io::AsyncReadExt;
use anyhow::Result;
use key_protocol::key_protocol_core::NSSAUserData;
@ -22,25 +23,25 @@ pub fn get_home() -> Result<PathBuf> {
}
/// Fetch config from `NSSA_WALLET_HOME_DIR`
pub fn fetch_config() -> Result<WalletConfig> {
pub async fn fetch_config() -> Result<WalletConfig> {
let config_home = get_home()?;
let file = File::open(config_home.join("wallet_config.json"))?;
let reader = BufReader::new(file);
let config_contents = tokio::fs::read(config_home.join("wallet_config.json")).await?;
Ok(serde_json::from_reader(reader)?)
Ok(serde_json::from_slice(&config_contents)?)
}
/// Fetch list of accounts stored at `NSSA_WALLET_HOME_DIR/curr_accounts.json`
///
/// If file not present, it is considered as empty list of persistent accounts
pub fn fetch_persistent_accounts() -> Result<Vec<PersistentAccountData>> {
pub async fn fetch_persistent_accounts() -> Result<Vec<PersistentAccountData>> {
let home = get_home()?;
let accs_path = home.join("curr_accounts.json");
let mut persistent_accounts_content = vec![];
match File::open(accs_path) {
Ok(file) => {
let reader = BufReader::new(file);
Ok(serde_json::from_reader(reader)?)
match tokio::fs::File::open(accs_path).await {
Ok(mut file) => {
file.read_to_end(&mut persistent_accounts_content).await?;
Ok(serde_json::from_slice(&persistent_accounts_content)?)
}
Err(err) => match err.kind() {
std::io::ErrorKind::NotFound => Ok(vec![]),

View File

@ -1,7 +1,8 @@
use std::{fs::File, io::Write, path::PathBuf, str::FromStr, sync::Arc};
use std::{path::PathBuf, sync::Arc};
use base64::{Engine, engine::general_purpose::STANDARD as BASE64};
use common::{
block::HashableBlockData,
sequencer_client::SequencerClient,
transaction::{EncodedTransaction, NSSATransaction},
};
@ -10,17 +11,24 @@ use anyhow::Result;
use chain_storage::WalletChainStore;
use config::WalletConfig;
use log::info;
use nssa::{Account, Address, program::Program};
use nssa::{
Account, Address, privacy_preserving_transaction::message::EncryptedAccountData,
program::Program,
};
use clap::{Parser, Subcommand};
use nssa_core::{Commitment, MembershipProof};
use tokio::io::AsyncWriteExt;
use crate::cli::WalletSubcommand;
use crate::cli::{
WalletSubcommand, account::AccountSubcommand, chain::ChainSubcommand,
native_token_transfer_program::NativeTokenTransferProgramSubcommand,
pinata_program::PinataProgramSubcommand,
};
use crate::{
cli::token_program::TokenProgramSubcommand,
helperfunctions::{
HumanReadableAccount, fetch_config, fetch_persistent_accounts, get_home,
produce_data_for_storage,
fetch_config, fetch_persistent_accounts, get_home, produce_data_for_storage,
},
poller::TxPoller,
};
@ -43,13 +51,13 @@ pub struct WalletCore {
}
impl WalletCore {
pub fn start_from_config_update_chain(config: WalletConfig) -> Result<Self> {
pub async fn start_from_config_update_chain(config: WalletConfig) -> Result<Self> {
let client = Arc::new(SequencerClient::new(config.sequencer_addr.clone())?);
let tx_poller = TxPoller::new(config.clone(), client.clone());
let mut storage = WalletChainStore::new(config)?;
let persistent_accounts = fetch_persistent_accounts()?;
let persistent_accounts = fetch_persistent_accounts().await?;
for pers_acc_data in persistent_accounts {
storage.insert_account_data(pers_acc_data);
}
@ -62,15 +70,15 @@ impl WalletCore {
}
///Store persistent accounts at home
pub fn store_persistent_accounts(&self) -> Result<PathBuf> {
pub async fn store_persistent_accounts(&self) -> Result<PathBuf> {
let home = get_home()?;
let accs_path = home.join("curr_accounts.json");
let data = produce_data_for_storage(&self.storage.user_data);
let accs = serde_json::to_vec_pretty(&data)?;
let mut accs_file = File::create(accs_path.as_path())?;
accs_file.write_all(&accs)?;
let mut accs_file = tokio::fs::File::create(accs_path.as_path()).await?;
accs_file.write_all(&accs).await?;
info!("Stored accounts data at {accs_path:#?}");
@ -182,179 +190,37 @@ impl WalletCore {
#[derive(Subcommand, Debug, Clone)]
#[clap(about)]
pub enum Command {
///Send native token transfer from `from` to `to` for `amount`
///
/// Public operation
SendNativeTokenTransferPublic {
///from - valid 32 byte hex string
#[arg(long)]
from: String,
///to - valid 32 byte hex string
#[arg(long)]
to: String,
///amount - amount of balance to move
#[arg(long)]
amount: u128,
},
///Send native token transfer from `from` to `to` for `amount`
///
/// Private operation
SendNativeTokenTransferPrivateOwnedAccount {
///from - valid 32 byte hex string
#[arg(long)]
from: String,
///to - valid 32 byte hex string
#[arg(long)]
to: String,
///amount - amount of balance to move
#[arg(long)]
amount: u128,
},
///Send native token transfer from `from` to `to` for `amount`
///
/// Private operation
SendNativeTokenTransferPrivateForeignAccount {
///from - valid 32 byte hex string
#[arg(long)]
from: String,
///to_npk - valid 32 byte hex string
#[arg(long)]
to_npk: String,
///to_ipk - valid 33 byte hex string
#[arg(long)]
to_ipk: String,
///amount - amount of balance to move
#[arg(long)]
amount: u128,
},
///Send native token transfer from `from` to `to` for `amount`
///
/// Deshielded operation
SendNativeTokenTransferDeshielded {
///from - valid 32 byte hex string
#[arg(long)]
from: String,
///to - valid 32 byte hex string
#[arg(long)]
to: String,
///amount - amount of balance to move
#[arg(long)]
amount: u128,
},
///Send native token transfer from `from` to `to` for `amount`
///
/// Shielded operation
SendNativeTokenTransferShielded {
///from - valid 32 byte hex string
#[arg(long)]
from: String,
///to - valid 32 byte hex string
#[arg(long)]
to: String,
///amount - amount of balance to move
#[arg(long)]
amount: u128,
},
///Send native token transfer from `from` to `to` for `amount`
///
/// Shielded operation
SendNativeTokenTransferShieldedForeignAccount {
///from - valid 32 byte hex string
#[arg(long)]
from: String,
///to_npk - valid 32 byte hex string
#[arg(long)]
to_npk: String,
///to_ipk - valid 33 byte hex string
#[arg(long)]
to_ipk: String,
///amount - amount of balance to move
#[arg(long)]
amount: u128,
},
///Claim account `acc_addr` generated in transaction `tx_hash`, using secret `sh_secret` at ciphertext id `ciph_id`
FetchPrivateAccount {
///tx_hash - valid 32 byte hex string
#[arg(long)]
tx_hash: String,
///acc_addr - valid 32 byte hex string
#[arg(long)]
acc_addr: String,
///output_id - id of the output in the transaction
#[arg(long)]
output_id: usize,
},
///Get private account with `addr` from storage
GetPrivateAccount {
#[arg(short, long)]
addr: String,
},
///Register new public account
RegisterAccountPublic {},
///Register new private account
RegisterAccountPrivate {},
///Fetch transaction by `hash`
FetchTx {
#[arg(short, long)]
tx_hash: String,
},
///Get account `addr` balance
GetPublicAccountBalance {
#[arg(short, long)]
addr: String,
},
///Get account `addr` nonce
GetPublicAccountNonce {
#[arg(short, long)]
addr: String,
},
///Get account at address `addr`
GetPublicAccount {
#[arg(short, long)]
addr: String,
},
// TODO: Testnet only. Refactor to prevent compilation on mainnet.
// Claim piñata prize
ClaimPinata {
///pinata_addr - valid 32 byte hex string
#[arg(long)]
pinata_addr: String,
///winner_addr - valid 32 byte hex string
#[arg(long)]
winner_addr: String,
///solution - solution to pinata challenge
#[arg(long)]
solution: u128,
},
///Transfer command
#[command(subcommand)]
Transfer(NativeTokenTransferProgramSubcommand),
///Chain command
#[command(subcommand)]
Chain(ChainSubcommand),
///Chain command
#[command(subcommand)]
Account(AccountSubcommand),
///Pinata command
#[command(subcommand)]
PinataProgram(PinataProgramSubcommand),
///Token command
#[command(subcommand)]
TokenProgram(TokenProgramSubcommand),
AuthenticatedTransferInitializePublicAccount {},
// Check the wallet can connect to the node and builtin local programs
// match the remote versions
CheckHealth {},
// TODO: Testnet only. Refactor to prevent compilation on mainnet.
// Claim piñata prize
ClaimPinataPrivateReceiverOwned {
///pinata_addr - valid 32 byte hex string
#[arg(long)]
pinata_addr: String,
///winner_addr - valid 32 byte hex string
#[arg(long)]
winner_addr: String,
///solution - solution to pinata challenge
#[arg(long)]
solution: u128,
},
///Token command
#[command(subcommand)]
TokenProgram(TokenProgramSubcommand),
}
///To execute commands, env var NSSA_WALLET_HOME_DIR must be set into directory with config
#[derive(Parser, Debug)]
#[clap(version, about)]
pub struct Args {
/// Continious run flag
#[arg(short, long)]
pub continious_run: bool,
/// Wallet command
#[command(subcommand)]
pub command: Command,
pub command: Option<Command>,
}
#[derive(Debug, Clone)]
@ -366,328 +232,27 @@ pub enum SubcommandReturnValue {
}
pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValue> {
let wallet_config = fetch_config()?;
let mut wallet_core = WalletCore::start_from_config_update_chain(wallet_config)?;
let wallet_config = fetch_config().await?;
let mut wallet_core = WalletCore::start_from_config_update_chain(wallet_config).await?;
let subcommand_ret = match command {
Command::SendNativeTokenTransferPublic { from, to, amount } => {
let from: Address = from.parse().unwrap();
let to: Address = to.parse().unwrap();
let res = wallet_core
.send_public_native_token_transfer(from, to, amount)
.await?;
println!("Results of tx send is {res:#?}");
let transfer_tx = wallet_core.poll_native_token_transfer(res.tx_hash).await?;
println!("Transaction data is {transfer_tx:?}");
let path = wallet_core.store_persistent_accounts()?;
println!("Stored persistent accounts at {path:#?}");
SubcommandReturnValue::Empty
Command::Transfer(transfer_subcommand) => {
transfer_subcommand
.handle_subcommand(&mut wallet_core)
.await?
}
Command::SendNativeTokenTransferPrivateOwnedAccount { from, to, amount } => {
let from: Address = from.parse().unwrap();
let to: Address = to.parse().unwrap();
let (res, [secret_from, secret_to]) = wallet_core
.send_private_native_token_transfer_owned_account(from, to, amount)
.await?;
println!("Results of tx send is {res:#?}");
let tx_hash = res.tx_hash;
let transfer_tx = wallet_core
.poll_native_token_transfer(tx_hash.clone())
.await?;
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
let acc_decode_data = vec![(secret_from, from), (secret_to, to)];
wallet_core
.decode_insert_privacy_preserving_transaction_results(tx, &acc_decode_data)?;
}
let path = wallet_core.store_persistent_accounts()?;
println!("Stored persistent accounts at {path:#?}");
SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }
Command::Chain(chain_subcommand) => {
chain_subcommand.handle_subcommand(&mut wallet_core).await?
}
Command::SendNativeTokenTransferPrivateForeignAccount {
from,
to_npk,
to_ipk,
amount,
} => {
let from: Address = from.parse().unwrap();
let to_npk_res = hex::decode(to_npk)?;
let mut to_npk = [0; 32];
to_npk.copy_from_slice(&to_npk_res);
let to_npk = nssa_core::NullifierPublicKey(to_npk);
let to_ipk_res = hex::decode(to_ipk)?;
let mut to_ipk = [0u8; 33];
to_ipk.copy_from_slice(&to_ipk_res);
let to_ipk =
nssa_core::encryption::shared_key_derivation::Secp256k1Point(to_ipk.to_vec());
let (res, [secret_from, _]) = wallet_core
.send_private_native_token_transfer_outer_account(from, to_npk, to_ipk, amount)
.await?;
println!("Results of tx send is {res:#?}");
let tx_hash = res.tx_hash;
let transfer_tx = wallet_core
.poll_native_token_transfer(tx_hash.clone())
.await?;
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
let acc_decode_data = vec![(secret_from, from)];
wallet_core
.decode_insert_privacy_preserving_transaction_results(tx, &acc_decode_data)?;
}
let path = wallet_core.store_persistent_accounts()?;
println!("Stored persistent accounts at {path:#?}");
SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }
Command::Account(account_subcommand) => {
account_subcommand
.handle_subcommand(&mut wallet_core)
.await?
}
Command::SendNativeTokenTransferDeshielded { from, to, amount } => {
let from: Address = from.parse().unwrap();
let to: Address = to.parse().unwrap();
let (res, secret) = wallet_core
.send_deshielded_native_token_transfer(from, to, amount)
.await?;
println!("Results of tx send is {res:#?}");
let tx_hash = res.tx_hash;
let transfer_tx = wallet_core
.poll_native_token_transfer(tx_hash.clone())
.await?;
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
let acc_decode_data = vec![(secret, from)];
wallet_core
.decode_insert_privacy_preserving_transaction_results(tx, &acc_decode_data)?;
}
let path = wallet_core.store_persistent_accounts()?;
println!("Stored persistent accounts at {path:#?}");
SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }
}
Command::SendNativeTokenTransferShielded { from, to, amount } => {
let from: Address = from.parse().unwrap();
let to: Address = to.parse().unwrap();
let (res, secret) = wallet_core
.send_shielded_native_token_transfer(from, to, amount)
.await?;
println!("Results of tx send is {res:#?}");
let tx_hash = res.tx_hash;
let transfer_tx = wallet_core
.poll_native_token_transfer(tx_hash.clone())
.await?;
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
let acc_decode_data = vec![(secret, to)];
wallet_core
.decode_insert_privacy_preserving_transaction_results(tx, &acc_decode_data)?;
}
let path = wallet_core.store_persistent_accounts()?;
println!("Stored persistent accounts at {path:#?}");
SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }
}
Command::SendNativeTokenTransferShieldedForeignAccount {
from,
to_npk,
to_ipk,
amount,
} => {
let from: Address = from.parse().unwrap();
let to_npk_res = hex::decode(to_npk)?;
let mut to_npk = [0; 32];
to_npk.copy_from_slice(&to_npk_res);
let to_npk = nssa_core::NullifierPublicKey(to_npk);
let to_ipk_res = hex::decode(to_ipk)?;
let mut to_ipk = [0u8; 33];
to_ipk.copy_from_slice(&to_ipk_res);
let to_ipk =
nssa_core::encryption::shared_key_derivation::Secp256k1Point(to_ipk.to_vec());
let (res, _) = wallet_core
.send_shielded_native_token_transfer_outer_account(from, to_npk, to_ipk, amount)
.await?;
println!("Results of tx send is {res:#?}");
let tx_hash = res.tx_hash;
let path = wallet_core.store_persistent_accounts()?;
println!("Stored persistent accounts at {path:#?}");
SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }
}
Command::FetchPrivateAccount {
tx_hash,
acc_addr,
output_id: ciph_id,
} => {
let acc_addr: Address = acc_addr.parse().unwrap();
let account_key_chain = wallet_core
.storage
.user_data
.user_private_accounts
.get(&acc_addr);
let Some((account_key_chain, _)) = account_key_chain else {
anyhow::bail!("Account not found");
};
let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?;
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
let to_ebc = tx.message.encrypted_private_post_states[ciph_id].clone();
let to_comm = tx.message.new_commitments[ciph_id].clone();
let shared_secret = account_key_chain.calculate_shared_secret_receiver(to_ebc.epk);
let res_acc_to = nssa_core::EncryptionScheme::decrypt(
&to_ebc.ciphertext,
&shared_secret,
&to_comm,
ciph_id as u32,
)
.unwrap();
println!("RES acc to {res_acc_to:#?}");
println!("Transaction data is {:?}", tx.message);
wallet_core
.storage
.insert_private_account_data(acc_addr, res_acc_to);
}
let path = wallet_core.store_persistent_accounts()?;
println!("Stored persistent accounts at {path:#?}");
SubcommandReturnValue::Empty
}
Command::RegisterAccountPublic {} => {
let addr = wallet_core.create_new_account_public();
println!("Generated new account with addr {addr}");
let path = wallet_core.store_persistent_accounts()?;
println!("Stored persistent accounts at {path:#?}");
SubcommandReturnValue::RegisterAccount { addr }
}
Command::RegisterAccountPrivate {} => {
let addr = wallet_core.create_new_account_private();
let (key, _) = wallet_core
.storage
.user_data
.get_private_account(&addr)
.unwrap();
println!("Generated new account with addr {addr}");
println!("With npk {}", hex::encode(&key.nullifer_public_key));
println!(
"With ipk {}",
hex::encode(key.incoming_viewing_public_key.to_bytes())
);
let path = wallet_core.store_persistent_accounts()?;
println!("Stored persistent accounts at {path:#?}");
SubcommandReturnValue::RegisterAccount { addr }
}
Command::FetchTx { tx_hash } => {
let tx_obj = wallet_core
.sequencer_client
.get_transaction_by_hash(tx_hash)
.await?;
println!("Transaction object {tx_obj:#?}");
SubcommandReturnValue::Empty
}
Command::GetPublicAccountBalance { addr } => {
let addr = Address::from_str(&addr)?;
let balance = wallet_core.get_account_balance(addr).await?;
println!("Accounts {addr} balance is {balance}");
SubcommandReturnValue::Empty
}
Command::GetPublicAccountNonce { addr } => {
let addr = Address::from_str(&addr)?;
let nonce = wallet_core.get_accounts_nonces(vec![addr]).await?[0];
println!("Accounts {addr} nonce is {nonce}");
SubcommandReturnValue::Empty
}
Command::GetPublicAccount { addr } => {
let addr: Address = addr.parse()?;
let account = wallet_core.get_account_public(addr).await?;
let account_hr: HumanReadableAccount = account.clone().into();
println!("{}", serde_json::to_string(&account_hr).unwrap());
SubcommandReturnValue::Account(account)
}
Command::GetPrivateAccount { addr } => {
let addr: Address = addr.parse()?;
if let Some(account) = wallet_core.get_account_private(&addr) {
let account_hr: HumanReadableAccount = account.into();
println!("{}", serde_json::to_string(&account_hr).unwrap());
} else {
println!("Private account not found.");
}
SubcommandReturnValue::Empty
}
Command::ClaimPinata {
pinata_addr,
winner_addr,
solution,
} => {
let res = wallet_core
.claim_pinata(
pinata_addr.parse().unwrap(),
winner_addr.parse().unwrap(),
solution,
)
.await?;
info!("Results of tx send is {res:#?}");
SubcommandReturnValue::Empty
Command::PinataProgram(pinata_subcommand) => {
pinata_subcommand
.handle_subcommand(&mut wallet_core)
.await?
}
Command::CheckHealth {} => {
let remote_program_ids = wallet_core
@ -719,63 +284,12 @@ pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValu
SubcommandReturnValue::Empty
}
Command::ClaimPinataPrivateReceiverOwned {
pinata_addr,
winner_addr,
solution,
} => {
let pinata_addr = pinata_addr.parse().unwrap();
let winner_addr = winner_addr.parse().unwrap();
let winner_initialization = wallet_core
.check_private_account_initialized(&winner_addr)
.await?;
let (res, [secret_winner]) = if let Some(winner_proof) = winner_initialization {
wallet_core
.claim_pinata_private_owned_account_already_initialized(
pinata_addr,
winner_addr,
solution,
winner_proof,
)
.await?
} else {
wallet_core
.claim_pinata_private_owned_account_not_initialized(
pinata_addr,
winner_addr,
solution,
)
.await?
};
info!("Results of tx send is {res:#?}");
let tx_hash = res.tx_hash;
let transfer_tx = wallet_core
.poll_native_token_transfer(tx_hash.clone())
.await?;
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
let acc_decode_data = vec![(secret_winner, winner_addr)];
wallet_core
.decode_insert_privacy_preserving_transaction_results(tx, &acc_decode_data)?;
}
let path = wallet_core.store_persistent_accounts()?;
println!("Stored persistent accounts at {path:#?}");
SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }
}
Command::AuthenticatedTransferInitializePublicAccount {} => {
let addr = wallet_core.create_new_account_public();
println!("Generated new account with addr {addr}");
let path = wallet_core.store_persistent_accounts()?;
let path = wallet_core.store_persistent_accounts().await?;
println!("Stored persistent accounts at {path:#?}");
@ -796,3 +310,88 @@ pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValu
Ok(subcommand_ret)
}
pub async fn execute_continious_run() -> Result<()> {
let config = fetch_config().await?;
let seq_client = Arc::new(SequencerClient::new(config.sequencer_addr.clone())?);
let mut wallet_core = WalletCore::start_from_config_update_chain(config.clone()).await?;
let mut latest_block_num = seq_client.get_last_block().await?.last_block;
let mut curr_last_block = latest_block_num;
loop {
for block_id in curr_last_block..(latest_block_num + 1) {
let block = borsh::from_slice::<HashableBlockData>(
&seq_client.get_block(block_id).await?.block,
)?;
for tx in block.transactions {
let nssa_tx = NSSATransaction::try_from(&tx)?;
if let NSSATransaction::PrivacyPreserving(tx) = nssa_tx {
let mut affected_accounts = vec![];
for (acc_addr, (key_chain, _)) in
&wallet_core.storage.user_data.user_private_accounts
{
let view_tag = EncryptedAccountData::compute_view_tag(
key_chain.nullifer_public_key.clone(),
key_chain.incoming_viewing_public_key.clone(),
);
for (ciph_id, encrypted_data) in tx
.message()
.encrypted_private_post_states
.iter()
.enumerate()
{
if encrypted_data.view_tag == view_tag {
let ciphertext = &encrypted_data.ciphertext;
let commitment = &tx.message.new_commitments[ciph_id];
let shared_secret = key_chain
.calculate_shared_secret_receiver(encrypted_data.epk.clone());
let res_acc = nssa_core::EncryptionScheme::decrypt(
ciphertext,
&shared_secret,
commitment,
ciph_id as u32,
);
if let Some(res_acc) = res_acc {
println!(
"Received new account for addr {acc_addr:#?} with account object {res_acc:#?}"
);
affected_accounts.push((*acc_addr, res_acc));
}
}
}
}
for (affected_addr, new_acc) in affected_accounts {
wallet_core
.storage
.insert_private_account_data(affected_addr, new_acc);
}
}
}
wallet_core.store_persistent_accounts().await?;
println!(
"Block at id {block_id} with timestamp {} parsed",
block.timestamp
);
}
curr_last_block = latest_block_num + 1;
tokio::time::sleep(std::time::Duration::from_millis(
config.seq_poll_timeout_millis,
))
.await;
latest_block_num = seq_client.get_last_block().await?.last_block;
}
}

View File

@ -1,7 +1,7 @@
use anyhow::Result;
use clap::Parser;
use clap::{CommandFactory, Parser};
use tokio::runtime::Builder;
use wallet::{Args, execute_subcommand};
use wallet::{Args, execute_continious_run, execute_subcommand};
pub const NUM_THREADS: usize = 2;
@ -17,7 +17,14 @@ fn main() -> Result<()> {
env_logger::init();
runtime.block_on(async move {
execute_subcommand(args.command).await.unwrap();
if let Some(command) = args.command {
execute_subcommand(command).await.unwrap();
} else if args.continious_run {
execute_continious_run().await.unwrap();
} else {
let help = Args::command().render_long_help();
println!("{help}");
}
});
Ok(())

View File

@ -6,7 +6,7 @@ use nssa::{
program::Program,
};
use nssa_core::{
Commitment, NullifierPublicKey, SharedSecretKey, account::AccountWithMetadata,
Commitment, MembershipProof, NullifierPublicKey, SharedSecretKey, account::AccountWithMetadata,
encryption::IncomingViewingPublicKey,
};
@ -98,11 +98,12 @@ impl WalletCore {
}
}
pub async fn send_private_native_token_transfer_owned_account(
pub async fn send_private_native_token_transfer_owned_account_already_initialized(
&self,
from: Address,
to: Address,
balance_to_move: u128,
to_proof: MembershipProof,
) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> {
let Some((from_keys, from_acc)) =
self.storage.user_data.get_private_account(&from).cloned()
@ -124,7 +125,6 @@ impl WalletCore {
let program = Program::authenticated_transfer_program();
let sender_commitment = Commitment::new(&from_npk, &from_acc);
let receiver_commitment = Commitment::new(&to_npk, &to_acc);
let sender_pre = AccountWithMetadata::new(from_acc.clone(), true, &from_npk);
let recipient_pre = AccountWithMetadata::new(to_acc.clone(), true, &to_npk);
@ -153,14 +153,7 @@ impl WalletCore {
.unwrap()
.unwrap(),
),
(
to_keys.private_key_holder.nullifier_secret_key,
self.sequencer_client
.get_proof_for_commitment(receiver_commitment)
.await
.unwrap()
.unwrap(),
),
(to_keys.private_key_holder.nullifier_secret_key, to_proof),
],
&program,
)
@ -196,4 +189,92 @@ impl WalletCore {
Err(ExecutionFailureKind::InsufficientFundsError)
}
}
pub async fn send_private_native_token_transfer_owned_account_not_initialized(
&self,
from: Address,
to: Address,
balance_to_move: u128,
) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> {
let Some((from_keys, from_acc)) =
self.storage.user_data.get_private_account(&from).cloned()
else {
return Err(ExecutionFailureKind::KeyNotFoundError);
};
let Some((to_keys, to_acc)) = self.storage.user_data.get_private_account(&to).cloned()
else {
return Err(ExecutionFailureKind::KeyNotFoundError);
};
let from_npk = from_keys.nullifer_public_key;
let from_ipk = from_keys.incoming_viewing_public_key;
let to_npk = to_keys.nullifer_public_key.clone();
let to_ipk = to_keys.incoming_viewing_public_key.clone();
if from_acc.balance >= balance_to_move {
let program = Program::authenticated_transfer_program();
let sender_commitment = Commitment::new(&from_npk, &from_acc);
let sender_pre = AccountWithMetadata::new(from_acc.clone(), true, &from_npk);
let recipient_pre = AccountWithMetadata::new(to_acc.clone(), false, &to_npk);
let eph_holder_from = EphemeralKeyHolder::new(&from_npk);
let shared_secret_from = eph_holder_from.calculate_shared_secret_sender(&from_ipk);
let eph_holder_to = EphemeralKeyHolder::new(&to_npk);
let shared_secret_to = eph_holder_to.calculate_shared_secret_sender(&to_ipk);
let (output, proof) = circuit::execute_and_prove(
&[sender_pre, recipient_pre],
&Program::serialize_instruction(balance_to_move).unwrap(),
&[1, 2],
&produce_random_nonces(2),
&[
(from_npk.clone(), shared_secret_from.clone()),
(to_npk.clone(), shared_secret_to.clone()),
],
&[(
from_keys.private_key_holder.nullifier_secret_key,
self.sequencer_client
.get_proof_for_commitment(sender_commitment)
.await
.unwrap()
.unwrap(),
)],
&program,
)
.unwrap();
let message = Message::try_from_circuit_output(
vec![],
vec![],
vec![
(
from_npk.clone(),
from_ipk.clone(),
eph_holder_from.generate_ephemeral_public_key(),
),
(
to_npk.clone(),
to_ipk.clone(),
eph_holder_to.generate_ephemeral_public_key(),
),
],
output,
)
.unwrap();
let witness_set = WitnessSet::for_message(&message, proof, &[]);
let tx = PrivacyPreservingTransaction::new(message, witness_set);
Ok((
self.sequencer_client.send_tx_private(tx).await?,
[shared_secret_from, shared_secret_to],
))
} else {
Err(ExecutionFailureKind::InsufficientFundsError)
}
}
}

View File

@ -6,14 +6,84 @@ use nssa::{
program::Program,
};
use nssa_core::{
Commitment, NullifierPublicKey, SharedSecretKey, account::AccountWithMetadata,
MembershipProof, NullifierPublicKey, SharedSecretKey, account::AccountWithMetadata,
encryption::IncomingViewingPublicKey,
};
use crate::{WalletCore, helperfunctions::produce_random_nonces};
impl WalletCore {
pub async fn send_shielded_native_token_transfer(
pub async fn send_shielded_native_token_transfer_already_initialized(
&self,
from: Address,
to: Address,
balance_to_move: u128,
to_proof: MembershipProof,
) -> Result<(SendTxResponse, SharedSecretKey), ExecutionFailureKind> {
let Ok(from_acc) = self.get_account_public(from).await else {
return Err(ExecutionFailureKind::KeyNotFoundError);
};
let Some((to_keys, to_acc)) = self.storage.user_data.get_private_account(&to).cloned()
else {
return Err(ExecutionFailureKind::KeyNotFoundError);
};
let to_npk = to_keys.nullifer_public_key.clone();
let to_ipk = to_keys.incoming_viewing_public_key.clone();
if from_acc.balance >= balance_to_move {
let program = Program::authenticated_transfer_program();
let sender_pre = AccountWithMetadata::new(from_acc.clone(), true, from);
let recipient_pre = AccountWithMetadata::new(to_acc.clone(), true, &to_npk);
let eph_holder = EphemeralKeyHolder::new(&to_npk);
let shared_secret = eph_holder.calculate_shared_secret_sender(&to_ipk);
let (output, proof) = circuit::execute_and_prove(
&[sender_pre, recipient_pre],
&nssa::program::Program::serialize_instruction(balance_to_move).unwrap(),
&[0, 1],
&produce_random_nonces(1),
&[(to_npk.clone(), shared_secret.clone())],
&[(to_keys.private_key_holder.nullifier_secret_key, to_proof)],
&program,
)
.unwrap();
let message = Message::try_from_circuit_output(
vec![from],
vec![from_acc.nonce],
vec![(
to_npk.clone(),
to_ipk.clone(),
eph_holder.generate_ephemeral_public_key(),
)],
output,
)
.unwrap();
let signing_key = self.storage.user_data.get_pub_account_signing_key(&from);
let Some(signing_key) = signing_key else {
return Err(ExecutionFailureKind::KeyNotFoundError);
};
let witness_set = WitnessSet::for_message(&message, proof, &[signing_key]);
let tx = PrivacyPreservingTransaction::new(message, witness_set);
Ok((
self.sequencer_client.send_tx_private(tx).await?,
shared_secret,
))
} else {
Err(ExecutionFailureKind::InsufficientFundsError)
}
}
pub async fn send_shielded_native_token_transfer_not_initialized(
&self,
from: Address,
to: Address,
@ -34,10 +104,8 @@ impl WalletCore {
if from_acc.balance >= balance_to_move {
let program = Program::authenticated_transfer_program();
let receiver_commitment = Commitment::new(&to_npk, &to_acc);
let sender_pre = AccountWithMetadata::new(from_acc.clone(), true, from);
let recipient_pre = AccountWithMetadata::new(to_acc.clone(), true, &to_npk);
let recipient_pre = AccountWithMetadata::new(to_acc.clone(), false, &to_npk);
let eph_holder = EphemeralKeyHolder::new(&to_npk);
let shared_secret = eph_holder.calculate_shared_secret_sender(&to_ipk);
@ -45,17 +113,10 @@ impl WalletCore {
let (output, proof) = circuit::execute_and_prove(
&[sender_pre, recipient_pre],
&nssa::program::Program::serialize_instruction(balance_to_move).unwrap(),
&[0, 1],
&[0, 2],
&produce_random_nonces(1),
&[(to_npk.clone(), shared_secret.clone())],
&[(
to_keys.private_key_holder.nullifier_secret_key,
self.sequencer_client
.get_proof_for_commitment(receiver_commitment)
.await
.unwrap()
.unwrap(),
)],
&[],
&program,
)
.unwrap();