diff --git a/Cargo.toml b/Cargo.toml index e340ffb..a4a2b89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,7 @@ bip39 = "2.2.0" hmac-sha512 = "1.1.7" chrono = "0.4.41" borsh = "1.5.7" +base58 = "0.2.0" rocksdb = { version = "0.21.0", default-features = false, features = [ "snappy", diff --git a/common/src/lib.rs b/common/src/lib.rs index c44d03f..7976479 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -8,3 +8,5 @@ pub mod transaction; //TODO: Compile only for tests pub mod test_utils; pub type HashType = [u8; 32]; + +pub const PINATA_BASE58: &str = "EfQhKQAkX2FJiwNii2WFQsGndjvF1Mzd7RuVe7QdPLw7"; diff --git a/integration_tests/configs/debug/sequencer/sequencer_config.json b/integration_tests/configs/debug/sequencer/sequencer_config.json index 2a2037d..beb39cb 100644 --- a/integration_tests/configs/debug/sequencer/sequencer_config.json +++ b/integration_tests/configs/debug/sequencer/sequencer_config.json @@ -8,49 +8,49 @@ "port": 3040, "initial_accounts": [ { - "addr": "d07ad2e84b27fa00c262f0a1eea0ff35ca0973547e6a106f72f193c2dc838b44", + "addr": "BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy", "balance": 10000 }, { - "addr": "e7ae77c5ef1a05999344af499fc78a1705398d62ed06cf2e1479f6def89a39bc", + "addr": "Gj1mJy5W7J5pfmLRujmQaLfLMWidNxQ6uwnhb666ZwHw", "balance": 20000 } ], "initial_commitments": [ { "npk": [ - 193, - 209, - 150, - 113, - 47, - 241, - 48, - 145, - 250, - 79, - 235, - 51, - 119, - 40, - 184, - 232, - 5, + 63, + 202, + 178, + 231, + 183, + 82, + 237, + 212, + 216, 221, - 36, - 21, - 201, - 106, - 90, - 210, - 129, - 106, - 71, - 99, - 208, + 215, + 255, 153, - 75, - 215 + 101, + 177, + 161, + 254, + 210, + 128, + 122, + 54, + 190, + 230, + 151, + 183, + 64, + 225, + 229, + 113, + 1, + 228, + 97 ], "account": { "program_owner": [ @@ -70,38 +70,38 @@ }, { "npk": [ - 27, - 250, + 192, + 251, + 166, + 243, + 167, + 236, + 84, + 249, + 35, 136, - 142, - 88, - 128, - 138, - 21, - 49, - 183, - 118, - 160, - 117, - 114, - 110, - 47, - 136, - 87, - 60, - 70, - 59, - 60, - 18, - 223, - 23, - 147, - 241, - 5, - 184, - 103, + 130, + 172, + 219, 225, - 105 + 161, + 139, + 229, + 89, + 243, + 125, + 194, + 213, + 209, + 30, + 23, + 174, + 100, + 244, + 124, + 74, + 140, + 47 ], "account": { "program_owner": [ @@ -154,4 +154,4 @@ 37, 37 ] -} +} \ No newline at end of file diff --git a/integration_tests/configs/debug/wallet/wallet_config.json b/integration_tests/configs/debug/wallet/wallet_config.json index 0081da6..95c95e9 100644 --- a/integration_tests/configs/debug/wallet/wallet_config.json +++ b/integration_tests/configs/debug/wallet/wallet_config.json @@ -9,85 +9,85 @@ "initial_accounts": [ { "Public": { - "address": "d07ad2e84b27fa00c262f0a1eea0ff35ca0973547e6a106f72f193c2dc838b44", + "address": "BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy", "pub_sign_key": [ - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1 + 16, + 162, + 106, + 154, + 236, + 125, + 52, + 184, + 35, + 100, + 238, + 174, + 69, + 197, + 41, + 77, + 187, + 10, + 118, + 75, + 0, + 11, + 148, + 238, + 185, + 181, + 133, + 17, + 220, + 72, + 124, + 77 ] } }, { "Public": { - "address": "e7ae77c5ef1a05999344af499fc78a1705398d62ed06cf2e1479f6def89a39bc", + "address": "Gj1mJy5W7J5pfmLRujmQaLfLMWidNxQ6uwnhb666ZwHw", "pub_sign_key": [ - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2 + 113, + 121, + 64, + 177, + 204, + 85, + 229, + 214, + 178, + 6, + 109, + 191, + 29, + 154, + 63, + 38, + 242, + 18, + 244, + 219, + 8, + 208, + 35, + 136, + 23, + 127, + 207, + 237, + 216, + 169, + 190, + 27 ] } }, { "Private": { - "address": "d360d6b5763f71ac6af56253687fd7d556d5c6c64312e53c0b92ef039a4375df", + "address": "3oCG8gqdKLMegw4rRfyaMQvuPHpcASt7xwttsmnZLSkw", "account": { "program_owner": [ 0, @@ -105,218 +105,218 @@ }, "key_chain": { "secret_spending_key": [ - 10, - 125, - 171, - 38, - 201, - 35, - 164, - 43, - 7, - 80, - 7, - 215, - 97, - 42, - 48, - 229, - 101, - 216, - 140, - 21, - 170, - 214, + 251, 82, - 53, + 235, + 1, + 146, + 96, + 30, + 81, + 162, + 234, + 33, + 15, + 123, + 129, 116, - 22, - 62, - 79, - 61, - 76, - 71, - 79 + 0, + 84, + 136, + 176, + 70, + 190, + 224, + 161, + 54, + 134, + 142, + 154, + 1, + 18, + 251, + 242, + 189 ], "private_key_holder": { "nullifier_secret_key": [ - 228, - 136, - 4, + 29, + 250, + 10, + 187, + 35, + 123, + 180, + 250, + 246, + 97, + 216, + 153, + 44, 156, - 33, - 40, - 194, - 172, - 95, - 168, - 201, - 33, - 24, - 30, - 126, - 197, - 156, - 113, - 64, - 162, - 131, - 210, - 110, - 60, - 24, - 154, - 86, - 59, - 184, - 95, - 245, - 176 + 16, + 93, + 241, + 26, + 174, + 219, + 72, + 84, + 34, + 247, + 112, + 101, + 217, + 243, + 189, + 173, + 75, + 20 ], "incoming_viewing_secret_key": [ - 197, - 33, - 51, - 200, - 1, - 121, - 60, - 52, - 233, - 234, - 12, - 166, - 196, - 227, - 187, - 1, - 10, - 101, - 183, - 105, - 140, - 28, - 152, + 251, + 201, + 22, + 154, + 100, + 165, + 218, + 108, + 163, + 190, + 135, + 91, + 145, + 84, + 69, + 241, + 46, + 117, 217, - 109, - 220, - 112, - 103, - 253, 110, - 98, - 6 + 197, + 248, + 91, + 193, + 14, + 104, + 88, + 103, + 67, + 153, + 182, + 158 ], "outgoing_viewing_secret_key": [ - 147, - 34, - 193, - 29, - 39, - 173, - 222, + 25, + 67, + 121, + 76, + 175, + 100, 30, - 118, - 199, - 44, - 204, - 43, - 232, - 107, - 223, - 249, - 207, - 245, - 183, - 63, - 209, - 129, - 48, - 254, - 66, - 22, - 199, - 81, - 145, - 126, - 92 + 198, + 105, + 123, + 49, + 169, + 75, + 178, + 75, + 210, + 100, + 143, + 210, + 243, + 228, + 243, + 21, + 18, + 36, + 84, + 164, + 186, + 139, + 113, + 214, + 12 ] }, "nullifer_public_key": [ - 193, - 209, - 150, - 113, - 47, - 241, - 48, - 145, - 250, - 79, - 235, - 51, - 119, - 40, - 184, - 232, - 5, + 63, + 202, + 178, + 231, + 183, + 82, + 237, + 212, + 216, 221, - 36, - 21, - 201, - 106, - 90, - 210, - 129, - 106, - 71, - 99, - 208, + 215, + 255, 153, - 75, - 215 + 101, + 177, + 161, + 254, + 210, + 128, + 122, + 54, + 190, + 230, + 151, + 183, + 64, + 225, + 229, + 113, + 1, + 228, + 97 ], "incoming_viewing_public_key": [ 3, - 78, + 235, + 139, + 131, + 237, 177, - 87, - 193, - 219, - 230, - 160, - 222, - 38, - 182, - 100, - 101, - 223, - 204, - 223, - 198, - 140, - 253, - 94, - 16, - 98, - 77, - 79, - 114, - 30, - 158, - 104, - 34, - 152, + 122, 189, - 31, - 95 + 6, + 177, + 167, + 178, + 202, + 117, + 246, + 58, + 28, + 65, + 132, + 79, + 220, + 139, + 119, + 243, + 187, + 160, + 212, + 121, + 61, + 247, + 116, + 72, + 205 ] } } }, { "Private": { - "address": "f27087ffc29b99035303697dcf6c8e323b1847d4261e6afd49e0d71c6dfa31ea", + "address": "AKTcXgJ1xoynta1Ec7y6Jso1z1JQtHqd7aPQ1h9er6xX", "account": { "program_owner": [ 0, @@ -334,214 +334,214 @@ }, "key_chain": { "secret_spending_key": [ - 153, - 109, - 202, - 226, - 97, - 212, - 77, - 147, - 75, - 107, - 153, - 106, - 89, - 167, - 49, - 230, - 122, + 238, + 171, + 241, + 69, + 111, + 217, + 85, + 64, + 19, + 82, + 18, + 189, + 32, + 91, 78, - 167, - 146, - 14, - 180, - 206, + 175, 107, - 96, - 193, - 255, - 122, - 207, - 30, - 142, - 99 + 7, + 109, + 60, + 52, + 44, + 243, + 230, + 72, + 244, + 192, + 92, + 137, + 33, + 118, + 254 ], "private_key_holder": { "nullifier_secret_key": [ - 128, + 25, + 211, 215, - 147, - 175, 119, - 16, - 140, - 219, - 155, - 134, - 27, - 81, - 64, - 40, - 196, - 240, - 61, - 144, - 232, - 164, - 181, 57, - 139, - 96, - 137, - 121, - 140, + 223, + 247, + 37, + 245, + 144, + 122, 29, - 169, - 68, - 187, - 65 + 118, + 245, + 83, + 228, + 23, + 9, + 101, + 120, + 88, + 33, + 238, + 207, + 128, + 61, + 110, + 2, + 89, + 62, + 164, + 13 ], "incoming_viewing_secret_key": [ - 185, - 121, - 146, - 213, - 13, - 3, - 93, - 206, - 25, - 127, - 155, - 21, - 155, - 115, + 193, + 181, + 14, + 196, + 142, + 84, + 15, + 65, + 128, + 101, + 70, + 196, + 241, + 47, 130, - 27, - 57, - 5, - 116, - 80, - 62, - 214, - 67, - 228, - 147, - 189, - 28, - 200, - 62, - 152, - 178, - 103 + 221, + 23, + 146, + 161, + 237, + 221, + 40, + 19, + 126, + 59, + 15, + 169, + 236, + 25, + 105, + 104, + 231 ], "outgoing_viewing_secret_key": [ - 163, - 58, - 118, - 160, + 20, + 170, + 220, + 108, + 41, + 23, + 155, + 217, + 247, + 190, 175, - 86, - 72, + 168, + 247, + 34, + 105, + 134, + 114, + 74, + 104, 91, - 81, - 69, - 150, - 154, - 113, 211, - 118, - 110, - 25, - 156, - 250, - 67, - 212, - 198, - 147, - 231, - 213, - 136, - 212, - 198, - 192, - 255, + 62, 126, - 122 + 13, + 130, + 100, + 241, + 214, + 250, + 236, + 38, + 150 ] }, "nullifer_public_key": [ - 27, - 250, + 192, + 251, + 166, + 243, + 167, + 236, + 84, + 249, + 35, 136, - 142, - 88, - 128, - 138, - 21, - 49, - 183, - 118, - 160, - 117, - 114, - 110, - 47, - 136, - 87, - 60, - 70, - 59, - 60, - 18, - 223, - 23, - 147, - 241, - 5, - 184, - 103, + 130, + 172, + 219, 225, - 105 + 161, + 139, + 229, + 89, + 243, + 125, + 194, + 213, + 209, + 30, + 23, + 174, + 100, + 244, + 124, + 74, + 140, + 47 ], "incoming_viewing_public_key": [ 2, - 56, - 160, - 1, - 22, - 197, - 187, - 214, - 204, - 221, - 84, - 87, - 12, - 204, - 0, - 119, - 116, - 176, - 6, - 149, - 145, - 100, - 211, - 162, - 19, - 158, - 197, - 112, - 142, - 172, - 1, + 181, 98, - 226 + 93, + 216, + 241, + 241, + 110, + 58, + 198, + 119, + 174, + 250, + 184, + 1, + 204, + 200, + 173, + 44, + 238, + 37, + 247, + 170, + 156, + 100, + 254, + 116, + 242, + 28, + 183, + 187, + 77, + 255 ] } } } ] -} +} \ No newline at end of file diff --git a/integration_tests/src/data_changer.bin b/integration_tests/src/data_changer.bin index d201f91..c4fbec0 100644 Binary files a/integration_tests/src/data_changer.bin and b/integration_tests/src/data_changer.bin differ diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index bbd5066..f718f9d 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -32,18 +32,24 @@ struct Args { test_name: String, } -pub const ACC_SENDER: &str = "d07ad2e84b27fa00c262f0a1eea0ff35ca0973547e6a106f72f193c2dc838b44"; -pub const ACC_RECEIVER: &str = "e7ae77c5ef1a05999344af499fc78a1705398d62ed06cf2e1479f6def89a39bc"; +pub const ACC_SENDER: &str = "BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy"; +pub const ACC_RECEIVER: &str = "Gj1mJy5W7J5pfmLRujmQaLfLMWidNxQ6uwnhb666ZwHw"; -pub const ACC_SENDER_PRIVATE: &str = - "d360d6b5763f71ac6af56253687fd7d556d5c6c64312e53c0b92ef039a4375df"; -pub const ACC_RECEIVER_PRIVATE: &str = - "f27087ffc29b99035303697dcf6c8e323b1847d4261e6afd49e0d71c6dfa31ea"; +pub const ACC_SENDER_PRIVATE: &str = "3oCG8gqdKLMegw4rRfyaMQvuPHpcASt7xwttsmnZLSkw"; +pub const ACC_RECEIVER_PRIVATE: &str = "AKTcXgJ1xoynta1Ec7y6Jso1z1JQtHqd7aPQ1h9er6xX"; pub const TIME_TO_WAIT_FOR_BLOCK_SECONDS: u64 = 12; pub const NSSA_PROGRAM_FOR_TEST_DATA_CHANGER: &[u8] = include_bytes!("data_changer.bin"); +fn make_public_account_input_from_str(addr: &str) -> String { + format!("Public/{addr}") +} + +fn make_private_account_input_from_str(addr: &str) -> String { + format!("Private/{addr}") +} + #[allow(clippy::type_complexity)] pub async fn pre_test( home_dir: PathBuf, @@ -86,7 +92,7 @@ pub async fn post_test(residual: (ServerHandle, JoinHandle>, TempDir) seq_http_server_handle.stop(true).await; let wallet_home = wallet::helperfunctions::get_home().unwrap(); - let persistent_data_home = wallet_home.join("curr_accounts.json"); + let persistent_data_home = wallet_home.join("storage.json"); //Removing persistent accounts after run to not affect other executions //Not necessary an error, if fails as there is tests for failure scenario @@ -157,3 +163,20 @@ async fn verify_commitment_is_in_state( Ok(Some(_)) ) } + +#[cfg(test)] +mod tests { + use crate::{make_private_account_input_from_str, make_public_account_input_from_str}; + + #[test] + fn correct_addr_from_prefix() { + let addr1 = "cafecafe"; + let addr2 = "deadbeaf"; + + let addr1_pub = make_public_account_input_from_str(addr1); + let addr2_priv = make_private_account_input_from_str(addr2); + + assert_eq!(addr1_pub, "Public/cafecafe".to_string()); + assert_eq!(addr2_priv, "Private/deadbeaf".to_string()); + } +} diff --git a/integration_tests/src/test_suite_map.rs b/integration_tests/src/test_suite_map.rs index 65b2095..e0c7f86 100644 --- a/integration_tests/src/test_suite_map.rs +++ b/integration_tests/src/test_suite_map.rs @@ -1,34 +1,26 @@ use std::{collections::HashMap, path::PathBuf, pin::Pin, time::Duration}; -use common::sequencer_client::SequencerClient; +use common::{PINATA_BASE58, sequencer_client::SequencerClient}; use log::info; use nssa::{Address, ProgramDeploymentTransaction, program::Program}; use nssa_core::{NullifierPublicKey, encryption::shared_key_derivation::Secp256k1Point}; use wallet::{ Command, SubcommandReturnValue, WalletCore, cli::{ - account::{AccountSubcommand, FetchSubcommand, RegisterSubcommand}, - native_token_transfer_program::{ - NativeTokenTransferProgramSubcommand, NativeTokenTransferProgramSubcommandPrivate, - NativeTokenTransferProgramSubcommandShielded, - }, - pinata_program::{ - PinataProgramSubcommand, PinataProgramSubcommandPrivate, PinataProgramSubcommandPublic, - }, - token_program::{ - TokenProgramSubcommand, TokenProgramSubcommandDeshielded, - TokenProgramSubcommandPrivate, TokenProgramSubcommandPublic, - TokenProgramSubcommandShielded, - }, + account::{AccountSubcommand, NewSubcommand}, + native_token_transfer_program::AuthTransferSubcommand, + pinata_program::PinataProgramAgnosticSubcommand, + token_program::TokenProgramAgnosticSubcommand, }, - config::PersistentAccountData, - helperfunctions::{fetch_config, fetch_persistent_accounts}, + config::{PersistentAccountData, PersistentStorage}, + helperfunctions::{fetch_config, fetch_persistent_storage}, }; use crate::{ ACC_RECEIVER, ACC_RECEIVER_PRIVATE, ACC_SENDER, ACC_SENDER_PRIVATE, NSSA_PROGRAM_FOR_TEST_DATA_CHANGER, TIME_TO_WAIT_FOR_BLOCK_SECONDS, - fetch_privacy_preserving_tx, + fetch_privacy_preserving_tx, make_private_account_input_from_str, + make_public_account_input_from_str, }; use crate::{post_test, pre_test, verify_commitment_is_in_state}; @@ -39,10 +31,12 @@ pub fn prepare_function_map() -> HashMap { #[nssa_integration_test] pub async fn test_success() { - info!("test_success"); - let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Public { - from: ACC_SENDER.to_string(), - to: ACC_RECEIVER.to_string(), + info!("########## test_success ##########"); + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: make_public_account_input_from_str(ACC_SENDER), + to: Some(make_public_account_input_from_str(ACC_RECEIVER)), + to_npk: None, + to_ipk: None, amount: 100, }); @@ -76,8 +70,8 @@ pub fn prepare_function_map() -> HashMap { #[nssa_integration_test] pub async fn test_success_move_to_another_account() { - info!("test_success_move_to_another_account"); - let command = Command::Account(AccountSubcommand::Register(RegisterSubcommand::Public {})); + info!("########## test_success_move_to_another_account ##########"); + let command = Command::Account(AccountSubcommand::New(NewSubcommand::Public {})); let wallet_config = fetch_config().await.unwrap(); @@ -85,7 +79,10 @@ pub fn prepare_function_map() -> HashMap { wallet::execute_subcommand(command).await.unwrap(); - let persistent_accounts = fetch_persistent_accounts().await.unwrap(); + let PersistentStorage { + accounts: persistent_accounts, + last_synced_block: _, + } = fetch_persistent_storage().await.unwrap(); let mut new_persistent_account_addr = String::new(); @@ -101,9 +98,13 @@ pub fn prepare_function_map() -> HashMap { panic!("Failed to produce new account, not present in persistent accounts"); } - let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Public { - from: ACC_SENDER.to_string(), - to: new_persistent_account_addr.clone(), + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: make_public_account_input_from_str(ACC_SENDER), + to: Some(make_public_account_input_from_str( + &new_persistent_account_addr, + )), + to_npk: None, + to_ipk: None, amount: 100, }); @@ -133,10 +134,12 @@ pub fn prepare_function_map() -> HashMap { #[nssa_integration_test] pub async fn test_failure() { - info!("test_failure"); - let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Public { - from: ACC_SENDER.to_string(), - to: ACC_RECEIVER.to_string(), + info!("########## test_failure ##########"); + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: make_public_account_input_from_str(ACC_SENDER), + to: Some(make_public_account_input_from_str(ACC_RECEIVER)), + to_npk: None, + to_ipk: None, amount: 1000000, }); @@ -172,10 +175,12 @@ pub fn prepare_function_map() -> HashMap { #[nssa_integration_test] pub async fn test_success_two_transactions() { - info!("test_success_two_transactions"); - let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Public { - from: ACC_SENDER.to_string(), - to: ACC_RECEIVER.to_string(), + info!("########## test_success_two_transactions ##########"); + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: make_public_account_input_from_str(ACC_SENDER), + to: Some(make_public_account_input_from_str(ACC_RECEIVER)), + to_npk: None, + to_ipk: None, amount: 100, }); @@ -206,9 +211,11 @@ pub fn prepare_function_map() -> HashMap { info!("First TX Success!"); - let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Public { - from: ACC_SENDER.to_string(), - to: ACC_RECEIVER.to_string(), + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: make_public_account_input_from_str(ACC_SENDER), + to: Some(make_public_account_input_from_str(ACC_RECEIVER)), + to_npk: None, + to_ipk: None, amount: 100, }); @@ -238,7 +245,7 @@ pub fn prepare_function_map() -> HashMap { #[nssa_integration_test] pub async fn test_get_account() { - info!("test_get_account"); + info!("########## test_get_account ##########"); let wallet_config = fetch_config().await.unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); @@ -261,28 +268,32 @@ pub fn prepare_function_map() -> HashMap { /// token transfer to a new account. #[nssa_integration_test] pub async fn test_success_token_program() { + info!("########## test_success_token_program ##########"); let wallet_config = fetch_config().await.unwrap(); // Create new account for the token definition - wallet::execute_subcommand(Command::Account(AccountSubcommand::Register( - RegisterSubcommand::Public {}, + wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + NewSubcommand::Public {}, ))) .await .unwrap(); // Create new account for the token supply holder - wallet::execute_subcommand(Command::Account(AccountSubcommand::Register( - RegisterSubcommand::Public {}, + wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + NewSubcommand::Public {}, ))) .await .unwrap(); // Create new account for receiving a token transaction - wallet::execute_subcommand(Command::Account(AccountSubcommand::Register( - RegisterSubcommand::Public {}, + wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + NewSubcommand::Public {}, ))) .await .unwrap(); - let persistent_accounts = fetch_persistent_accounts().await.unwrap(); + let PersistentStorage { + accounts: persistent_accounts, + last_synced_block: _, + } = fetch_persistent_storage().await.unwrap(); let mut new_persistent_accounts_addr = Vec::new(); @@ -304,14 +315,13 @@ pub fn prepare_function_map() -> HashMap { .expect("Failed to produce new account, not present in persistent accounts"); // Create new token - let subcommand = - TokenProgramSubcommand::Public(TokenProgramSubcommandPublic::CreateNewToken { - definition_addr: definition_addr.to_string(), - supply_addr: supply_addr.to_string(), - name: "A NAME".to_string(), - total_supply: 37, - }); - wallet::execute_subcommand(Command::TokenProgram(subcommand)) + let subcommand = TokenProgramAgnosticSubcommand::New { + definition_addr: make_public_account_input_from_str(&definition_addr.to_string()), + supply_addr: make_public_account_input_from_str(&supply_addr.to_string()), + name: "A NAME".to_string(), + total_supply: 37, + }; + wallet::execute_subcommand(Command::Token(subcommand)) .await .unwrap(); info!("Waiting for next block creation"); @@ -359,13 +369,17 @@ pub fn prepare_function_map() -> HashMap { ); // Transfer 7 tokens from `supply_acc` to the account at address `recipient_addr` - let subcommand = - TokenProgramSubcommand::Public(TokenProgramSubcommandPublic::TransferToken { - sender_addr: supply_addr.to_string(), - recipient_addr: recipient_addr.to_string(), - balance_to_move: 7, - }); - wallet::execute_subcommand(Command::TokenProgram(subcommand)) + let subcommand = TokenProgramAgnosticSubcommand::Send { + from: make_public_account_input_from_str(&supply_addr.to_string()), + to: Some(make_public_account_input_from_str( + &recipient_addr.to_string(), + )), + to_npk: None, + to_ipk: None, + amount: 7, + }; + + wallet::execute_subcommand(Command::Token(subcommand)) .await .unwrap(); info!("Waiting for next block creation"); @@ -411,13 +425,14 @@ pub fn prepare_function_map() -> HashMap { /// private token transfer to a new account. All accounts are owned except definition. #[nssa_integration_test] pub async fn test_success_token_program_private_owned() { + info!("########## test_success_token_program_private_owned ##########"); 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::Account(AccountSubcommand::Register( - RegisterSubcommand::Public {}, + } = wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + NewSubcommand::Public {}, ))) .await .unwrap() @@ -426,8 +441,8 @@ pub fn prepare_function_map() -> HashMap { }; // Create new account for the token supply holder (private) let SubcommandReturnValue::RegisterAccount { addr: supply_addr } = - wallet::execute_subcommand(Command::Account(AccountSubcommand::Register( - RegisterSubcommand::Private {}, + wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + NewSubcommand::Private {}, ))) .await .unwrap() @@ -437,8 +452,8 @@ pub fn prepare_function_map() -> HashMap { // Create new account for receiving a token transaction let SubcommandReturnValue::RegisterAccount { addr: recipient_addr, - } = wallet::execute_subcommand(Command::Account(AccountSubcommand::Register( - RegisterSubcommand::Private {}, + } = wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + NewSubcommand::Private {}, ))) .await .unwrap() @@ -447,16 +462,14 @@ pub fn prepare_function_map() -> HashMap { }; // Create new token - let subcommand = TokenProgramSubcommand::Private( - TokenProgramSubcommandPrivate::CreateNewTokenPrivateOwned { - definition_addr: definition_addr.to_string(), - supply_addr: supply_addr.to_string(), - name: "A NAME".to_string(), - total_supply: 37, - }, - ); + let subcommand = TokenProgramAgnosticSubcommand::New { + definition_addr: make_public_account_input_from_str(&definition_addr.to_string()), + supply_addr: make_private_account_input_from_str(&supply_addr.to_string()), + name: "A NAME".to_string(), + total_supply: 37, + }; - wallet::execute_subcommand(Command::TokenProgram(subcommand)) + wallet::execute_subcommand(Command::Token(subcommand)) .await .unwrap(); @@ -493,15 +506,17 @@ pub fn prepare_function_map() -> HashMap { assert!(verify_commitment_is_in_state(new_commitment1, &seq_client).await); // Transfer 7 tokens from `supply_acc` to the account at address `recipient_addr` - let subcommand = TokenProgramSubcommand::Private( - TokenProgramSubcommandPrivate::TransferTokenPrivateOwned { - sender_addr: supply_addr.to_string(), - recipient_addr: recipient_addr.to_string(), - balance_to_move: 7, - }, - ); + let subcommand = TokenProgramAgnosticSubcommand::Send { + from: make_private_account_input_from_str(&supply_addr.to_string()), + to: Some(make_private_account_input_from_str( + &recipient_addr.to_string(), + )), + to_npk: None, + to_ipk: None, + amount: 7, + }; - wallet::execute_subcommand(Command::TokenProgram(subcommand)) + wallet::execute_subcommand(Command::Token(subcommand)) .await .unwrap(); @@ -524,15 +539,17 @@ pub fn prepare_function_map() -> HashMap { assert!(verify_commitment_is_in_state(new_commitment2, &seq_client).await); // Transfer additional 7 tokens from `supply_acc` to the account at address `recipient_addr` - let subcommand = TokenProgramSubcommand::Private( - TokenProgramSubcommandPrivate::TransferTokenPrivateOwned { - sender_addr: supply_addr.to_string(), - recipient_addr: recipient_addr.to_string(), - balance_to_move: 7, - }, - ); + let subcommand = TokenProgramAgnosticSubcommand::Send { + from: make_private_account_input_from_str(&supply_addr.to_string()), + to: Some(make_private_account_input_from_str( + &recipient_addr.to_string(), + )), + to_npk: None, + to_ipk: None, + amount: 7, + }; - wallet::execute_subcommand(Command::TokenProgram(subcommand)) + wallet::execute_subcommand(Command::Token(subcommand)) .await .unwrap(); @@ -559,13 +576,14 @@ pub fn prepare_function_map() -> HashMap { /// private token transfer to a new account. #[nssa_integration_test] pub async fn test_success_token_program_private_claiming_path() { + info!("########## test_success_token_program_private_claiming_path ##########"); 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::Account(AccountSubcommand::Register( - RegisterSubcommand::Public {}, + } = wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + NewSubcommand::Public {}, ))) .await .unwrap() @@ -574,8 +592,8 @@ pub fn prepare_function_map() -> HashMap { }; // Create new account for the token supply holder (private) let SubcommandReturnValue::RegisterAccount { addr: supply_addr } = - wallet::execute_subcommand(Command::Account(AccountSubcommand::Register( - RegisterSubcommand::Private {}, + wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + NewSubcommand::Private {}, ))) .await .unwrap() @@ -585,8 +603,8 @@ pub fn prepare_function_map() -> HashMap { // Create new account for receiving a token transaction let SubcommandReturnValue::RegisterAccount { addr: recipient_addr, - } = wallet::execute_subcommand(Command::Account(AccountSubcommand::Register( - RegisterSubcommand::Private {}, + } = wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + NewSubcommand::Private {}, ))) .await .unwrap() @@ -595,16 +613,14 @@ pub fn prepare_function_map() -> HashMap { }; // Create new token - let subcommand = TokenProgramSubcommand::Private( - TokenProgramSubcommandPrivate::CreateNewTokenPrivateOwned { - definition_addr: definition_addr.to_string(), - supply_addr: supply_addr.to_string(), - name: "A NAME".to_string(), - total_supply: 37, - }, - ); + let subcommand = TokenProgramAgnosticSubcommand::New { + definition_addr: make_public_account_input_from_str(&definition_addr.to_string()), + supply_addr: make_private_account_input_from_str(&supply_addr.to_string()), + name: "A NAME".to_string(), + total_supply: 37, + }; - wallet::execute_subcommand(Command::TokenProgram(subcommand)) + wallet::execute_subcommand(Command::Token(subcommand)) .await .unwrap(); @@ -647,17 +663,18 @@ pub fn prepare_function_map() -> HashMap { .unwrap(); // Transfer 7 tokens from `supply_acc` to the account at address `recipient_addr` - let subcommand = TokenProgramSubcommand::Private( - TokenProgramSubcommandPrivate::TransferTokenPrivateForeign { - sender_addr: supply_addr.to_string(), - recipient_npk: hex::encode(recipient_keys.nullifer_public_key.0), - recipient_ipk: hex::encode(recipient_keys.incoming_viewing_public_key.0.clone()), - balance_to_move: 7, - }, - ); + let subcommand = TokenProgramAgnosticSubcommand::Send { + from: make_private_account_input_from_str(&supply_addr.to_string()), + to: None, + to_npk: Some(hex::encode(recipient_keys.nullifer_public_key.0)), + to_ipk: Some(hex::encode( + recipient_keys.incoming_viewing_public_key.0.clone(), + )), + amount: 7, + }; - let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = - wallet::execute_subcommand(Command::TokenProgram(subcommand)) + let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash: _ } = + wallet::execute_subcommand(Command::Token(subcommand)) .await .unwrap() else { @@ -667,11 +684,7 @@ pub fn prepare_function_map() -> HashMap { info!("Waiting for next block creation"); tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - let command = Command::Account(AccountSubcommand::Fetch(FetchSubcommand::PrivateAccount { - tx_hash, - acc_addr: recipient_addr.to_string(), - output_id: 1, - })); + let command = Command::Account(AccountSubcommand::SyncPrivate {}); wallet::execute_subcommand(command).await.unwrap(); @@ -695,13 +708,14 @@ pub fn prepare_function_map() -> HashMap { /// shielded token transfer to a new account. All accounts are owned except definition. #[nssa_integration_test] pub async fn test_success_token_program_shielded_owned() { + info!("########## test_success_token_program_shielded_owned ##########"); 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::Account(AccountSubcommand::Register( - RegisterSubcommand::Public {}, + } = wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + NewSubcommand::Public {}, ))) .await .unwrap() @@ -710,8 +724,8 @@ pub fn prepare_function_map() -> HashMap { }; // Create new account for the token supply holder (public) let SubcommandReturnValue::RegisterAccount { addr: supply_addr } = - wallet::execute_subcommand(Command::Account(AccountSubcommand::Register( - RegisterSubcommand::Public {}, + wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + NewSubcommand::Public {}, ))) .await .unwrap() @@ -721,8 +735,8 @@ pub fn prepare_function_map() -> HashMap { // Create new account for receiving a token transaction let SubcommandReturnValue::RegisterAccount { addr: recipient_addr, - } = wallet::execute_subcommand(Command::Account(AccountSubcommand::Register( - RegisterSubcommand::Private {}, + } = wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + NewSubcommand::Private {}, ))) .await .unwrap() @@ -731,15 +745,14 @@ pub fn prepare_function_map() -> HashMap { }; // Create new token - let subcommand = - TokenProgramSubcommand::Public(TokenProgramSubcommandPublic::CreateNewToken { - definition_addr: definition_addr.to_string(), - supply_addr: supply_addr.to_string(), - name: "A NAME".to_string(), - total_supply: 37, - }); + let subcommand = TokenProgramAgnosticSubcommand::New { + definition_addr: make_public_account_input_from_str(&definition_addr.to_string()), + supply_addr: make_public_account_input_from_str(&supply_addr.to_string()), + name: "A NAME".to_string(), + total_supply: 37, + }; - wallet::execute_subcommand(Command::TokenProgram(subcommand)) + wallet::execute_subcommand(Command::Token(subcommand)) .await .unwrap(); @@ -766,15 +779,17 @@ pub fn prepare_function_map() -> HashMap { ); // Transfer 7 tokens from `supply_acc` to the account at address `recipient_addr` - let subcommand = TokenProgramSubcommand::Shielded( - TokenProgramSubcommandShielded::TransferTokenShieldedOwned { - sender_addr: supply_addr.to_string(), - recipient_addr: recipient_addr.to_string(), - balance_to_move: 7, - }, - ); + let subcommand = TokenProgramAgnosticSubcommand::Send { + from: make_public_account_input_from_str(&supply_addr.to_string()), + to: Some(make_private_account_input_from_str( + &recipient_addr.to_string(), + )), + to_npk: None, + to_ipk: None, + amount: 7, + }; - wallet::execute_subcommand(Command::TokenProgram(subcommand)) + wallet::execute_subcommand(Command::Token(subcommand)) .await .unwrap(); @@ -792,15 +807,17 @@ pub fn prepare_function_map() -> HashMap { assert!(verify_commitment_is_in_state(new_commitment2, &seq_client).await); // Transfer additional 7 tokens from `supply_acc` to the account at address `recipient_addr` - let subcommand = TokenProgramSubcommand::Shielded( - TokenProgramSubcommandShielded::TransferTokenShieldedOwned { - sender_addr: supply_addr.to_string(), - recipient_addr: recipient_addr.to_string(), - balance_to_move: 7, - }, - ); + let subcommand = TokenProgramAgnosticSubcommand::Send { + from: make_public_account_input_from_str(&supply_addr.to_string()), + to: Some(make_private_account_input_from_str( + &recipient_addr.to_string(), + )), + to_npk: None, + to_ipk: None, + amount: 7, + }; - wallet::execute_subcommand(Command::TokenProgram(subcommand)) + wallet::execute_subcommand(Command::Token(subcommand)) .await .unwrap(); @@ -822,13 +839,14 @@ pub fn prepare_function_map() -> HashMap { /// deshielded token transfer to a new account. All accounts are owned except definition. #[nssa_integration_test] pub async fn test_success_token_program_deshielded_owned() { + info!("########## test_success_token_program_deshielded_owned ##########"); 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::Account(AccountSubcommand::Register( - RegisterSubcommand::Public {}, + } = wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + NewSubcommand::Public {}, ))) .await .unwrap() @@ -837,8 +855,8 @@ pub fn prepare_function_map() -> HashMap { }; // Create new account for the token supply holder (private) let SubcommandReturnValue::RegisterAccount { addr: supply_addr } = - wallet::execute_subcommand(Command::Account(AccountSubcommand::Register( - RegisterSubcommand::Private {}, + wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + NewSubcommand::Private {}, ))) .await .unwrap() @@ -848,8 +866,8 @@ pub fn prepare_function_map() -> HashMap { // Create new account for receiving a token transaction let SubcommandReturnValue::RegisterAccount { addr: recipient_addr, - } = wallet::execute_subcommand(Command::Account(AccountSubcommand::Register( - RegisterSubcommand::Public {}, + } = wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + NewSubcommand::Public {}, ))) .await .unwrap() @@ -858,16 +876,14 @@ pub fn prepare_function_map() -> HashMap { }; // Create new token - let subcommand = TokenProgramSubcommand::Private( - TokenProgramSubcommandPrivate::CreateNewTokenPrivateOwned { - definition_addr: definition_addr.to_string(), - supply_addr: supply_addr.to_string(), - name: "A NAME".to_string(), - total_supply: 37, - }, - ); + let subcommand = TokenProgramAgnosticSubcommand::New { + definition_addr: make_public_account_input_from_str(&definition_addr.to_string()), + supply_addr: make_private_account_input_from_str(&supply_addr.to_string()), + name: "A NAME".to_string(), + total_supply: 37, + }; - wallet::execute_subcommand(Command::TokenProgram(subcommand)) + wallet::execute_subcommand(Command::Token(subcommand)) .await .unwrap(); @@ -904,15 +920,17 @@ pub fn prepare_function_map() -> HashMap { assert!(verify_commitment_is_in_state(new_commitment1, &seq_client).await); // Transfer 7 tokens from `supply_acc` to the account at address `recipient_addr` - let subcommand = TokenProgramSubcommand::Deshielded( - TokenProgramSubcommandDeshielded::TransferTokenDeshielded { - sender_addr: supply_addr.to_string(), - recipient_addr: recipient_addr.to_string(), - balance_to_move: 7, - }, - ); + let subcommand = TokenProgramAgnosticSubcommand::Send { + from: make_private_account_input_from_str(&supply_addr.to_string()), + to: Some(make_public_account_input_from_str( + &recipient_addr.to_string(), + )), + to_npk: None, + to_ipk: None, + amount: 7, + }; - wallet::execute_subcommand(Command::TokenProgram(subcommand)) + wallet::execute_subcommand(Command::Token(subcommand)) .await .unwrap(); @@ -930,15 +948,17 @@ pub fn prepare_function_map() -> HashMap { assert!(verify_commitment_is_in_state(new_commitment1, &seq_client).await); // Transfer additional 7 tokens from `supply_acc` to the account at address `recipient_addr` - let subcommand = TokenProgramSubcommand::Deshielded( - TokenProgramSubcommandDeshielded::TransferTokenDeshielded { - sender_addr: supply_addr.to_string(), - recipient_addr: recipient_addr.to_string(), - balance_to_move: 7, - }, - ); + let subcommand = TokenProgramAgnosticSubcommand::Send { + from: make_private_account_input_from_str(&supply_addr.to_string()), + to: Some(make_public_account_input_from_str( + &recipient_addr.to_string(), + )), + to_npk: None, + to_ipk: None, + amount: 7, + }; - wallet::execute_subcommand(Command::TokenProgram(subcommand)) + wallet::execute_subcommand(Command::Token(subcommand)) .await .unwrap(); @@ -958,17 +978,17 @@ pub fn prepare_function_map() -> HashMap { #[nssa_integration_test] pub async fn test_success_private_transfer_to_another_owned_account() { - info!("test_success_private_transfer_to_another_owned_account"); + info!("########## 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::Transfer(NativeTokenTransferProgramSubcommand::Private( - NativeTokenTransferProgramSubcommandPrivate::PrivateOwned { - from: from.to_string(), - to: to.to_string(), - amount: 100, - }, - )); + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: make_private_account_input_from_str(&from.to_string()), + to: Some(make_private_account_input_from_str(&to.to_string())), + to_npk: None, + to_ipk: None, + amount: 100, + }); wallet::execute_subcommand(command).await.unwrap(); @@ -994,20 +1014,19 @@ pub fn prepare_function_map() -> HashMap { #[nssa_integration_test] pub async fn test_success_private_transfer_to_another_foreign_account() { - info!("test_success_private_transfer_to_another_foreign_account"); + info!("########## test_success_private_transfer_to_another_foreign_account ##########"); let from: Address = ACC_SENDER_PRIVATE.parse().unwrap(); let to_npk = NullifierPublicKey([42; 32]); let to_npk_string = hex::encode(to_npk.0); let to_ipk = Secp256k1Point::from_scalar(to_npk.0); - 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 command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: make_private_account_input_from_str(&from.to_string()), + to: None, + to_npk: Some(to_npk_string), + to_ipk: Some(hex::encode(to_ipk.0)), + amount: 100, + }); let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = wallet::execute_subcommand(command).await.unwrap() @@ -1041,10 +1060,12 @@ pub fn prepare_function_map() -> HashMap { #[nssa_integration_test] pub async fn test_success_private_transfer_to_another_owned_account_claiming_path() { - info!("test_success_private_transfer_to_another_owned_account_claiming_path"); + info!( + "########## test_success_private_transfer_to_another_owned_account_claiming_path ##########" + ); let from: Address = ACC_SENDER_PRIVATE.parse().unwrap(); - let command = Command::Account(AccountSubcommand::Register(RegisterSubcommand::Private {})); + let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private {})); let sub_ret = wallet::execute_subcommand(command).await.unwrap(); let SubcommandReturnValue::RegisterAccount { addr: to_addr } = sub_ret else { @@ -1065,14 +1086,13 @@ pub fn prepare_function_map() -> HashMap { .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 command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: make_private_account_input_from_str(&from.to_string()), + to: None, + to_npk: Some(hex::encode(to_keys.nullifer_public_key.0)), + to_ipk: Some(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 { @@ -1081,11 +1101,7 @@ pub fn prepare_function_map() -> HashMap { let tx = fetch_privacy_preserving_tx(&seq_client, tx_hash.clone()).await; - let command = Command::Account(AccountSubcommand::Fetch(FetchSubcommand::PrivateAccount { - tx_hash, - acc_addr: to_addr.to_string(), - output_id: 1, - })); + let command = Command::Account(AccountSubcommand::SyncPrivate {}); wallet::execute_subcommand(command).await.unwrap(); let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) .await @@ -1110,12 +1126,14 @@ pub fn prepare_function_map() -> HashMap { #[nssa_integration_test] 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"); + 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 command = Command::Account(AccountSubcommand::New(NewSubcommand::Private {})); let sub_ret = wallet::execute_subcommand(command).await.unwrap(); let SubcommandReturnValue::RegisterAccount { addr: to_addr } = sub_ret else { @@ -1136,14 +1154,13 @@ pub fn prepare_function_map() -> HashMap { .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 command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: make_private_account_input_from_str(&from.to_string()), + to: None, + to_npk: Some(hex::encode(to_keys.nullifer_public_key.0)), + to_ipk: Some(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 { @@ -1181,12 +1198,15 @@ pub fn prepare_function_map() -> HashMap { #[nssa_integration_test] pub async fn test_success_deshielded_transfer_to_another_account() { - info!("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::Transfer(NativeTokenTransferProgramSubcommand::Deshielded { - from: from.to_string(), - to: to.to_string(), + + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: make_private_account_input_from_str(&from.to_string()), + to: Some(make_public_account_input_from_str(&to.to_string())), + to_npk: None, + to_ipk: None, amount: 100, }); @@ -1227,16 +1247,17 @@ pub fn prepare_function_map() -> HashMap { #[nssa_integration_test] pub async fn test_success_shielded_transfer_to_another_owned_account() { - info!("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::Transfer(NativeTokenTransferProgramSubcommand::Shielded( - NativeTokenTransferProgramSubcommandShielded::ShieldedOwned { - from: from.to_string(), - to: to.to_string(), - amount: 100, - }, - )); + + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: make_public_account_input_from_str(&from.to_string()), + to: Some(make_private_account_input_from_str(&to.to_string())), + to_npk: None, + to_ipk: None, + amount: 100, + }); let wallet_config = fetch_config().await.unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); @@ -1268,20 +1289,19 @@ pub fn prepare_function_map() -> HashMap { #[nssa_integration_test] pub async fn test_success_shielded_transfer_to_another_foreign_account() { - info!("test_success_shielded_transfer_to_another_foreign_account"); + info!("########## test_success_shielded_transfer_to_another_foreign_account ##########"); let to_npk = NullifierPublicKey([42; 32]); let to_npk_string = hex::encode(to_npk.0); let to_ipk = Secp256k1Point::from_scalar(to_npk.0); let from: Address = ACC_SENDER.parse().unwrap(); - 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 command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: make_public_account_input_from_str(&from.to_string()), + to: None, + to_npk: Some(to_npk_string), + to_ipk: Some(hex::encode(to_ipk.0)), + amount: 100, + }); let wallet_config = fetch_config().await.unwrap(); @@ -1314,24 +1334,21 @@ pub fn prepare_function_map() -> HashMap { #[nssa_integration_test] pub async fn test_pinata() { - info!("test_pinata"); - let pinata_addr = "cafe".repeat(16); + info!("########## test_pinata ##########"); + let pinata_addr = PINATA_BASE58; let pinata_prize = 150; let solution = 989106; - let command = Command::PinataProgram(PinataProgramSubcommand::Public( - PinataProgramSubcommandPublic::Claim { - pinata_addr: pinata_addr.clone(), - winner_addr: ACC_SENDER.to_string(), - solution, - }, - )); + let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim { + to_addr: make_public_account_input_from_str(ACC_SENDER), + solution, + }); let wallet_config = fetch_config().await.unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); let pinata_balance_pre = seq_client - .get_account_balance(pinata_addr.clone()) + .get_account_balance(pinata_addr.to_string()) .await .unwrap() .balance; @@ -1343,7 +1360,7 @@ pub fn prepare_function_map() -> HashMap { info!("Checking correct balance move"); let pinata_balance_post = seq_client - .get_account_balance(pinata_addr.clone()) + .get_account_balance(pinata_addr.to_string()) .await .unwrap() .balance; @@ -1362,7 +1379,7 @@ pub fn prepare_function_map() -> HashMap { #[nssa_integration_test] pub async fn test_program_deployment() { - info!("test program deployment"); + info!("########## test program deployment ##########"); let bytecode = NSSA_PROGRAM_FOR_TEST_DATA_CHANGER.to_vec(); let message = nssa::program_deployment_transaction::Message::new(bytecode.clone()); let transaction = ProgramDeploymentTransaction::new(message); @@ -1379,7 +1396,7 @@ pub fn prepare_function_map() -> HashMap { // We pass an uninitialized account and we expect after execution to be owned by the data // changer program (NSSA account claiming mechanism) with data equal to [0] (due to program logic) let data_changer = Program::new(bytecode).unwrap(); - let address: Address = "deadbeef".repeat(8).parse().unwrap(); + let address: Address = "11".repeat(16).parse().unwrap(); let message = nssa::public_transaction::Message::try_new( data_changer.id(), vec![address], @@ -1409,15 +1426,19 @@ pub fn prepare_function_map() -> HashMap { #[nssa_integration_test] pub async fn test_authenticated_transfer_initialize_function() { - info!("test initialize account for authenticated transfer"); - let command = Command::AuthenticatedTransferInitializePublicAccount {}; - + info!("########## test initialize account for authenticated transfer ##########"); + let command = Command::Account(AccountSubcommand::New(NewSubcommand::Public {})); let SubcommandReturnValue::RegisterAccount { addr } = wallet::execute_subcommand(command).await.unwrap() else { panic!("Error creating account"); }; + let command = Command::AuthTransfer(AuthTransferSubcommand::Init { + addr: make_public_account_input_from_str(&addr.to_string()), + }); + wallet::execute_subcommand(command).await.unwrap(); + info!("Checking correct execution"); let wallet_config = fetch_config().await.unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); @@ -1441,30 +1462,27 @@ pub fn prepare_function_map() -> HashMap { #[nssa_integration_test] pub async fn test_pinata_private_receiver() { - info!("test_pinata_private_receiver"); - let pinata_addr = "cafe".repeat(16); + info!("########## test_pinata_private_receiver ##########"); + let pinata_addr = PINATA_BASE58; let pinata_prize = 150; let solution = 989106; - let command = Command::PinataProgram(PinataProgramSubcommand::Private( - PinataProgramSubcommandPrivate::ClaimPrivateOwned { - pinata_addr: pinata_addr.clone(), - winner_addr: ACC_SENDER_PRIVATE.to_string(), - solution, - }, - )); + let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim { + to_addr: make_private_account_input_from_str(ACC_SENDER_PRIVATE), + solution, + }); let wallet_config = fetch_config().await.unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); let pinata_balance_pre = seq_client - .get_account_balance(pinata_addr.clone()) + .get_account_balance(pinata_addr.to_string()) .await .unwrap() .balance; - let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = + let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash: _ } = wallet::execute_subcommand(command).await.unwrap() else { panic!("invalid subcommand return value"); @@ -1475,16 +1493,12 @@ pub fn prepare_function_map() -> HashMap { info!("Checking correct balance move"); let pinata_balance_post = seq_client - .get_account_balance(pinata_addr.clone()) + .get_account_balance(pinata_addr.to_string()) .await .unwrap() .balance; - let command = Command::Account(AccountSubcommand::Fetch(FetchSubcommand::PrivateAccount { - tx_hash: tx_hash.clone(), - acc_addr: ACC_SENDER_PRIVATE.to_string(), - output_id: 0, - })); + let command = Command::Account(AccountSubcommand::SyncPrivate {}); wallet::execute_subcommand(command).await.unwrap(); let wallet_config = fetch_config().await.unwrap(); @@ -1505,15 +1519,15 @@ pub fn prepare_function_map() -> HashMap { #[nssa_integration_test] pub async fn test_pinata_private_receiver_new_account() { - info!("test_pinata_private_receiver"); - let pinata_addr = "cafe".repeat(16); + info!("########## test_pinata_private_receiver ##########"); + let pinata_addr = PINATA_BASE58; let pinata_prize = 150; let solution = 989106; // Create new account for the token supply holder (private) let SubcommandReturnValue::RegisterAccount { addr: winner_addr } = - wallet::execute_subcommand(Command::Account(AccountSubcommand::Register( - RegisterSubcommand::Private {}, + wallet::execute_subcommand(Command::Account(AccountSubcommand::New( + NewSubcommand::Private {}, ))) .await .unwrap() @@ -1521,20 +1535,17 @@ pub fn prepare_function_map() -> HashMap { panic!("invalid subcommand return value"); }; - let command = Command::PinataProgram(PinataProgramSubcommand::Private( - PinataProgramSubcommandPrivate::ClaimPrivateOwned { - pinata_addr: pinata_addr.clone(), - winner_addr: winner_addr.to_string(), - solution, - }, - )); + let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim { + to_addr: make_private_account_input_from_str(&winner_addr.to_string()), + solution, + }); let wallet_config = fetch_config().await.unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); let pinata_balance_pre = seq_client - .get_account_balance(pinata_addr.clone()) + .get_account_balance(pinata_addr.to_string()) .await .unwrap() .balance; @@ -1546,7 +1557,7 @@ pub fn prepare_function_map() -> HashMap { info!("Checking correct balance move"); let pinata_balance_post = seq_client - .get_account_balance(pinata_addr.clone()) + .get_account_balance(pinata_addr.to_string()) .await .unwrap() .balance; diff --git a/key_protocol/Cargo.toml b/key_protocol/Cargo.toml index 544a2f8..b0708b4 100644 --- a/key_protocol/Cargo.toml +++ b/key_protocol/Cargo.toml @@ -9,7 +9,8 @@ serde.workspace = true k256.workspace = true sha2.workspace = true rand.workspace = true -hex.workspace = true +base58.workspace = true +hex = "0.4.3" aes-gcm.workspace = true bip39.workspace = true hmac-sha512.workspace = true diff --git a/key_protocol/src/key_management/mod.rs b/key_protocol/src/key_management/mod.rs index 5650fd5..f22a99f 100644 --- a/key_protocol/src/key_management/mod.rs +++ b/key_protocol/src/key_management/mod.rs @@ -55,6 +55,7 @@ impl KeyChain { #[cfg(test)] mod tests { use aes_gcm::aead::OsRng; + use base58::ToBase58; use k256::AffinePoint; use k256::elliptic_curve::group::GroupEncoding; use rand::RngCore; @@ -119,7 +120,7 @@ mod tests { println!("======Public data======"); println!(); - println!("Address{:?}", hex::encode(address.value())); + println!("Address{:?}", address.value().to_base58()); println!( "Nulifier public key {:?}", hex::encode(nullifer_public_key.to_byte_array()) diff --git a/key_protocol/src/key_protocol_core/mod.rs b/key_protocol/src/key_protocol_core/mod.rs index df5502e..b1ebe71 100644 --- a/key_protocol/src/key_protocol_core/mod.rs +++ b/key_protocol/src/key_protocol_core/mod.rs @@ -142,5 +142,10 @@ mod tests { let is_key_chain_generated = user_data.get_private_account(&addr_private).is_some(); assert!(is_key_chain_generated); + + let addr_private_str = addr_private.to_string(); + println!("{addr_private_str:#?}"); + let key_chain = &user_data.get_private_account(&addr_private).unwrap().0; + println!("{key_chain:#?}"); } } diff --git a/nssa/core/Cargo.toml b/nssa/core/Cargo.toml index e1951c4..5712eaf 100644 --- a/nssa/core/Cargo.toml +++ b/nssa/core/Cargo.toml @@ -10,8 +10,9 @@ thiserror = { version = "2.0.12", optional = true } bytemuck = { version = "1.13", optional = true } chacha20 = { version = "0.9", default-features = false } k256 = { version = "0.13.3", optional = true } -hex = { version = "0.4.3", optional = true } +base58 = { version = "0.2.0", optional = true } +anyhow = { version = "1.0.98", optional = true } [features] default = [] -host = ["thiserror", "bytemuck", "k256", "hex"] +host = ["thiserror", "bytemuck", "k256", "base58", "anyhow"] diff --git a/nssa/core/src/address.rs b/nssa/core/src/address.rs index 2627368..6355351 100644 --- a/nssa/core/src/address.rs +++ b/nssa/core/src/address.rs @@ -3,6 +3,9 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "host")] use std::{fmt::Display, str::FromStr}; +#[cfg(feature = "host")] +use base58::{FromBase58, ToBase58}; + #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash)] #[cfg_attr( any(feature = "host", test), @@ -31,8 +34,8 @@ impl AsRef<[u8]> for Address { #[cfg(feature = "host")] #[derive(Debug, thiserror::Error)] pub enum AddressError { - #[error("invalid hex")] - InvalidHex(#[from] hex::FromHexError), + #[error("invalid base58")] + InvalidBase58(#[from] anyhow::Error), #[error("invalid length: expected 32 bytes, got {0}")] InvalidLength(usize), } @@ -41,7 +44,9 @@ pub enum AddressError { impl FromStr for Address { type Err = AddressError; fn from_str(s: &str) -> Result { - let bytes = hex::decode(s)?; + let bytes = s + .from_base58() + .map_err(|err| anyhow::anyhow!("Invalid base58 err {err:?}"))?; if bytes.len() != 32 { return Err(AddressError::InvalidLength(bytes.len())); } @@ -54,7 +59,7 @@ impl FromStr for Address { #[cfg(feature = "host")] impl Display for Address { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", hex::encode(self.value)) + write!(f, "{}", self.value.to_base58()) } } @@ -65,29 +70,29 @@ mod tests { #[test] fn parse_valid_address() { - let hex_str = "00".repeat(32); // 64 hex chars = 32 bytes - let addr: Address = hex_str.parse().unwrap(); + let base58_str = "11111111111111111111111111111111"; + let addr: Address = base58_str.parse().unwrap(); assert_eq!(addr.value, [0u8; 32]); } #[test] - fn parse_invalid_hex() { - let hex_str = "zz".repeat(32); // invalid hex chars - let result = hex_str.parse::
().unwrap_err(); - assert!(matches!(result, AddressError::InvalidHex(_))); + fn parse_invalid_base58() { + let base58_str = "00".repeat(32); // invalid base58 chars + let result = base58_str.parse::
().unwrap_err(); + assert!(matches!(result, AddressError::InvalidBase58(_))); } #[test] fn parse_wrong_length_short() { - let hex_str = "00".repeat(31); // 62 chars = 31 bytes - let result = hex_str.parse::
().unwrap_err(); + let base58_str = "11".repeat(31); // 62 chars = 31 bytes + let result = base58_str.parse::
().unwrap_err(); assert!(matches!(result, AddressError::InvalidLength(_))); } #[test] fn parse_wrong_length_long() { - let hex_str = "00".repeat(33); // 66 chars = 33 bytes - let result = hex_str.parse::
().unwrap_err(); + let base58_str = "11".repeat(33); // 66 chars = 33 bytes + let result = base58_str.parse::
().unwrap_err(); assert!(matches!(result, AddressError::InvalidLength(_))); } } diff --git a/nssa/core/src/program.rs b/nssa/core/src/program.rs index 82023f3..3ecee30 100644 --- a/nssa/core/src/program.rs +++ b/nssa/core/src/program.rs @@ -12,11 +12,20 @@ pub struct ProgramInput { pub instruction: T, } +#[derive(Serialize, Deserialize, Clone)] +#[cfg_attr(any(feature = "host", test), derive(Debug, PartialEq, Eq))] +pub struct ChainedCall { + pub program_id: ProgramId, + pub instruction_data: InstructionData, + pub account_indices: Vec, +} + #[derive(Serialize, Deserialize, Clone)] #[cfg_attr(any(feature = "host", test), derive(Debug, PartialEq, Eq))] pub struct ProgramOutput { pub pre_states: Vec, pub post_states: Vec, + pub chained_call: Option, } pub fn read_nssa_inputs() -> ProgramInput { @@ -33,6 +42,20 @@ pub fn write_nssa_outputs(pre_states: Vec, post_states: Vec let output = ProgramOutput { pre_states, post_states, + chained_call: None, + }; + env::commit(&output); +} + +pub fn write_nssa_outputs_with_chained_call( + pre_states: Vec, + post_states: Vec, + chained_call: Option, +) { + let output = ProgramOutput { + pre_states, + post_states, + chained_call, }; env::commit(&output); } @@ -79,9 +102,14 @@ pub fn validate_execution( { return false; } + + // 6. If a post state has default program owner, the pre state must have been a default account + if post.program_owner == DEFAULT_PROGRAM_ID && pre.account != Account::default() { + return false; + } } - // 6. Total balance is preserved + // 7. Total balance is preserved let total_balance_pre_states: u128 = pre_states.iter().map(|pre| pre.account.balance).sum(); let total_balance_post_states: u128 = post_states.iter().map(|post| post.balance).sum(); if total_balance_pre_states != total_balance_post_states { diff --git a/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs b/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs index a1aa8c9..d8ed15d 100644 --- a/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs +++ b/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs @@ -27,8 +27,14 @@ fn main() { let ProgramOutput { pre_states, post_states, + chained_call, } = program_output; + // TODO: implement chained calls for privacy preserving transactions + if chained_call.is_some() { + panic!("Privacy preserving transactions do not support yet chained calls.") + } + // Check that there are no repeated account ids if !validate_uniqueness_of_account_ids(&pre_states) { panic!("Repeated account ids found") diff --git a/nssa/src/program.rs b/nssa/src/program.rs index 7771aaf..11eb413 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -1,6 +1,6 @@ use crate::program_methods::{AUTHENTICATED_TRANSFER_ELF, PINATA_ELF, TOKEN_ELF}; use nssa_core::{ - account::{Account, AccountWithMetadata}, + account::AccountWithMetadata, program::{InstructionData, ProgramId, ProgramOutput}, }; @@ -48,7 +48,7 @@ impl Program { &self, pre_states: &[AccountWithMetadata], instruction_data: &InstructionData, - ) -> Result, NssaError> { + ) -> Result { // Write inputs to the program let mut env_builder = ExecutorEnv::builder(); env_builder.session_limit(Some(MAX_NUM_CYCLES_PUBLIC_EXECUTION)); @@ -62,12 +62,12 @@ impl Program { .map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?; // Get outputs - let ProgramOutput { post_states, .. } = session_info + let program_output = session_info .journal .decode() .map_err(|e| NssaError::ProgramExecutionFailed(e.to_string()))?; - Ok(post_states) + Ok(program_output) } /// Writes inputs to `env_builder` in the order expected by the programs @@ -107,11 +107,11 @@ impl Program { #[cfg(test)] mod tests { - use nssa_core::account::{Account, AccountId, AccountWithMetadata}; - use program_methods::{ + use crate::program_methods::{ AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID, PINATA_ELF, PINATA_ID, TOKEN_ELF, TOKEN_ID, }; + use nssa_core::account::{Account, AccountId, AccountWithMetadata}; use crate::program::Program; @@ -195,6 +195,15 @@ mod tests { elf: BURNER_ELF.to_vec(), } } + + pub fn chain_caller() -> Self { + use test_program_methods::{CHAIN_CALLER_ELF, CHAIN_CALLER_ID}; + + Program { + id: CHAIN_CALLER_ID, + elf: CHAIN_CALLER_ELF.to_vec(), + } + } } #[test] @@ -221,12 +230,12 @@ mod tests { balance: balance_to_move, ..Account::default() }; - let [sender_post, recipient_post] = program + let program_output = program .execute(&[sender, recipient], &instruction_data) - .unwrap() - .try_into() .unwrap(); + let [sender_post, recipient_post] = program_output.post_states.try_into().unwrap(); + assert_eq!(sender_post, expected_sender_post); assert_eq!(recipient_post, expected_recipient_post); } diff --git a/nssa/src/public_transaction/transaction.rs b/nssa/src/public_transaction/transaction.rs index b0b8f73..d118d0c 100644 --- a/nssa/src/public_transaction/transaction.rs +++ b/nssa/src/public_transaction/transaction.rs @@ -3,7 +3,7 @@ use std::collections::{HashMap, HashSet}; use nssa_core::{ account::{Account, AccountWithMetadata}, address::Address, - program::validate_execution, + program::{DEFAULT_PROGRAM_ID, validate_execution}, }; use sha2::{Digest, digest::FixedOutput}; @@ -18,6 +18,7 @@ pub struct PublicTransaction { message: Message, witness_set: WitnessSet, } +const MAX_NUMBER_CHAINED_CALLS: usize = 10; impl PublicTransaction { pub fn new(message: Message, witness_set: WitnessSet) -> Self { @@ -88,7 +89,7 @@ impl PublicTransaction { } // Build pre_states for execution - let pre_states: Vec<_> = message + let mut input_pre_states: Vec<_> = message .addresses .iter() .map(|address| { @@ -100,21 +101,86 @@ impl PublicTransaction { }) .collect(); - // Check the `program_id` corresponds to a deployed program - let Some(program) = state.programs().get(&message.program_id) else { - return Err(NssaError::InvalidInput("Unknown program".into())); - }; + let mut state_diff: HashMap = HashMap::new(); - // // Execute program - let post_states = program.execute(&pre_states, &message.instruction_data)?; + let mut program_id = message.program_id; + let mut instruction_data = message.instruction_data.clone(); - // Verify execution corresponds to a well-behaved program. - // See the # Programs section for the definition of the `validate_execution` method. - if !validate_execution(&pre_states, &post_states, message.program_id) { - return Err(NssaError::InvalidProgramBehavior); + for _i in 0..MAX_NUMBER_CHAINED_CALLS { + // Check the `program_id` corresponds to a deployed program + let Some(program) = state.programs().get(&program_id) else { + return Err(NssaError::InvalidInput("Unknown program".into())); + }; + + let mut program_output = program.execute(&input_pre_states, &instruction_data)?; + + // This check is equivalent to checking that the program output pre_states coinicide + // with the values in the public state or with any modifications to those values + // during the chain of calls. + if input_pre_states != program_output.pre_states { + return Err(NssaError::InvalidProgramBehavior); + } + + // Verify execution corresponds to a well-behaved program. + // See the # Programs section for the definition of the `validate_execution` method. + if !validate_execution( + &program_output.pre_states, + &program_output.post_states, + program_id, + ) { + return Err(NssaError::InvalidProgramBehavior); + } + + // The invoked program claims the accounts with default program id. + for post in program_output.post_states.iter_mut() { + if post.program_owner == DEFAULT_PROGRAM_ID { + post.program_owner = program_id; + } + } + + // Update the state diff + for (pre, post) in program_output + .pre_states + .iter() + .zip(program_output.post_states.iter()) + { + state_diff.insert(pre.account_id, post.clone()); + } + + if let Some(next_chained_call) = program_output.chained_call { + program_id = next_chained_call.program_id; + instruction_data = next_chained_call.instruction_data; + + // Build post states with metadata for next call + let mut post_states_with_metadata = Vec::new(); + for (pre, post) in program_output + .pre_states + .iter() + .zip(program_output.post_states) + { + let mut post_with_metadata = pre.clone(); + post_with_metadata.account = post.clone(); + post_states_with_metadata.push(post_with_metadata); + } + + input_pre_states = next_chained_call + .account_indices + .iter() + .map(|&i| { + post_states_with_metadata + .get(i) + .ok_or_else(|| { + NssaError::InvalidInput("Invalid account indices".into()) + }) + .cloned() + }) + .collect::, NssaError>>()?; + } else { + break; + }; } - Ok(message.addresses.iter().cloned().zip(post_states).collect()) + Ok(state_diff) } } diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 83183f5..4120824 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -6,9 +6,7 @@ use crate::{ }; use nssa_core::{ Commitment, CommitmentSetDigest, DUMMY_COMMITMENT, MembershipProof, Nullifier, - account::Account, - address::Address, - program::{DEFAULT_PROGRAM_ID, ProgramId}, + account::Account, address::Address, program::ProgramId, }; use std::collections::{HashMap, HashSet}; @@ -114,10 +112,6 @@ impl V02State { let current_account = self.get_account_by_address_mut(address); *current_account = post; - // The invoked program claims the accounts with default program id. - if current_account.program_owner == DEFAULT_PROGRAM_ID { - current_account.program_owner = tx.message().program_id; - } } for address in tx.signer_addresses() { @@ -263,6 +257,7 @@ pub mod tests { Commitment, Nullifier, NullifierPublicKey, NullifierSecretKey, SharedSecretKey, account::{Account, AccountId, AccountWithMetadata, Nonce}, encryption::{EphemeralPublicKey, IncomingViewingPublicKey, Scalar}, + program::ProgramId, }; fn transfer_transaction( @@ -436,7 +431,7 @@ pub mod tests { } #[test] - fn transition_from_chained_authenticated_transfer_program_invocations() { + fn transition_from_sequence_of_authenticated_transfer_program_invocations() { let key1 = PrivateKey::try_new([8; 32]).unwrap(); let address1 = Address::from(&PublicKey::new_from_private_key(&key1)); let key2 = PrivateKey::try_new([2; 32]).unwrap(); @@ -475,6 +470,7 @@ pub mod tests { self.insert_program(Program::data_changer()); self.insert_program(Program::minter()); self.insert_program(Program::burner()); + self.insert_program(Program::chain_caller()); self } @@ -2045,4 +2041,80 @@ pub mod tests { assert!(matches!(result, Err(NssaError::CircuitProvingError(_)))); } + + #[test] + fn test_claiming_mechanism() { + let program = Program::authenticated_transfer_program(); + let key = PrivateKey::try_new([1; 32]).unwrap(); + let address = Address::from(&PublicKey::new_from_private_key(&key)); + let initial_balance = 100; + let initial_data = [(address, initial_balance)]; + let mut state = + V02State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs(); + let from = address; + let from_key = key; + let to = Address::new([2; 32]); + let amount: u128 = 37; + + // Check the recipient is an uninitialized account + assert_eq!(state.get_account_by_address(&to), Account::default()); + + let expected_recipient_post = Account { + program_owner: program.id(), + balance: amount, + ..Account::default() + }; + + let message = + public_transaction::Message::try_new(program.id(), vec![from, to], vec![0], amount) + .unwrap(); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]); + let tx = PublicTransaction::new(message, witness_set); + + state.transition_from_public_transaction(&tx).unwrap(); + + let recipient_post = state.get_account_by_address(&to); + + assert_eq!(recipient_post, expected_recipient_post); + } + + #[test] + fn test_chained_call() { + let program = Program::chain_caller(); + let key = PrivateKey::try_new([1; 32]).unwrap(); + let address = Address::from(&PublicKey::new_from_private_key(&key)); + let initial_balance = 100; + let initial_data = [(address, initial_balance)]; + let mut state = + V02State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs(); + let from = address; + let from_key = key; + let to = Address::new([2; 32]); + let amount: u128 = 37; + let instruction: (u128, ProgramId) = + (amount, Program::authenticated_transfer_program().id()); + + let expected_to_post = Account { + program_owner: Program::chain_caller().id(), + balance: amount, + ..Account::default() + }; + + let message = public_transaction::Message::try_new( + program.id(), + vec![to, from], //The chain_caller program permutes the account order in the chain call + vec![0], + instruction, + ) + .unwrap(); + let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]); + let tx = PublicTransaction::new(message, witness_set); + + state.transition_from_public_transaction(&tx).unwrap(); + + let from_post = state.get_account_by_address(&from); + let to_post = state.get_account_by_address(&to); + assert_eq!(from_post.balance, initial_balance - amount); + assert_eq!(to_post, expected_to_post); + } } diff --git a/nssa/test_program_methods/guest/Cargo.lock b/nssa/test_program_methods/guest/Cargo.lock index 8cb2bec..d7e5b67 100644 --- a/nssa/test_program_methods/guest/Cargo.lock +++ b/nssa/test_program_methods/guest/Cargo.lock @@ -1824,6 +1824,8 @@ name = "programs" version = "0.1.0" dependencies = [ "nssa-core", + "risc0-zkvm", + "serde", ] [[package]] diff --git a/nssa/test_program_methods/guest/Cargo.toml b/nssa/test_program_methods/guest/Cargo.toml index 2289292..9e5f543 100644 --- a/nssa/test_program_methods/guest/Cargo.toml +++ b/nssa/test_program_methods/guest/Cargo.toml @@ -6,4 +6,6 @@ edition = "2024" [workspace] [dependencies] +risc0-zkvm = { version = "3.0.3", features = ['std'] } nssa-core = { path = "../../core" } +serde = { version = "1.0.219", default-features = false } diff --git a/nssa/test_program_methods/guest/src/bin/chain_caller.rs b/nssa/test_program_methods/guest/src/bin/chain_caller.rs new file mode 100644 index 0000000..dfd77b1 --- /dev/null +++ b/nssa/test_program_methods/guest/src/bin/chain_caller.rs @@ -0,0 +1,34 @@ +use nssa_core::program::{ + ChainedCall, ProgramId, ProgramInput, read_nssa_inputs, write_nssa_outputs_with_chained_call, +}; +use risc0_zkvm::serde::to_vec; + +type Instruction = (u128, ProgramId); + +/// A program that calls another program. +/// It permutes the order of the input accounts on the subsequent call +fn main() { + let ProgramInput { + pre_states, + instruction: (balance, program_id), + } = read_nssa_inputs::(); + + let [sender_pre, receiver_pre] = match pre_states.try_into() { + Ok(array) => array, + Err(_) => return, + }; + + let instruction_data = to_vec(&balance).unwrap(); + + let chained_call = Some(ChainedCall { + program_id, + instruction_data, + account_indices: vec![1, 0], // <- Account order permutation here + }); + + write_nssa_outputs_with_chained_call( + vec![sender_pre.clone(), receiver_pre.clone()], + vec![sender_pre.account, receiver_pre.account], + chained_call, + ); +} diff --git a/sequencer_core/Cargo.toml b/sequencer_core/Cargo.toml index 72a8cc4..6e9979c 100644 --- a/sequencer_core/Cargo.toml +++ b/sequencer_core/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] -hex.workspace = true +base58.workspace = true anyhow.workspace = true serde.workspace = true rand.workspace = true diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index af2f6d4..018e71c 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -1,6 +1,8 @@ use std::fmt::Display; use anyhow::Result; +#[cfg(feature = "testnet")] +use common::PINATA_BASE58; use common::{ HashType, block::HashableBlockData, @@ -82,7 +84,7 @@ impl SequencerCore { let mut state = nssa::V02State::new_with_genesis_accounts(&init_accs, &initial_commitments); #[cfg(feature = "testnet")] - state.add_pinata_program("cafe".repeat(16).parse().unwrap()); + state.add_pinata_program(PINATA_BASE58.parse().unwrap()); let mut this = Self { state, @@ -239,6 +241,7 @@ impl SequencerCore { #[cfg(test)] mod tests { + use base58::{FromBase58, ToBase58}; use common::test_utils::sequencer_sign_key_for_testing; use nssa::PrivateKey; @@ -273,23 +276,23 @@ mod tests { } fn setup_sequencer_config() -> SequencerConfig { - let acc1_addr = vec![ + let acc1_addr: Vec = vec![ 208, 122, 210, 232, 75, 39, 250, 0, 194, 98, 240, 161, 238, 160, 255, 53, 202, 9, 115, 84, 126, 106, 16, 111, 114, 241, 147, 194, 220, 131, 139, 68, ]; - let acc2_addr = vec![ + let acc2_addr: Vec = vec![ 231, 174, 119, 197, 239, 26, 5, 153, 147, 68, 175, 73, 159, 199, 138, 23, 5, 57, 141, 98, 237, 6, 207, 46, 20, 121, 246, 222, 248, 154, 57, 188, ]; let initial_acc1 = AccountInitialData { - addr: hex::encode(acc1_addr), + addr: acc1_addr.to_base58(), balance: 10000, }; let initial_acc2 = AccountInitialData { - addr: hex::encode(acc2_addr), + addr: acc2_addr.to_base58(), balance: 20000, }; @@ -324,11 +327,17 @@ mod tests { assert_eq!(sequencer.sequencer_config.max_num_tx_in_block, 10); assert_eq!(sequencer.sequencer_config.port, 8080); - let acc1_addr = hex::decode(config.initial_accounts[0].addr.clone()) + let acc1_addr = config.initial_accounts[0] + .addr + .clone() + .from_base58() .unwrap() .try_into() .unwrap(); - let acc2_addr = hex::decode(config.initial_accounts[1].addr.clone()) + let acc2_addr = config.initial_accounts[1] + .addr + .clone() + .from_base58() .unwrap() .try_into() .unwrap(); @@ -348,23 +357,23 @@ mod tests { #[test] fn test_start_different_intial_accounts_balances() { - let acc1_addr = vec![ + let acc1_addr: Vec = vec![ 27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143, ]; - let acc2_addr = vec![ + let acc2_addr: Vec = vec![ 77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234, 216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102, ]; let initial_acc1 = AccountInitialData { - addr: hex::encode(acc1_addr), + addr: acc1_addr.to_base58(), balance: 10000, }; let initial_acc2 = AccountInitialData { - addr: hex::encode(acc2_addr), + addr: acc2_addr.to_base58(), balance: 20000, }; @@ -373,11 +382,17 @@ mod tests { let config = setup_sequencer_config_variable_initial_accounts(initial_accounts); let sequencer = SequencerCore::start_from_config(config.clone()); - let acc1_addr = hex::decode(config.initial_accounts[0].addr.clone()) + let acc1_addr = config.initial_accounts[0] + .addr + .clone() + .from_base58() .unwrap() .try_into() .unwrap(); - let acc2_addr = hex::decode(config.initial_accounts[1].addr.clone()) + let acc2_addr = config.initial_accounts[1] + .addr + .clone() + .from_base58() .unwrap() .try_into() .unwrap(); @@ -418,11 +433,17 @@ mod tests { common_setup(&mut sequencer); - let acc1 = hex::decode(sequencer.sequencer_config.initial_accounts[0].addr.clone()) + let acc1 = sequencer.sequencer_config.initial_accounts[0] + .addr + .clone() + .from_base58() .unwrap() .try_into() .unwrap(); - let acc2 = hex::decode(sequencer.sequencer_config.initial_accounts[1].addr.clone()) + let acc2 = sequencer.sequencer_config.initial_accounts[1] + .addr + .clone() + .from_base58() .unwrap() .try_into() .unwrap(); @@ -444,11 +465,17 @@ mod tests { common_setup(&mut sequencer); - let acc1 = hex::decode(sequencer.sequencer_config.initial_accounts[0].addr.clone()) + let acc1 = sequencer.sequencer_config.initial_accounts[0] + .addr + .clone() + .from_base58() .unwrap() .try_into() .unwrap(); - let acc2 = hex::decode(sequencer.sequencer_config.initial_accounts[1].addr.clone()) + let acc2 = sequencer.sequencer_config.initial_accounts[1] + .addr + .clone() + .from_base58() .unwrap() .try_into() .unwrap(); @@ -480,11 +507,17 @@ mod tests { common_setup(&mut sequencer); - let acc1 = hex::decode(sequencer.sequencer_config.initial_accounts[0].addr.clone()) + let acc1 = sequencer.sequencer_config.initial_accounts[0] + .addr + .clone() + .from_base58() .unwrap() .try_into() .unwrap(); - let acc2 = hex::decode(sequencer.sequencer_config.initial_accounts[1].addr.clone()) + let acc2 = sequencer.sequencer_config.initial_accounts[1] + .addr + .clone() + .from_base58() .unwrap() .try_into() .unwrap(); @@ -516,11 +549,17 @@ mod tests { common_setup(&mut sequencer); - let acc1 = hex::decode(sequencer.sequencer_config.initial_accounts[0].addr.clone()) + let acc1 = sequencer.sequencer_config.initial_accounts[0] + .addr + .clone() + .from_base58() .unwrap() .try_into() .unwrap(); - let acc2 = hex::decode(sequencer.sequencer_config.initial_accounts[1].addr.clone()) + let acc2 = sequencer.sequencer_config.initial_accounts[1] + .addr + .clone() + .from_base58() .unwrap() .try_into() .unwrap(); @@ -606,11 +645,17 @@ mod tests { common_setup(&mut sequencer); - let acc1 = hex::decode(sequencer.sequencer_config.initial_accounts[0].addr.clone()) + let acc1 = sequencer.sequencer_config.initial_accounts[0] + .addr + .clone() + .from_base58() .unwrap() .try_into() .unwrap(); - let acc2 = hex::decode(sequencer.sequencer_config.initial_accounts[1].addr.clone()) + let acc2 = sequencer.sequencer_config.initial_accounts[1] + .addr + .clone() + .from_base58() .unwrap() .try_into() .unwrap(); @@ -647,11 +692,17 @@ mod tests { common_setup(&mut sequencer); - let acc1 = hex::decode(sequencer.sequencer_config.initial_accounts[0].addr.clone()) + let acc1 = sequencer.sequencer_config.initial_accounts[0] + .addr + .clone() + .from_base58() .unwrap() .try_into() .unwrap(); - let acc2 = hex::decode(sequencer.sequencer_config.initial_accounts[1].addr.clone()) + let acc2 = sequencer.sequencer_config.initial_accounts[1] + .addr + .clone() + .from_base58() .unwrap() .try_into() .unwrap(); @@ -688,14 +739,8 @@ mod tests { #[test] fn test_restart_from_storage() { let config = setup_sequencer_config(); - let acc1_addr = hex::decode(config.initial_accounts[0].addr.clone()) - .unwrap() - .try_into() - .unwrap(); - let acc2_addr = hex::decode(config.initial_accounts[1].addr.clone()) - .unwrap() - .try_into() - .unwrap(); + let acc1_addr: nssa::Address = config.initial_accounts[0].addr.parse().unwrap(); + let acc2_addr: nssa::Address = config.initial_accounts[1].addr.parse().unwrap(); let balance_to_move = 13; // In the following code block a transaction will be processed that moves `balance_to_move` @@ -706,9 +751,9 @@ mod tests { let signing_key = PrivateKey::try_new([1; 32]).unwrap(); let tx = common::test_utils::create_transaction_native_token_transfer( - acc1_addr, + *acc1_addr.value(), 0, - acc2_addr, + *acc2_addr.value(), balance_to_move, signing_key, ); @@ -727,14 +772,8 @@ mod tests { // Instantiating a new sequencer from the same config. This should load the existing block // with the above transaction and update the state to reflect that. let sequencer = SequencerCore::start_from_config(config.clone()); - let balance_acc_1 = sequencer - .state - .get_account_by_address(&nssa::Address::new(acc1_addr)) - .balance; - let balance_acc_2 = sequencer - .state - .get_account_by_address(&nssa::Address::new(acc2_addr)) - .balance; + let balance_acc_1 = sequencer.state.get_account_by_address(&acc1_addr).balance; + let balance_acc_2 = sequencer.state.get_account_by_address(&acc2_addr).balance; // Balances should be consistent with the stored block assert_eq!( diff --git a/sequencer_core/src/sequencer_store/mod.rs b/sequencer_core/src/sequencer_store/mod.rs new file mode 100644 index 0000000..dd99639 --- /dev/null +++ b/sequencer_core/src/sequencer_store/mod.rs @@ -0,0 +1,74 @@ +use std::path::Path; + +use block_store::SequecerBlockStore; +use common::block::HashableBlockData; +use nssa::{self, Address}; +use rand::{RngCore, rngs::OsRng}; + +use crate::config::AccountInitialData; + +pub mod block_store; + +pub struct SequecerChainStore { + pub state: nssa::V02State, + pub block_store: SequecerBlockStore, +} + +impl SequecerChainStore { + pub fn new_with_genesis( + home_dir: &Path, + genesis_id: u64, + is_genesis_random: bool, + initial_accounts: &[AccountInitialData], + initial_commitments: &[nssa_core::Commitment], + signing_key: nssa::PrivateKey, + ) -> Self { + let init_accs: Vec<(Address, u128)> = initial_accounts + .iter() + .map(|acc_data| (acc_data.addr.parse().unwrap(), acc_data.balance)) + .collect(); + + #[cfg(not(feature = "testnet"))] + let state = nssa::V02State::new_with_genesis_accounts(&init_accs, initial_commitments); + + #[cfg(feature = "testnet")] + let state = { + use common::PINATA_BASE58; + + let mut this = + nssa::V02State::new_with_genesis_accounts(&init_accs, initial_commitments); + this.add_pinata_program(PINATA_BASE58.parse().unwrap()); + this + }; + + let mut data = [0; 32]; + let mut prev_block_hash = [0; 32]; + + if is_genesis_random { + OsRng.fill_bytes(&mut data); + OsRng.fill_bytes(&mut prev_block_hash); + } + + let curr_time = chrono::Utc::now().timestamp_millis() as u64; + + let hashable_data = HashableBlockData { + block_id: genesis_id, + transactions: vec![], + prev_block_hash, + timestamp: curr_time, + }; + + let genesis_block = hashable_data.into_block(&signing_key); + + //Sequencer should panic if unable to open db, + //as fixing this issue may require actions non-native to program scope + let block_store = SequecerBlockStore::open_db_with_genesis( + &home_dir.join("rocksdb"), + Some(genesis_block), + signing_key, + ) + .unwrap(); + + Self { state, block_store } + } +} diff --git a/sequencer_rpc/Cargo.toml b/sequencer_rpc/Cargo.toml index af7e011..557ce6a 100644 --- a/sequencer_rpc/Cargo.toml +++ b/sequencer_rpc/Cargo.toml @@ -10,7 +10,8 @@ log.workspace = true serde.workspace = true actix-cors.workspace = true futures.workspace = true -hex.workspace = true +base58.workspace = true +hex = "0.4.3" tempfile.workspace = true base64.workspace = true diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index ae6ee21..878dd50 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use actix_web::Error as HttpError; +use base58::FromBase58; use base64::{Engine, engine::general_purpose}; use nssa::{self, program::Program}; use sequencer_core::config::AccountInitialData; @@ -160,8 +161,10 @@ impl JsonHandler { /// The address must be a valid hex string of the correct length. async fn process_get_account_balance(&self, request: Request) -> Result { let get_account_req = GetAccountBalanceRequest::parse(Some(request.params))?; - let address_bytes = hex::decode(get_account_req.address) - .map_err(|_| RpcError::invalid_params("invalid hex".to_string()))?; + let address_bytes = get_account_req + .address + .from_base58() + .map_err(|_| RpcError::invalid_params("invalid base58".to_string()))?; let address = nssa::Address::new( address_bytes .try_into() @@ -307,6 +310,7 @@ mod tests { use std::sync::Arc; use crate::{JsonHandler, rpc_handler}; + use base58::ToBase58; use base64::{Engine, engine::general_purpose}; use common::{ rpc_primitives::RpcPollingConfig, test_utils::sequencer_sign_key_for_testing, @@ -324,23 +328,23 @@ mod tests { fn sequencer_config_for_tests() -> SequencerConfig { let tempdir = tempdir().unwrap(); let home = tempdir.path().to_path_buf(); - let acc1_addr = vec![ + let acc1_addr: Vec = vec![ 208, 122, 210, 232, 75, 39, 250, 0, 194, 98, 240, 161, 238, 160, 255, 53, 202, 9, 115, 84, 126, 106, 16, 111, 114, 241, 147, 194, 220, 131, 139, 68, ]; - let acc2_addr = vec![ + let acc2_addr: Vec = vec![ 231, 174, 119, 197, 239, 26, 5, 153, 147, 68, 175, 73, 159, 199, 138, 23, 5, 57, 141, 98, 237, 6, 207, 46, 20, 121, 246, 222, 248, 154, 57, 188, ]; let initial_acc1 = AccountInitialData { - addr: hex::encode(acc1_addr), + addr: acc1_addr.to_base58(), balance: 10000, }; let initial_acc2 = AccountInitialData { - addr: hex::encode(acc2_addr), + addr: acc2_addr.to_base58(), balance: 20000, }; @@ -425,7 +429,7 @@ mod tests { let request = serde_json::json!({ "jsonrpc": "2.0", "method": "get_account_balance", - "params": { "address": "efac".repeat(16) }, + "params": { "address": "11".repeat(16) }, "id": 1 }); let expected_response = serde_json::json!({ @@ -442,12 +446,12 @@ mod tests { } #[actix_web::test] - async fn test_get_account_balance_for_invalid_hex() { + async fn test_get_account_balance_for_invalid_base58() { let (json_handler, _, _) = components_for_tests(); let request = serde_json::json!({ "jsonrpc": "2.0", "method": "get_account_balance", - "params": { "address": "not_a_valid_hex" }, + "params": { "address": "not_a_valid_base58" }, "id": 1 }); let expected_response = serde_json::json!({ @@ -456,7 +460,7 @@ mod tests { "error": { "code": -32602, "message": "Invalid params", - "data": "invalid hex" + "data": "invalid base58" } }); let response = call_rpc_handler_with_json(json_handler, request).await; @@ -518,7 +522,7 @@ mod tests { let request = serde_json::json!({ "jsonrpc": "2.0", "method": "get_accounts_nonces", - "params": { "addresses": ["efac".repeat(16)] }, + "params": { "addresses": ["11".repeat(16)] }, "id": 1 }); let expected_response = serde_json::json!({ @@ -566,7 +570,7 @@ mod tests { let request = serde_json::json!({ "jsonrpc": "2.0", "method": "get_account", - "params": { "address": "efac".repeat(16) }, + "params": { "address": "11".repeat(16) }, "id": 1 }); let expected_response = serde_json::json!({ diff --git a/sequencer_runner/configs/debug/sequencer_config.json b/sequencer_runner/configs/debug/sequencer_config.json index 19ff458..acd0caa 100644 --- a/sequencer_runner/configs/debug/sequencer_config.json +++ b/sequencer_runner/configs/debug/sequencer_config.json @@ -8,49 +8,49 @@ "port": 3040, "initial_accounts": [ { - "addr": "d07ad2e84b27fa00c262f0a1eea0ff35ca0973547e6a106f72f193c2dc838b44", + "addr": "BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy", "balance": 10000 }, { - "addr": "e7ae77c5ef1a05999344af499fc78a1705398d62ed06cf2e1479f6def89a39bc", + "addr": "Gj1mJy5W7J5pfmLRujmQaLfLMWidNxQ6uwnhb666ZwHw", "balance": 20000 } ], "initial_commitments": [ { "npk": [ - 193, - 209, - 150, - 113, - 47, - 241, - 48, - 145, - 250, - 79, - 235, - 51, - 119, - 40, - 184, - 232, - 5, + 63, + 202, + 178, + 231, + 183, + 82, + 237, + 212, + 216, 221, - 36, - 21, - 201, - 106, - 90, - 210, - 129, - 106, - 71, - 99, - 208, + 215, + 255, 153, - 75, - 215 + 101, + 177, + 161, + 254, + 210, + 128, + 122, + 54, + 190, + 230, + 151, + 183, + 64, + 225, + 229, + 113, + 1, + 228, + 97 ], "account": { "program_owner": [ @@ -70,38 +70,38 @@ }, { "npk": [ - 27, - 250, + 192, + 251, + 166, + 243, + 167, + 236, + 84, + 249, + 35, 136, - 142, - 88, - 128, - 138, - 21, - 49, - 183, - 118, - 160, - 117, - 114, - 110, - 47, - 136, - 87, - 60, - 70, - 59, - 60, - 18, - 223, - 23, - 147, - 241, - 5, - 184, - 103, + 130, + 172, + 219, 225, - 105 + 161, + 139, + 229, + 89, + 243, + 125, + 194, + 213, + 209, + 30, + 23, + 174, + 100, + 244, + 124, + 74, + 140, + 47 ], "account": { "program_owner": [ @@ -154,4 +154,4 @@ 37, 37 ] -} +} \ No newline at end of file diff --git a/wallet/Cargo.toml b/wallet/Cargo.toml index 48d79e2..b04d67e 100644 --- a/wallet/Cargo.toml +++ b/wallet/Cargo.toml @@ -16,7 +16,8 @@ nssa-core = { path = "../nssa/core" } base64.workspace = true bytemuck = "1.23.2" borsh.workspace = true -hex.workspace = true +base58.workspace = true +hex = "0.4.3" rand.workspace = true [dependencies.key_protocol] diff --git a/wallet/src/chain_storage/mod.rs b/wallet/src/chain_storage/mod.rs index e07ba8e..8fc8805 100644 --- a/wallet/src/chain_storage/mod.rs +++ b/wallet/src/chain_storage/mod.rs @@ -75,19 +75,91 @@ mod tests { use tempfile::tempdir; fn create_initial_accounts() -> Vec { - let initial_acc1 = serde_json::from_str(r#"{ + let initial_acc1 = serde_json::from_str( + r#"{ "Public": { - "address": "d07ad2e84b27fa00c262f0a1eea0ff35ca0973547e6a106f72f193c2dc838b44", - "pub_sign_key": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + "address": "BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy", + "pub_sign_key": [ + 16, + 162, + 106, + 154, + 236, + 125, + 52, + 184, + 35, + 100, + 238, + 174, + 69, + 197, + 41, + 77, + 187, + 10, + 118, + 75, + 0, + 11, + 148, + 238, + 185, + 181, + 133, + 17, + 220, + 72, + 124, + 77 + ] } - }"#).unwrap(); + }"#, + ) + .unwrap(); - let initial_acc2 = serde_json::from_str(r#"{ + let initial_acc2 = serde_json::from_str( + r#"{ "Public": { - "address": "e7ae77c5ef1a05999344af499fc78a1705398d62ed06cf2e1479f6def89a39bc", - "pub_sign_key": [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] + "address": "Gj1mJy5W7J5pfmLRujmQaLfLMWidNxQ6uwnhb666ZwHw", + "pub_sign_key": [ + 113, + 121, + 64, + 177, + 204, + 85, + 229, + 214, + 178, + 6, + 109, + 191, + 29, + 154, + 63, + 38, + 242, + 18, + 244, + 219, + 8, + 208, + 35, + 136, + 23, + 127, + 207, + 237, + 216, + 169, + 190, + 27 + ] } - }"#).unwrap(); + }"#, + ) + .unwrap(); let initial_accounts = vec![initial_acc1, initial_acc2]; diff --git a/wallet/src/cli/account.rs b/wallet/src/cli/account.rs index 9ec4b20..3ff4470 100644 --- a/wallet/src/cli/account.rs +++ b/wallet/src/cli/account.rs @@ -1,213 +1,117 @@ -use std::str::FromStr; - use anyhow::Result; +use base58::ToBase58; use clap::Subcommand; -use common::transaction::NSSATransaction; -use nssa::Address; +use nssa::{Account, Address, program::Program}; +use serde::Serialize; use crate::{ - SubcommandReturnValue, WalletCore, cli::WalletSubcommand, helperfunctions::HumanReadableAccount, + SubcommandReturnValue, WalletCore, + cli::WalletSubcommand, + helperfunctions::{AddressPrivacyKind, HumanReadableAccount, parse_addr_with_privacy_prefix}, + parse_block_range, }; +const TOKEN_DEFINITION_TYPE: u8 = 0; +const TOKEN_DEFINITION_DATA_SIZE: usize = 23; + +const TOKEN_HOLDING_TYPE: u8 = 1; +const TOKEN_HOLDING_DATA_SIZE: usize = 49; + +struct TokenDefinition { + #[allow(unused)] + account_type: u8, + name: [u8; 6], + total_supply: u128, +} + +struct TokenHolding { + #[allow(unused)] + account_type: u8, + definition_id: Address, + balance: u128, +} + +impl TokenDefinition { + fn parse(data: &[u8]) -> Option { + if data.len() != TOKEN_DEFINITION_DATA_SIZE || data[0] != TOKEN_DEFINITION_TYPE { + None + } else { + let account_type = data[0]; + let name = data[1..7].try_into().unwrap(); + let total_supply = u128::from_le_bytes(data[7..].try_into().unwrap()); + + Some(Self { + account_type, + name, + total_supply, + }) + } + } +} + +impl TokenHolding { + fn parse(data: &[u8]) -> Option { + if data.len() != TOKEN_HOLDING_DATA_SIZE || data[0] != TOKEN_HOLDING_TYPE { + None + } else { + let account_type = data[0]; + let definition_id = Address::new(data[1..33].try_into().unwrap()); + let balance = u128::from_le_bytes(data[33..].try_into().unwrap()); + Some(Self { + definition_id, + balance, + account_type, + }) + } + } +} + ///Represents generic chain CLI subcommand #[derive(Subcommand, Debug, Clone)] pub enum AccountSubcommand { - ///Get + ///Get account data + Get { + ///Flag to get raw account data + #[arg(short, long)] + raw: bool, + ///Valid 32 byte base58 string with privacy prefix + #[arg(short, long)] + addr: String, + }, + ///Produce new public or private account #[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, - }, + New(NewSubcommand), + ///Sync private accounts + SyncPrivate {}, } ///Represents generic register CLI subcommand #[derive(Subcommand, Debug, Clone)] -pub enum RegisterSubcommand { +pub enum NewSubcommand { ///Register new public account Public {}, ///Register new private account Private {}, } -impl WalletSubcommand for GetSubcommand { +impl WalletSubcommand for NewSubcommand { async fn handle_subcommand( self, wallet_core: &mut WalletCore, ) -> Result { 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 { - 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 { - match self { - RegisterSubcommand::Public {} => { + NewSubcommand::Public {} => { let addr = wallet_core.create_new_account_public(); - println!("Generated new account with addr {addr}"); + println!("Generated new account with addr Public/{addr}"); - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); Ok(SubcommandReturnValue::RegisterAccount { addr }) } - RegisterSubcommand::Private {} => { + NewSubcommand::Private {} => { let addr = wallet_core.create_new_account_private(); let (key, _) = wallet_core @@ -216,14 +120,17 @@ impl WalletSubcommand for RegisterSubcommand { .get_private_account(&addr) .unwrap(); - println!("Generated new account with addr {addr}"); - println!("With npk {}", hex::encode(&key.nullifer_public_key)); + println!( + "Generated new account with addr Private/{}", + addr.to_bytes().to_base58() + ); + println!("With npk {}", hex::encode(key.nullifer_public_key.0)); println!( "With ipk {}", hex::encode(key.incoming_viewing_public_key.to_bytes()) ); - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); @@ -233,20 +140,155 @@ impl WalletSubcommand for RegisterSubcommand { } } +#[derive(Debug, Serialize)] +pub struct AuthenticatedTransferAccountView { + pub balance: u128, +} + +impl From for AuthenticatedTransferAccountView { + fn from(value: nssa::Account) -> Self { + Self { + balance: value.balance, + } + } +} + +#[derive(Debug, Serialize)] +pub struct TokedDefinitionAccountView { + pub account_type: String, + pub name: String, + pub total_supply: u128, +} + +impl From for TokedDefinitionAccountView { + fn from(value: TokenDefinition) -> Self { + Self { + account_type: "Token definition".to_string(), + name: hex::encode(value.name), + total_supply: value.total_supply, + } + } +} + +#[derive(Debug, Serialize)] +pub struct TokedHoldingAccountView { + pub account_type: String, + pub definition_id: String, + pub balance: u128, +} + +impl From for TokedHoldingAccountView { + fn from(value: TokenHolding) -> Self { + Self { + account_type: "Token holding".to_string(), + definition_id: value.definition_id.to_string(), + balance: value.balance, + } + } +} + impl WalletSubcommand for AccountSubcommand { async fn handle_subcommand( self, wallet_core: &mut WalletCore, ) -> Result { match self { - AccountSubcommand::Get(get_subcommand) => { - get_subcommand.handle_subcommand(wallet_core).await + AccountSubcommand::Get { raw, addr } => { + let (addr, addr_kind) = parse_addr_with_privacy_prefix(&addr)?; + + let addr = addr.parse()?; + + let account = match addr_kind { + AddressPrivacyKind::Public => wallet_core.get_account_public(addr).await?, + AddressPrivacyKind::Private => wallet_core + .get_account_private(&addr) + .ok_or(anyhow::anyhow!("Private account not found in storage"))?, + }; + + if account == Account::default() { + println!("Account is Uninitialized"); + + return Ok(SubcommandReturnValue::Empty); + } + + if raw { + let account_hr: HumanReadableAccount = account.clone().into(); + println!("{}", serde_json::to_string(&account_hr).unwrap()); + + return Ok(SubcommandReturnValue::Empty); + } + + let auth_tr_prog_id = Program::authenticated_transfer_program().id(); + let token_prog_id = Program::token().id(); + + let acc_view = match &account.program_owner { + _ if account.program_owner == auth_tr_prog_id => { + let acc_view: AuthenticatedTransferAccountView = account.into(); + + println!("Account owned by authenticated transfer program"); + + serde_json::to_string(&acc_view)? + } + _ if account.program_owner == token_prog_id => { + if let Some(token_def) = TokenDefinition::parse(&account.data) { + let acc_view: TokedDefinitionAccountView = token_def.into(); + + println!("Definition account owned by token program"); + + serde_json::to_string(&acc_view)? + } else if let Some(token_hold) = TokenHolding::parse(&account.data) { + let acc_view: TokedHoldingAccountView = token_hold.into(); + + println!("Holding account owned by token program"); + + serde_json::to_string(&acc_view)? + } else { + anyhow::bail!("Invalid data for account {addr:#?} with token program"); + } + } + _ => { + let account_hr: HumanReadableAccount = account.clone().into(); + serde_json::to_string(&account_hr).unwrap() + } + }; + + println!("{}", acc_view); + + Ok(SubcommandReturnValue::Empty) } - AccountSubcommand::Fetch(fetch_subcommand) => { - fetch_subcommand.handle_subcommand(wallet_core).await + AccountSubcommand::New(new_subcommand) => { + new_subcommand.handle_subcommand(wallet_core).await } - AccountSubcommand::Register(register_subcommand) => { - register_subcommand.handle_subcommand(wallet_core).await + AccountSubcommand::SyncPrivate {} => { + let last_synced_block = wallet_core.last_synced_block; + let curr_last_block = wallet_core + .sequencer_client + .get_last_block() + .await? + .last_block; + + if !wallet_core + .storage + .user_data + .user_private_accounts + .is_empty() + { + parse_block_range( + last_synced_block + 1, + curr_last_block, + wallet_core.sequencer_client.clone(), + wallet_core, + ) + .await?; + } else { + wallet_core.last_synced_block = curr_last_block; + + let path = wallet_core.store_persistent_data().await?; + + println!("Stored persistent data at {path:#?}"); + } + + Ok(SubcommandReturnValue::SyncedToBlock(curr_last_block)) } } } diff --git a/wallet/src/cli/chain.rs b/wallet/src/cli/chain.rs index 4db18fc..a6e7999 100644 --- a/wallet/src/cli/chain.rs +++ b/wallet/src/cli/chain.rs @@ -6,12 +6,16 @@ use crate::{SubcommandReturnValue, WalletCore, cli::WalletSubcommand}; ///Represents generic chain CLI subcommand #[derive(Subcommand, Debug, Clone)] pub enum ChainSubcommand { - GetLatestBlockId {}, - GetBlockAtId { + ///Get current block id from sequencer + CurrentBlockId {}, + ///Get block at id from sequencer + Block { #[arg(short, long)] id: u64, }, - GetTransactionAtHash { + ///Get transaction at hash from sequencer + Transaction { + ///hash - valid 32 byte hex string #[arg(short, long)] hash: String, }, @@ -23,17 +27,17 @@ impl WalletSubcommand for ChainSubcommand { wallet_core: &mut WalletCore, ) -> Result { match self { - ChainSubcommand::GetLatestBlockId {} => { + ChainSubcommand::CurrentBlockId {} => { 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 } => { + ChainSubcommand::Block { id } => { let block_res = wallet_core.sequencer_client.get_block(id).await?; println!("Last block id is {:#?}", block_res.block); } - ChainSubcommand::GetTransactionAtHash { hash } => { + ChainSubcommand::Transaction { hash } => { let tx_res = wallet_core .sequencer_client .get_transaction_by_hash(hash) diff --git a/wallet/src/cli/native_token_transfer_program.rs b/wallet/src/cli/native_token_transfer_program.rs index b568931..e286bb9 100644 --- a/wallet/src/cli/native_token_transfer_program.rs +++ b/wallet/src/cli/native_token_transfer_program.rs @@ -3,7 +3,193 @@ use clap::Subcommand; use common::transaction::NSSATransaction; use nssa::Address; -use crate::{SubcommandReturnValue, WalletCore, cli::WalletSubcommand}; +use crate::{ + SubcommandReturnValue, WalletCore, + cli::WalletSubcommand, + helperfunctions::{AddressPrivacyKind, parse_addr_with_privacy_prefix}, +}; + +///Represents generic CLI subcommand for a wallet working with native token transfer program +#[derive(Subcommand, Debug, Clone)] +pub enum AuthTransferSubcommand { + ///Initialize account under authenticated transfer program + Init { + ///addr - valid 32 byte base58 string with privacy prefix + #[arg(long)] + addr: String, + }, + ///Send native tokens from one account to another with variable privacy + /// + ///If receiver is private, then `to` and (`to_npk` , `to_ipk`) is a mutually exclusive patterns. + /// + ///First is used for owned accounts, second otherwise. + Send { + ///from - valid 32 byte base58 string with privacy prefix + #[arg(long)] + from: String, + ///to - valid 32 byte base58 string with privacy prefix + #[arg(long)] + to: Option, + ///to_npk - valid 32 byte hex string + #[arg(long)] + to_npk: Option, + ///to_ipk - valid 33 byte hex string + #[arg(long)] + to_ipk: Option, + ///amount - amount of balance to move + #[arg(long)] + amount: u128, + }, +} + +impl WalletSubcommand for AuthTransferSubcommand { + async fn handle_subcommand( + self, + wallet_core: &mut WalletCore, + ) -> Result { + match self { + AuthTransferSubcommand::Init { addr } => { + let (addr, addr_privacy) = parse_addr_with_privacy_prefix(&addr)?; + + match addr_privacy { + AddressPrivacyKind::Public => { + let addr = addr.parse()?; + + let res = wallet_core + .register_account_under_authenticated_transfers_programs(addr) + .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_data().await?; + + println!("Stored persistent accounts at {path:#?}"); + } + AddressPrivacyKind::Private => { + let addr = addr.parse()?; + + let (res, [secret]) = wallet_core + .register_account_under_authenticated_transfers_programs_private(addr) + .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, addr)]; + + wallet_core.decode_insert_privacy_preserving_transaction_results( + tx, + &acc_decode_data, + )?; + } + + let path = wallet_core.store_persistent_data().await?; + + println!("Stored persistent accounts at {path:#?}"); + } + } + + Ok(SubcommandReturnValue::Empty) + } + AuthTransferSubcommand::Send { + from, + to, + to_npk, + to_ipk, + amount, + } => { + let underlying_subcommand = match (to, to_npk, to_ipk) { + (None, None, None) => { + anyhow::bail!( + "Provide either account address of receiver or their public keys" + ); + } + (Some(_), Some(_), Some(_)) => { + anyhow::bail!( + "Provide only one variant: either account address of receiver or their public keys" + ); + } + (_, Some(_), None) | (_, None, Some(_)) => { + anyhow::bail!("List of public keys is uncomplete"); + } + (Some(to), None, None) => { + let (from, from_privacy) = parse_addr_with_privacy_prefix(&from)?; + let (to, to_privacy) = parse_addr_with_privacy_prefix(&to)?; + + match (from_privacy, to_privacy) { + (AddressPrivacyKind::Public, AddressPrivacyKind::Public) => { + NativeTokenTransferProgramSubcommand::Public { from, to, amount } + } + (AddressPrivacyKind::Private, AddressPrivacyKind::Private) => { + NativeTokenTransferProgramSubcommand::Private( + NativeTokenTransferProgramSubcommandPrivate::PrivateOwned { + from, + to, + amount, + }, + ) + } + (AddressPrivacyKind::Private, AddressPrivacyKind::Public) => { + NativeTokenTransferProgramSubcommand::Deshielded { + from, + to, + amount, + } + } + (AddressPrivacyKind::Public, AddressPrivacyKind::Private) => { + NativeTokenTransferProgramSubcommand::Shielded( + NativeTokenTransferProgramSubcommandShielded::ShieldedOwned { + from, + to, + amount, + }, + ) + } + } + } + (None, Some(to_npk), Some(to_ipk)) => { + let (from, from_privacy) = parse_addr_with_privacy_prefix(&from)?; + + match from_privacy { + AddressPrivacyKind::Private => { + NativeTokenTransferProgramSubcommand::Private( + NativeTokenTransferProgramSubcommandPrivate::PrivateForeign { + from, + to_npk, + to_ipk, + amount, + }, + ) + } + AddressPrivacyKind::Public => { + NativeTokenTransferProgramSubcommand::Shielded( + NativeTokenTransferProgramSubcommandShielded::ShieldedForeign { + from, + to_npk, + to_ipk, + amount, + }, + ) + } + } + } + }; + + underlying_subcommand.handle_subcommand(wallet_core).await + } + } + } +} ///Represents generic CLI subcommand for a wallet working with native token transfer program #[derive(Subcommand, Debug, Clone)] @@ -158,7 +344,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate { )?; } - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); @@ -202,7 +388,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate { )?; } - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); @@ -252,7 +438,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded { )?; } - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); @@ -285,7 +471,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded { let tx_hash = res.tx_hash; - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); @@ -331,7 +517,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand { )?; } - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); @@ -351,7 +537,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand { println!("Transaction data is {transfer_tx:?}"); - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); diff --git a/wallet/src/cli/pinata_program.rs b/wallet/src/cli/pinata_program.rs index 75d3d6a..fafd5f1 100644 --- a/wallet/src/cli/pinata_program.rs +++ b/wallet/src/cli/pinata_program.rs @@ -1,9 +1,59 @@ use anyhow::Result; use clap::Subcommand; -use common::transaction::NSSATransaction; +use common::{PINATA_BASE58, transaction::NSSATransaction}; use log::info; -use crate::{SubcommandReturnValue, WalletCore, cli::WalletSubcommand}; +use crate::{ + SubcommandReturnValue, WalletCore, + cli::WalletSubcommand, + helperfunctions::{AddressPrivacyKind, parse_addr_with_privacy_prefix}, +}; + +///Represents generic CLI subcommand for a wallet working with pinata program +#[derive(Subcommand, Debug, Clone)] +pub enum PinataProgramAgnosticSubcommand { + ///Claim pinata + Claim { + ///to_addr - valid 32 byte base58 string with privacy prefix + #[arg(long)] + to_addr: String, + ///solution - solution to pinata challenge + #[arg(long)] + solution: u128, + }, +} + +impl WalletSubcommand for PinataProgramAgnosticSubcommand { + async fn handle_subcommand( + self, + wallet_core: &mut WalletCore, + ) -> Result { + let underlying_subcommand = match self { + PinataProgramAgnosticSubcommand::Claim { to_addr, solution } => { + let (to_addr, to_addr_privacy) = parse_addr_with_privacy_prefix(&to_addr)?; + + match to_addr_privacy { + AddressPrivacyKind::Public => { + PinataProgramSubcommand::Public(PinataProgramSubcommandPublic::Claim { + pinata_addr: PINATA_BASE58.to_string(), + winner_addr: to_addr, + solution, + }) + } + AddressPrivacyKind::Private => PinataProgramSubcommand::Private( + PinataProgramSubcommandPrivate::ClaimPrivateOwned { + pinata_addr: PINATA_BASE58.to_string(), + winner_addr: to_addr, + solution, + }, + ), + } + } + }; + + underlying_subcommand.handle_subcommand(wallet_core).await + } +} ///Represents generic CLI subcommand for a wallet working with pinata program #[derive(Subcommand, Debug, Clone)] @@ -131,7 +181,7 @@ impl WalletSubcommand for PinataProgramSubcommandPrivate { )?; } - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); diff --git a/wallet/src/cli/token_program.rs b/wallet/src/cli/token_program.rs index 25de77d..6ce7dbe 100644 --- a/wallet/src/cli/token_program.rs +++ b/wallet/src/cli/token_program.rs @@ -3,7 +3,197 @@ use clap::Subcommand; use common::transaction::NSSATransaction; use nssa::Address; -use crate::{SubcommandReturnValue, WalletCore, cli::WalletSubcommand}; +use crate::{ + SubcommandReturnValue, WalletCore, + cli::WalletSubcommand, + helperfunctions::{AddressPrivacyKind, parse_addr_with_privacy_prefix}, +}; + +///Represents generic CLI subcommand for a wallet working with token program +#[derive(Subcommand, Debug, Clone)] +pub enum TokenProgramAgnosticSubcommand { + ///Produce a new token + /// + ///Currently the only supported privacy options is for public definition + New { + ///definition_addr - valid 32 byte base58 string with privacy prefix + #[arg(long)] + definition_addr: String, + ///supply_addr - valid 32 byte base58 string with privacy prefix + #[arg(long)] + supply_addr: String, + #[arg(short, long)] + name: String, + #[arg(short, long)] + total_supply: u128, + }, + ///Send tokens from one account to another with variable privacy + /// + ///If receiver is private, then `to` and (`to_npk` , `to_ipk`) is a mutually exclusive patterns. + /// + ///First is used for owned accounts, second otherwise. + Send { + ///from - valid 32 byte base58 string with privacy prefix + #[arg(long)] + from: String, + ///to - valid 32 byte base58 string with privacy prefix + #[arg(long)] + to: Option, + ///to_npk - valid 32 byte hex string + #[arg(long)] + to_npk: Option, + ///to_ipk - valid 33 byte hex string + #[arg(long)] + to_ipk: Option, + ///amount - amount of balance to move + #[arg(long)] + amount: u128, + }, +} + +impl WalletSubcommand for TokenProgramAgnosticSubcommand { + async fn handle_subcommand( + self, + wallet_core: &mut WalletCore, + ) -> Result { + match self { + TokenProgramAgnosticSubcommand::New { + definition_addr, + supply_addr, + name, + total_supply, + } => { + let (definition_addr, definition_addr_privacy) = + parse_addr_with_privacy_prefix(&definition_addr)?; + let (supply_addr, supply_addr_privacy) = + parse_addr_with_privacy_prefix(&supply_addr)?; + + let underlying_subcommand = match (definition_addr_privacy, supply_addr_privacy) { + (AddressPrivacyKind::Public, AddressPrivacyKind::Public) => { + TokenProgramSubcommand::Public( + TokenProgramSubcommandPublic::CreateNewToken { + definition_addr, + supply_addr, + name, + total_supply, + }, + ) + } + (AddressPrivacyKind::Public, AddressPrivacyKind::Private) => { + TokenProgramSubcommand::Private( + TokenProgramSubcommandPrivate::CreateNewTokenPrivateOwned { + definition_addr, + supply_addr, + name, + total_supply, + }, + ) + } + (AddressPrivacyKind::Private, AddressPrivacyKind::Private) => { + //ToDo: maybe implement this one. It is not immediately clear why definition should be private. + anyhow::bail!("Unavailable privacy pairing") + } + (AddressPrivacyKind::Private, AddressPrivacyKind::Public) => { + //ToDo: Probably valid. If definition is not public, but supply is it is very suspicious. + anyhow::bail!("Unavailable privacy pairing") + } + }; + + underlying_subcommand.handle_subcommand(wallet_core).await + } + TokenProgramAgnosticSubcommand::Send { + from, + to, + to_npk, + to_ipk, + amount, + } => { + let underlying_subcommand = match (to, to_npk, to_ipk) { + (None, None, None) => { + anyhow::bail!( + "Provide either account address of receiver or their public keys" + ); + } + (Some(_), Some(_), Some(_)) => { + anyhow::bail!( + "Provide only one variant: either account address of receiver or their public keys" + ); + } + (_, Some(_), None) | (_, None, Some(_)) => { + anyhow::bail!("List of public keys is uncomplete"); + } + (Some(to), None, None) => { + let (from, from_privacy) = parse_addr_with_privacy_prefix(&from)?; + let (to, to_privacy) = parse_addr_with_privacy_prefix(&to)?; + + match (from_privacy, to_privacy) { + (AddressPrivacyKind::Public, AddressPrivacyKind::Public) => { + TokenProgramSubcommand::Public( + TokenProgramSubcommandPublic::TransferToken { + sender_addr: from, + recipient_addr: to, + balance_to_move: amount, + }, + ) + } + (AddressPrivacyKind::Private, AddressPrivacyKind::Private) => { + TokenProgramSubcommand::Private( + TokenProgramSubcommandPrivate::TransferTokenPrivateOwned { + sender_addr: from, + recipient_addr: to, + balance_to_move: amount, + }, + ) + } + (AddressPrivacyKind::Private, AddressPrivacyKind::Public) => { + TokenProgramSubcommand::Deshielded( + TokenProgramSubcommandDeshielded::TransferTokenDeshielded { + sender_addr: from, + recipient_addr: to, + balance_to_move: amount, + }, + ) + } + (AddressPrivacyKind::Public, AddressPrivacyKind::Private) => { + TokenProgramSubcommand::Shielded( + TokenProgramSubcommandShielded::TransferTokenShieldedOwned { + sender_addr: from, + recipient_addr: to, + balance_to_move: amount, + }, + ) + } + } + } + (None, Some(to_npk), Some(to_ipk)) => { + let (from, from_privacy) = parse_addr_with_privacy_prefix(&from)?; + + match from_privacy { + AddressPrivacyKind::Private => TokenProgramSubcommand::Private( + TokenProgramSubcommandPrivate::TransferTokenPrivateForeign { + sender_addr: from, + recipient_npk: to_npk, + recipient_ipk: to_ipk, + balance_to_move: amount, + }, + ), + AddressPrivacyKind::Public => TokenProgramSubcommand::Shielded( + TokenProgramSubcommandShielded::TransferTokenShieldedForeign { + sender_addr: from, + recipient_npk: to_npk, + recipient_ipk: to_ipk, + balance_to_move: amount, + }, + ), + } + } + }; + + underlying_subcommand.handle_subcommand(wallet_core).await + } + } + } +} ///Represents generic CLI subcommand for a wallet working with token_program #[derive(Subcommand, Debug, Clone)] @@ -221,7 +411,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate { )?; } - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); @@ -278,7 +468,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate { )?; } - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); @@ -328,7 +518,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate { )?; } - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); @@ -376,7 +566,7 @@ impl WalletSubcommand for TokenProgramSubcommandDeshielded { )?; } - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); @@ -431,7 +621,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded { println!("Transaction data is {:?}", tx.message); } - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); @@ -485,7 +675,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded { )?; } - let path = wallet_core.store_persistent_accounts().await?; + let path = wallet_core.store_persistent_data().await?; println!("Stored persistent accounts at {path:#?}"); diff --git a/wallet/src/config.rs b/wallet/src/config.rs index aa9b5ba..af1f493 100644 --- a/wallet/src/config.rs +++ b/wallet/src/config.rs @@ -46,6 +46,12 @@ pub enum PersistentAccountData { Private(PersistentAccountDataPrivate), } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PersistentStorage { + pub accounts: Vec, + pub last_synced_block: u64, +} + impl InitialAccountData { pub fn address(&self) -> nssa::Address { match &self { diff --git a/wallet/src/helperfunctions.rs b/wallet/src/helperfunctions.rs index a67b8ec..1d35efb 100644 --- a/wallet/src/helperfunctions.rs +++ b/wallet/src/helperfunctions.rs @@ -12,8 +12,7 @@ use serde::Serialize; use crate::{ HOME_DIR_ENV_VAR, config::{ - PersistentAccountData, PersistentAccountDataPrivate, PersistentAccountDataPublic, - WalletConfig, + PersistentAccountDataPrivate, PersistentAccountDataPublic, PersistentStorage, WalletConfig, }, }; @@ -30,21 +29,24 @@ pub async fn fetch_config() -> Result { Ok(serde_json::from_slice(&config_contents)?) } -/// Fetch list of accounts stored at `NSSA_WALLET_HOME_DIR/curr_accounts.json` +/// Fetch data stored at `NSSA_WALLET_HOME_DIR/storage.json` /// /// If file not present, it is considered as empty list of persistent accounts -pub async fn fetch_persistent_accounts() -> Result> { +pub async fn fetch_persistent_storage() -> Result { let home = get_home()?; - let accs_path = home.join("curr_accounts.json"); - let mut persistent_accounts_content = vec![]; + let accs_path = home.join("storage.json"); + let mut storage_content = vec![]; 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)?) + file.read_to_end(&mut storage_content).await?; + Ok(serde_json::from_slice(&storage_content)?) } Err(err) => match err.kind() { - std::io::ErrorKind::NotFound => Ok(vec![]), + std::io::ErrorKind::NotFound => Ok(PersistentStorage { + accounts: vec![], + last_synced_block: 0, + }), _ => { anyhow::bail!("IO error {err:#?}"); } @@ -52,8 +54,11 @@ pub async fn fetch_persistent_accounts() -> Result> { } } -/// Produces a list of accounts for storage -pub fn produce_data_for_storage(user_data: &NSSAUserData) -> Vec { +/// Produces data for storage +pub fn produce_data_for_storage( + user_data: &NSSAUserData, + last_synced_block: u64, +) -> PersistentStorage { let mut vec_for_storage = vec![]; for (addr, key) in &user_data.pub_account_signing_keys { @@ -77,7 +82,10 @@ pub fn produce_data_for_storage(user_data: &NSSAUserData) -> Vec Vec { @@ -86,6 +94,30 @@ pub(crate) fn produce_random_nonces(size: usize) -> Vec { result.into_iter().map(Nonce::from_le_bytes).collect() } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum AddressPrivacyKind { + Public, + Private, +} + +pub(crate) fn parse_addr_with_privacy_prefix( + addr_base58: &str, +) -> Result<(String, AddressPrivacyKind)> { + if addr_base58.starts_with("Public/") { + Ok(( + addr_base58.strip_prefix("Public/").unwrap().to_string(), + AddressPrivacyKind::Public, + )) + } else if addr_base58.starts_with("Private/") { + Ok(( + addr_base58.strip_prefix("Private/").unwrap().to_string(), + AddressPrivacyKind::Private, + )) + } else { + anyhow::bail!("Unsupported privacy kind, available variants is Public/ and Private/"); + } +} + /// Human-readable representation of an account. #[derive(Serialize)] pub(crate) struct HumanReadableAccount { @@ -126,4 +158,20 @@ mod tests { std::env::remove_var(HOME_DIR_ENV_VAR); } } + + #[test] + fn test_addr_parse_with_privacy() { + let addr_base58 = "Public/BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy"; + let (_, addr_kind) = parse_addr_with_privacy_prefix(addr_base58).unwrap(); + + assert_eq!(addr_kind, AddressPrivacyKind::Public); + + let addr_base58 = "Private/BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy"; + let (_, addr_kind) = parse_addr_with_privacy_prefix(addr_base58).unwrap(); + + assert_eq!(addr_kind, AddressPrivacyKind::Private); + + let addr_base58 = "asdsada/BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy"; + assert!(parse_addr_with_privacy_prefix(addr_base58).is_err()); + } } diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 833422f..3beac67 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -20,16 +20,18 @@ use clap::{Parser, Subcommand}; use nssa_core::{Commitment, MembershipProof}; use tokio::io::AsyncWriteExt; -use crate::cli::{ - WalletSubcommand, account::AccountSubcommand, chain::ChainSubcommand, - native_token_transfer_program::NativeTokenTransferProgramSubcommand, - pinata_program::PinataProgramSubcommand, +use crate::{ + cli::{ + WalletSubcommand, account::AccountSubcommand, chain::ChainSubcommand, + native_token_transfer_program::AuthTransferSubcommand, + pinata_program::PinataProgramAgnosticSubcommand, + token_program::TokenProgramAgnosticSubcommand, + }, + config::PersistentStorage, + helperfunctions::fetch_persistent_storage, }; use crate::{ - cli::token_program::TokenProgramSubcommand, - helperfunctions::{ - fetch_config, fetch_persistent_accounts, get_home, produce_data_for_storage, - }, + helperfunctions::{fetch_config, get_home, produce_data_for_storage}, poller::TxPoller, }; @@ -49,6 +51,7 @@ pub struct WalletCore { pub storage: WalletChainStore, pub poller: TxPoller, pub sequencer_client: Arc, + pub last_synced_block: u64, } impl WalletCore { @@ -58,7 +61,10 @@ impl WalletCore { let mut storage = WalletChainStore::new(config)?; - let persistent_accounts = fetch_persistent_accounts().await?; + let PersistentStorage { + accounts: persistent_accounts, + last_synced_block, + } = fetch_persistent_storage().await?; for pers_acc_data in persistent_accounts { storage.insert_account_data(pers_acc_data); } @@ -67,23 +73,24 @@ impl WalletCore { storage, poller: tx_poller, sequencer_client: client.clone(), + last_synced_block, }) } - ///Store persistent accounts at home - pub async fn store_persistent_accounts(&self) -> Result { + ///Store persistent data at home + pub async fn store_persistent_data(&self) -> Result { let home = get_home()?; - let accs_path = home.join("curr_accounts.json"); + let storage_path = home.join("storage.json"); - let data = produce_data_for_storage(&self.storage.user_data); - let accs = serde_json::to_vec_pretty(&data)?; + let data = produce_data_for_storage(&self.storage.user_data, self.last_synced_block); + let storage = serde_json::to_vec_pretty(&data)?; - let mut accs_file = tokio::fs::File::create(accs_path.as_path()).await?; - accs_file.write_all(&accs).await?; + let mut storage_file = tokio::fs::File::create(storage_path.as_path()).await?; + storage_file.write_all(&storage).await?; - info!("Stored accounts data at {accs_path:#?}"); + info!("Stored data at {storage_path:#?}"); - Ok(accs_path) + Ok(storage_path) } pub fn create_new_account_public(&mut self) -> Address { @@ -191,28 +198,32 @@ impl WalletCore { #[derive(Subcommand, Debug, Clone)] #[clap(about)] pub enum Command { - ///Transfer command + ///Authenticated transfer subcommand #[command(subcommand)] - Transfer(NativeTokenTransferProgramSubcommand), - ///Chain command + AuthTransfer(AuthTransferSubcommand), + ///Generic chain info subcommand #[command(subcommand)] - Chain(ChainSubcommand), - ///Chain command + ChainInfo(ChainSubcommand), + ///Account view and sync subcommand #[command(subcommand)] Account(AccountSubcommand), - ///Pinata command + ///Pinata program interaction subcommand #[command(subcommand)] - PinataProgram(PinataProgramSubcommand), - ///Token command + Pinata(PinataProgramAgnosticSubcommand), + ///Token program interaction subcommand #[command(subcommand)] - TokenProgram(TokenProgramSubcommand), - AuthenticatedTransferInitializePublicAccount {}, - // Check the wallet can connect to the node and builtin local programs - // match the remote versions + Token(TokenProgramAgnosticSubcommand), + /// Check the wallet can connect to the node and builtin local programs + /// match the remote versions CheckHealth {}, } ///To execute commands, env var NSSA_WALLET_HOME_DIR must be set into directory with config +/// +/// All account adresses must be valid 32 byte base58 strings. +/// +/// All account addresses must be provided as {privacy_prefix}/{addr}, +/// where valid options for `privacy_prefix` is `Public` and `Private` #[derive(Parser, Debug)] #[clap(version, about)] pub struct Args { @@ -230,6 +241,7 @@ pub enum SubcommandReturnValue { RegisterAccount { addr: nssa::Address }, Account(nssa::Account), Empty, + SyncedToBlock(u64), } pub async fn execute_subcommand(command: Command) -> Result { @@ -237,12 +249,12 @@ pub async fn execute_subcommand(command: Command) -> Result { + Command::AuthTransfer(transfer_subcommand) => { transfer_subcommand .handle_subcommand(&mut wallet_core) .await? } - Command::Chain(chain_subcommand) => { + Command::ChainInfo(chain_subcommand) => { chain_subcommand.handle_subcommand(&mut wallet_core).await? } Command::Account(account_subcommand) => { @@ -250,7 +262,7 @@ pub async fn execute_subcommand(command: Command) -> Result { + Command::Pinata(pinata_subcommand) => { pinata_subcommand .handle_subcommand(&mut wallet_core) .await? @@ -285,26 +297,7 @@ pub async fn execute_subcommand(command: Command) -> Result { - 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:#?}"); - - let res = wallet_core - .register_account_under_authenticated_transfers_programs(addr) - .await?; - - println!("Results of tx send is {res:#?}"); - - let _transfer_tx = wallet_core.poll_native_token_transfer(res.tx_hash).await?; - - SubcommandReturnValue::RegisterAccount { addr } - } - Command::TokenProgram(token_subcommand) => { + Command::Token(token_subcommand) => { token_subcommand.handle_subcommand(&mut wallet_core).await? } }; @@ -312,6 +305,80 @@ pub async fn execute_subcommand(command: Command) -> Result, + wallet_core: &mut WalletCore, +) -> Result<()> { + for block_id in start..(stop + 1) { + let block = + borsh::from_slice::(&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.last_synced_block = block_id; + wallet_core.store_persistent_data().await?; + + println!( + "Block at id {block_id} with timestamp {} parsed", + block.timestamp + ); + } + + Ok(()) +} + pub async fn execute_continious_run() -> Result<()> { let config = fetch_config().await?; let seq_client = Arc::new(SequencerClient::new(config.sequencer_addr.clone())?); @@ -321,70 +388,13 @@ pub async fn execute_continious_run() -> Result<()> { 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::( - &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 - ); - } + parse_block_range( + curr_last_block, + latest_block_num, + seq_client.clone(), + &mut wallet_core, + ) + .await?; curr_last_block = latest_block_num + 1; diff --git a/wallet/src/transaction_utils.rs b/wallet/src/transaction_utils.rs index 0761fa7..2dd69ca 100644 --- a/wallet/src/transaction_utils.rs +++ b/wallet/src/transaction_utils.rs @@ -537,4 +537,53 @@ impl WalletCore { Ok(self.sequencer_client.send_tx_private(tx).await?) } + + pub async fn register_account_under_authenticated_transfers_programs_private( + &self, + from: Address, + ) -> Result<(SendTxResponse, [SharedSecretKey; 1]), ExecutionFailureKind> { + let AccountPreparedData { + nsk: _, + npk: from_npk, + ipk: from_ipk, + auth_acc: sender_pre, + proof: _, + } = self.private_acc_preparation(from, false, false).await?; + + let eph_holder_from = EphemeralKeyHolder::new(&from_npk); + let shared_secret_from = eph_holder_from.calculate_shared_secret_sender(&from_ipk); + + let instruction: u128 = 0; + + let (output, proof) = circuit::execute_and_prove( + &[sender_pre], + &Program::serialize_instruction(instruction).unwrap(), + &[2], + &produce_random_nonces(1), + &[(from_npk.clone(), shared_secret_from.clone())], + &[], + &Program::authenticated_transfer_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(), + )], + 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], + )) + } }