diff --git a/Cargo.toml b/Cargo.toml index 8eefa02..2b98867 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,8 +4,7 @@ members = [ "integration_tests", "sequencer_runner", "storage", - "accounts", - "utxo", + "key_protocol", "sequencer_rpc", "mempool", "wallet", diff --git a/accounts/src/account_core/address.rs b/accounts/src/account_core/address.rs deleted file mode 100644 index 83ce792..0000000 --- a/accounts/src/account_core/address.rs +++ /dev/null @@ -1,3 +0,0 @@ -// TODO: Consider wrapping `AccountAddress` in a struct. - -pub type AccountAddress = [u8; 32]; diff --git a/accounts/src/account_core/mod.rs b/accounts/src/account_core/mod.rs deleted file mode 100644 index 944f595..0000000 --- a/accounts/src/account_core/mod.rs +++ /dev/null @@ -1,221 +0,0 @@ -use std::collections::HashMap; - -use anyhow::Result; -use common::{merkle_tree_public::TreeHashType, transaction::Tag}; -use k256::AffinePoint; -use log::info; -use nssa::Address; -use serde::{Deserialize, Serialize}; -use utxo::utxo_core::UTXO; - -use crate::key_management::{ - constants_types::{CipherText, Nonce}, - ephemeral_key_holder::EphemeralKeyHolder, - AddressKeyHolder, -}; - -pub type PublicKey = AffinePoint; - -#[derive(Clone, Debug)] -pub struct Account { - pub key_holder: AddressKeyHolder, - pub address: Address, - pub balance: u64, - pub utxos: HashMap, -} - -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct AccountForSerialization { - pub key_holder: AddressKeyHolder, - pub address: String, - pub balance: u64, - pub utxos: HashMap, -} - -impl From for AccountForSerialization { - fn from(value: Account) -> Self { - AccountForSerialization { - key_holder: value.key_holder, - balance: value.balance, - address: value.address.to_string(), - utxos: value - .utxos - .into_iter() - .map(|(key, val)| (hex::encode(key), val)) - .collect(), - } - } -} - -impl From for Account { - fn from(value: AccountForSerialization) -> Self { - let public_key = - nssa::PublicKey::new_from_private_key(value.key_holder.get_pub_account_signing_key()); - let address = nssa::Address::from(&public_key); - Account { - key_holder: value.key_holder, - address, - balance: value.balance, - utxos: value - .utxos - .into_iter() - .map(|(key, val)| (hex::decode(key).unwrap().try_into().unwrap(), val)) - .collect(), - } - } -} - -impl Serialize for Account { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let account_for_serialization: AccountForSerialization = From::from(self.clone()); - account_for_serialization.serialize(serializer) - } -} - -impl<'de> Deserialize<'de> for Account { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let account_for_serialization = ::deserialize(deserializer)?; - Ok(account_for_serialization.into()) - } -} - -impl Account { - pub fn new() -> Self { - let key_holder = AddressKeyHolder::new_os_random(); - let public_key = - nssa::PublicKey::new_from_private_key(key_holder.get_pub_account_signing_key()); - let address = nssa::Address::from(&public_key); - let balance = 0; - let utxos = HashMap::new(); - - Self { - key_holder, - address, - balance, - utxos, - } - } - - pub fn new_with_balance(balance: u64) -> Self { - let key_holder = AddressKeyHolder::new_os_random(); - let public_key = - nssa::PublicKey::new_from_private_key(key_holder.get_pub_account_signing_key()); - let address = nssa::Address::from(&public_key); - let utxos = HashMap::new(); - - Self { - key_holder, - address, - balance, - utxos, - } - } - - pub fn encrypt_data( - ephemeral_key_holder: &EphemeralKeyHolder, - viewing_public_key_receiver: AffinePoint, - data: &[u8], - ) -> (CipherText, Nonce) { - ephemeral_key_holder.encrypt_data(viewing_public_key_receiver, data) - } - - pub fn decrypt_data( - &self, - ephemeral_public_key_sender: AffinePoint, - ciphertext: CipherText, - nonce: Nonce, - ) -> Result, aes_gcm::Error> { - self.key_holder - .decrypt_data(ephemeral_public_key_sender, ciphertext, nonce) - } - - pub fn add_new_utxo_outputs(&mut self, utxos: Vec) -> Result<()> { - for utxo in utxos { - if self.utxos.contains_key(&utxo.hash) { - return Err(anyhow::anyhow!("UTXO already exists")); - } - self.utxos.insert(utxo.hash, utxo); - } - Ok(()) - } - - pub fn update_public_balance(&mut self, new_balance: u64) { - self.balance = new_balance; - } - - pub fn add_asset( - &mut self, - asset: Asset, - amount: u128, - privacy_flag: bool, - ) -> Result<()> { - let asset_utxo = UTXO::new( - *self.address.value(), - serde_json::to_vec(&asset)?, - amount, - privacy_flag, - ); - - self.utxos.insert(asset_utxo.hash, asset_utxo); - - Ok(()) - } - - pub fn log(&self) { - info!("Keys generated"); - info!("Account address is {:?}", hex::encode(self.address)); - info!("Account balance is {:?}", self.balance); - } - - pub fn make_tag(&self) -> Tag { - self.address.value()[0] - } -} - -impl Default for Account { - fn default() -> Self { - Self::new() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn generate_dummy_utxo(address: TreeHashType, amount: u128) -> UTXO { - UTXO::new(address, vec![], amount, false) - } - - #[test] - fn test_new_account() { - let account = Account::new(); - - assert_eq!(account.balance, 0); - } - - #[test] - fn test_add_new_utxo_outputs() { - let mut account = Account::new(); - let utxo1 = generate_dummy_utxo(*account.address.value(), 100); - let utxo2 = generate_dummy_utxo(*account.address.value(), 200); - - let result = account.add_new_utxo_outputs(vec![utxo1.clone(), utxo2.clone()]); - - assert!(result.is_ok()); - assert_eq!(account.utxos.len(), 2); - } - - #[test] - fn test_update_public_balance() { - let mut account = Account::new(); - account.update_public_balance(500); - - assert_eq!(account.balance, 500); - } -} diff --git a/accounts/src/lib.rs b/accounts/src/lib.rs deleted file mode 100644 index 998d3ec..0000000 --- a/accounts/src/lib.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod account_core; -pub mod key_management; diff --git a/common/src/lib.rs b/common/src/lib.rs index b7f45ab..01ae261 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -68,6 +68,8 @@ pub enum ExecutionFailureKind { DecodeError(String), #[error("Inputs amounts does not match outputs")] AmountMismatchError, + #[error("Accounts key not found")] + KeyNotFoundError, #[error("Sequencer client error: {0:?}")] SequencerClientError(#[from] SequencerClientError), #[error("Insufficient gas for operation")] diff --git a/integration_tests/Cargo.toml b/integration_tests/Cargo.toml index 10c49fb..df2bad8 100644 --- a/integration_tests/Cargo.toml +++ b/integration_tests/Cargo.toml @@ -36,5 +36,5 @@ path = "../wallet" [dependencies.common] path = "../common" -[dependencies.accounts] -path = "../accounts" +[dependencies.key_protocol] +path = "../key_protocol" diff --git a/integration_tests/configs/debug/wallet/wallet_config.json b/integration_tests/configs/debug/wallet/wallet_config.json index 6085a36..d888119 100644 --- a/integration_tests/configs/debug/wallet/wallet_config.json +++ b/integration_tests/configs/debug/wallet/wallet_config.json @@ -1,45 +1,113 @@ { - "home": "./node", - "override_rust_log": null, - "sequencer_addr": "http://127.0.0.1:3040", - "seq_poll_timeout_secs": 10, - "initial_accounts": [ - { - "address": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", - "balance": 10000, - "key_holder": { - "address": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", - "nullifer_public_key": "03A340BECA9FAAB444CED0140681D72EA1318B5C611704FEE017DA9836B17DB718", - "pub_account_signing_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], - "top_secret_key_holder": { - "secret_spending_key": "7BC46784DB1BC67825D8F029436846712BFDF9B5D79EA3AB11D39A52B9B229D4" + "home": "./node", + "override_rust_log": null, + "sequencer_addr": "http://127.0.0.1:3040", + "seq_poll_timeout_secs": 10, + "initial_accounts": [ + { + "address": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", + "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 + ], + "account": { + "program_owner": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "balance": 10000, + "nonce": 0, + "data": [] + } }, - "utxo_secret_key_holder": { - "nullifier_secret_key": "BB54A8D3C9C51B82C431082D1845A74677B0EF829A11B517E1D9885DE3139506", - "viewing_secret_key": "AD923E92F6A5683E30140CEAB2702AFB665330C1EE4EFA70FAF29767B6B52BAF" - }, - "viewing_public_key": "0361220C5D277E7A1709340FD31A52600C1432B9C45B9BCF88A43581D58824A8B6" - }, - "utxos": {} - }, - { - "address": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", - "balance": 20000, - "key_holder": { - "address": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", - "nullifer_public_key": "02172F50274DE67C4087C344F5D58E11DF761D90285B095060E0994FAA6BCDE271", - "pub_account_signing_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], - "top_secret_key_holder": { - "secret_spending_key": "80A186737C8D38B4288A03F0F589957D9C040D79C19F3E0CC4BA80F8494E5179" - }, - "utxo_secret_key_holder": { - "nullifier_secret_key": "746928E63F0984F6F4818933493CE9C067562D9CB932FDC06D82C86CDF6D7122", - "viewing_secret_key": "89176CF4BC9E673807643FD52110EF99D4894335AFB10D881AC0B5041FE1FCB7" - }, - "viewing_public_key": "026072A8F83FEC3472E30CDD4767683F30B91661D25B1040AD9A5FC2E01D659F99" - }, - "utxos": {} - } - ] + { + "address": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", + "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 + ], + "account": { + "program_owner": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "balance": 20000, + "nonce": 0, + "data": [] + } + } + ] } diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index b5bb265..812e44f 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -156,7 +156,9 @@ pub async fn test_failure() { let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - wallet::execute_subcommand(command).await.unwrap(); + let failed_send = wallet::execute_subcommand(command).await; + + assert!(failed_send.is_err()); info!("Waiting for next block creation"); tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; diff --git a/accounts/Cargo.toml b/key_protocol/Cargo.toml similarity index 88% rename from accounts/Cargo.toml rename to key_protocol/Cargo.toml index d4bc3fa..2f2bb07 100644 --- a/accounts/Cargo.toml +++ b/key_protocol/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "accounts" +name = "key_protocol" version = "0.1.0" edition = "2021" @@ -17,9 +17,7 @@ hex.workspace = true aes-gcm.workspace = true lazy_static.workspace = true tiny-keccak.workspace = true - -[dependencies.utxo] -path = "../utxo" +nssa-core = { path = "../nssa/core" } [dependencies.common] path = "../common" diff --git a/accounts/src/key_management/constants_types.rs b/key_protocol/src/key_management/constants_types.rs similarity index 100% rename from accounts/src/key_management/constants_types.rs rename to key_protocol/src/key_management/constants_types.rs diff --git a/accounts/src/key_management/ephemeral_key_holder.rs b/key_protocol/src/key_management/ephemeral_key_holder.rs similarity index 100% rename from accounts/src/key_management/ephemeral_key_holder.rs rename to key_protocol/src/key_management/ephemeral_key_holder.rs diff --git a/accounts/src/key_management/mod.rs b/key_protocol/src/key_management/mod.rs similarity index 81% rename from accounts/src/key_management/mod.rs rename to key_protocol/src/key_management/mod.rs index 9e48cbd..bc6d14c 100644 --- a/accounts/src/key_management/mod.rs +++ b/key_protocol/src/key_management/mod.rs @@ -1,15 +1,15 @@ +use std::collections::HashMap; + use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit}; use constants_types::{CipherText, Nonce}; use elliptic_curve::point::AffineCoordinates; use k256::AffinePoint; use log::info; -use rand::{rngs::OsRng, Rng}; use secret_holders::{SeedHolder, TopSecretKeyHolder, UTXOSecretKeyHolder}; use serde::{Deserialize, Serialize}; -use crate::account_core::PublicKey; +use crate::key_protocol_core::PublicKey; pub type PublicAccountSigningKey = [u8; 32]; -use nssa::{self}; pub mod constants_types; pub mod ephemeral_key_holder; @@ -17,15 +17,16 @@ pub mod secret_holders; #[derive(Serialize, Deserialize, Clone, Debug)] ///Entrypoint to key management -pub struct AddressKeyHolder { +pub struct KeyChain { top_secret_key_holder: TopSecretKeyHolder, pub utxo_secret_key_holder: UTXOSecretKeyHolder, - pub_account_signing_key: nssa::PrivateKey, + ///Map for all users accounts + pub_account_signing_keys: HashMap, pub nullifer_public_key: PublicKey, pub viewing_public_key: PublicKey, } -impl AddressKeyHolder { +impl KeyChain { pub fn new_os_random() -> Self { //Currently dropping SeedHolder at the end of initialization. //Now entirely sure if we need it in the future. @@ -37,26 +38,50 @@ impl AddressKeyHolder { let nullifer_public_key = utxo_secret_key_holder.generate_nullifier_public_key(); let viewing_public_key = utxo_secret_key_holder.generate_viewing_public_key(); - let mut rng = OsRng; - let pub_account_signing_key = loop { - match nssa::PrivateKey::try_new(rng.gen()) { - Ok(key) => break key, - Err(_) => continue, - } - }; + Self { + top_secret_key_holder, + utxo_secret_key_holder, + nullifer_public_key, + viewing_public_key, + pub_account_signing_keys: HashMap::new(), + } + } + + pub fn new_os_random_with_accounts(accounts: HashMap) -> Self { + //Currently dropping SeedHolder at the end of initialization. + //Now entirely sure if we need it in the future. + let seed_holder = SeedHolder::new_os_random(); + let top_secret_key_holder = seed_holder.produce_top_secret_key_holder(); + + let utxo_secret_key_holder = top_secret_key_holder.produce_utxo_secret_holder(); + + let nullifer_public_key = utxo_secret_key_holder.generate_nullifier_public_key(); + let viewing_public_key = utxo_secret_key_holder.generate_viewing_public_key(); Self { top_secret_key_holder, utxo_secret_key_holder, nullifer_public_key, viewing_public_key, - pub_account_signing_key, + pub_account_signing_keys: accounts, } } + pub fn generate_new_private_key(&mut self) -> nssa::Address { + let private_key = nssa::PrivateKey::new_os_random(); + let address = nssa::Address::from(&nssa::PublicKey::new_from_private_key(&private_key)); + + self.pub_account_signing_keys.insert(address, private_key); + + address + } + /// Returns the signing key for public transaction signatures - pub fn get_pub_account_signing_key(&self) -> &nssa::PrivateKey { - &self.pub_account_signing_key + pub fn get_pub_account_signing_key( + &self, + address: &nssa::Address, + ) -> Option<&nssa::PrivateKey> { + self.pub_account_signing_keys.get(address) } pub fn calculate_shared_secret_receiver( @@ -127,8 +152,8 @@ mod tests { #[test] fn test_new_os_random() { - // Ensure that a new AddressKeyHolder instance can be created without errors. - let address_key_holder = AddressKeyHolder::new_os_random(); + // Ensure that a new KeyChain instance can be created without errors. + let address_key_holder = KeyChain::new_os_random(); // Check that key holder fields are initialized with expected types assert!(!Into::::into( @@ -141,7 +166,7 @@ mod tests { #[test] fn test_calculate_shared_secret_receiver() { - let address_key_holder = AddressKeyHolder::new_os_random(); + let address_key_holder = KeyChain::new_os_random(); // Generate a random ephemeral public key sender let scalar = Scalar::random(&mut OsRng); @@ -157,7 +182,7 @@ mod tests { #[test] fn test_decrypt_data() { - let address_key_holder = AddressKeyHolder::new_os_random(); + let address_key_holder = KeyChain::new_os_random(); // Generate an ephemeral key and shared secret let ephemeral_public_key_sender = @@ -188,8 +213,8 @@ mod tests { #[test] fn test_new_os_random_initialization() { - // Ensure that AddressKeyHolder is initialized correctly - let address_key_holder = AddressKeyHolder::new_os_random(); + // Ensure that KeyChain is initialized correctly + let address_key_holder = KeyChain::new_os_random(); // Check that key holder fields are initialized with expected types and values assert!(!Into::::into( @@ -202,7 +227,7 @@ mod tests { #[test] fn test_calculate_shared_secret_with_identity_point() { - let address_key_holder = AddressKeyHolder::new_os_random(); + let address_key_holder = KeyChain::new_os_random(); // Use identity point as ephemeral public key let identity_point = AffinePoint::identity(); @@ -217,7 +242,7 @@ mod tests { #[test] #[should_panic] fn test_decrypt_data_with_incorrect_nonce() { - let address_key_holder = AddressKeyHolder::new_os_random(); + let address_key_holder = KeyChain::new_os_random(); // Generate ephemeral public key and shared secret let scalar = Scalar::random(OsRng); @@ -250,7 +275,7 @@ mod tests { #[test] #[should_panic] fn test_decrypt_data_with_incorrect_ciphertext() { - let address_key_holder = AddressKeyHolder::new_os_random(); + let address_key_holder = KeyChain::new_os_random(); // Generate ephemeral public key and shared secret let scalar = Scalar::random(OsRng); @@ -285,7 +310,7 @@ mod tests { #[test] fn test_encryption_decryption_round_trip() { - let address_key_holder = AddressKeyHolder::new_os_random(); + let address_key_holder = KeyChain::new_os_random(); // Generate ephemeral key and shared secret let scalar = Scalar::random(OsRng); @@ -303,7 +328,7 @@ mod tests { .encrypt(nonce, plaintext.as_ref()) .expect("encryption failure"); - // Decrypt the data using the `AddressKeyHolder` instance + // Decrypt the data using the `KeyChain` instance let decrypted_data = address_key_holder .decrypt_data( ephemeral_public_key_sender, @@ -318,9 +343,15 @@ mod tests { #[test] fn test_get_public_account_signing_key() { - let address_key_holder = AddressKeyHolder::new_os_random(); - let signing_key = address_key_holder.get_pub_account_signing_key(); - assert_eq!(signing_key, &address_key_holder.pub_account_signing_key); + let mut address_key_holder = KeyChain::new_os_random(); + + let address = address_key_holder.generate_new_private_key(); + + let is_private_key_generated = address_key_holder + .get_pub_account_signing_key(&address) + .is_some(); + + assert!(is_private_key_generated); } #[test] @@ -333,13 +364,7 @@ mod tests { let nullifer_public_key = utxo_secret_key_holder.generate_nullifier_public_key(); let viewing_public_key = utxo_secret_key_holder.generate_viewing_public_key(); - let mut rng = OsRng; - let pub_account_signing_key = loop { - match nssa::PrivateKey::try_new(rng.gen()) { - Ok(key) => break key, - Err(_) => continue, - } - }; + let pub_account_signing_key = nssa::PrivateKey::new_os_random(); let public_key = nssa::PublicKey::new_from_private_key(&pub_account_signing_key); diff --git a/accounts/src/key_management/secret_holders.rs b/key_protocol/src/key_management/secret_holders.rs similarity index 100% rename from accounts/src/key_management/secret_holders.rs rename to key_protocol/src/key_management/secret_holders.rs diff --git a/key_protocol/src/key_protocol_core/mod.rs b/key_protocol/src/key_protocol_core/mod.rs new file mode 100644 index 0000000..67cb52c --- /dev/null +++ b/key_protocol/src/key_protocol_core/mod.rs @@ -0,0 +1,72 @@ +use std::collections::HashMap; + +use anyhow::Result; +use k256::AffinePoint; +use serde::{Deserialize, Serialize}; + +use crate::key_management::KeyChain; + +pub type PublicKey = AffinePoint; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct NSSAUserData { + pub key_holder: KeyChain, +} + +impl NSSAUserData { + pub fn new() -> Self { + let key_holder = KeyChain::new_os_random(); + + Self { key_holder } + } + + fn valid_key_transaction_pairing_check( + accounts_keys_map: &HashMap, + ) -> bool { + let mut check_res = true; + for (addr, key) in accounts_keys_map { + if &nssa::Address::from(&nssa::PublicKey::new_from_private_key(key)) != addr { + check_res = false; + } + } + check_res + } + + pub fn new_with_accounts( + accounts_keys: HashMap, + ) -> Result { + if !Self::valid_key_transaction_pairing_check(&accounts_keys) { + anyhow::bail!("Key transaction pairing check not satisfied, there is addresses, which is not derived from keys"); + } + + let key_holder = KeyChain::new_os_random_with_accounts(accounts_keys); + + Ok(Self { key_holder }) + } + + pub fn generate_new_account(&mut self) -> nssa::Address { + self.key_holder.generate_new_private_key() + } + + pub fn get_account_signing_key(&self, address: &nssa::Address) -> Option<&nssa::PrivateKey> { + self.key_holder.get_pub_account_signing_key(address) + } +} + +impl Default for NSSAUserData { + fn default() -> Self { + Self::new() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_new_account() { + let mut user_data = NSSAUserData::new(); + + let _addr = user_data.generate_new_account(); + } +} diff --git a/key_protocol/src/lib.rs b/key_protocol/src/lib.rs new file mode 100644 index 0000000..1a52c20 --- /dev/null +++ b/key_protocol/src/lib.rs @@ -0,0 +1,2 @@ +pub mod key_management; +pub mod key_protocol_core; diff --git a/nssa/program_methods/guest/Cargo.lock b/nssa/program_methods/guest/Cargo.lock index eb5eb30..18285e9 100644 --- a/nssa/program_methods/guest/Cargo.lock +++ b/nssa/program_methods/guest/Cargo.lock @@ -648,9 +648,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" dependencies = [ "powerfmt", "serde", @@ -2785,12 +2785,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.41" +version = "0.3.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "8ca967379f9d8eb8058d86ed467d81d03e81acd45757e4ca341c24affbe8e8e3" dependencies = [ "deranged", - "itoa", "num-conv", "powerfmt", "serde", @@ -2800,15 +2799,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "a9108bb380861b07264b950ded55a44a14a4adc68b9f5efd85aafc3aa4d40a68" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "7182799245a7264ce590b349d90338f1c1affad93d2639aed5f8f69c090b334c" dependencies = [ "num-conv", "time-core", diff --git a/nssa/src/address.rs b/nssa/src/address.rs index 7e4bc1e..93304d5 100644 --- a/nssa/src/address.rs +++ b/nssa/src/address.rs @@ -1,5 +1,7 @@ use std::{fmt::Display, str::FromStr}; +use serde::{Deserialize, Serialize}; + use crate::signature::PublicKey; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] @@ -57,6 +59,28 @@ impl Display for Address { } } +impl Serialize for Address { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let hex_string = self.to_string(); + + hex_string.serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for Address { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let hex_string = String::deserialize(deserializer)?; + + Address::from_str(&hex_string).map_err(serde::de::Error::custom) + } +} + #[cfg(test)] mod tests { use crate::{Address, address::AddressError}; diff --git a/nssa/src/signature/private_key.rs b/nssa/src/signature/private_key.rs index b35d7da..667fc30 100644 --- a/nssa/src/signature/private_key.rs +++ b/nssa/src/signature/private_key.rs @@ -1,3 +1,4 @@ +use rand::{Rng, rngs::OsRng}; use serde::{Deserialize, Serialize}; use crate::error::NssaError; @@ -8,6 +9,17 @@ use crate::error::NssaError; pub struct PrivateKey([u8; 32]); impl PrivateKey { + pub fn new_os_random() -> Self { + let mut rng = OsRng; + + loop { + match Self::try_new(rng.r#gen()) { + Ok(key) => break key, + Err(_) => continue, + }; + } + } + fn is_valid_key(value: [u8; 32]) -> bool { secp256k1::SecretKey::from_byte_array(value).is_ok() } @@ -33,4 +45,9 @@ mod tests { let key = PrivateKey::try_new([1; 32]).unwrap(); assert_eq!(key.value(), &key.0); } + + #[test] + fn test_produce_key() { + let _key = PrivateKey::new_os_random(); + } } diff --git a/sequencer_core/Cargo.toml b/sequencer_core/Cargo.toml index 2083f42..2e3570b 100644 --- a/sequencer_core/Cargo.toml +++ b/sequencer_core/Cargo.toml @@ -22,8 +22,8 @@ path = "../storage" [dependencies.mempool] path = "../mempool" -[dependencies.accounts] -path = "../accounts" +[dependencies.key_protocol] +path = "../key_protocol" [dependencies.common] path = "../common" diff --git a/sequencer_rpc/Cargo.toml b/sequencer_rpc/Cargo.toml index ce61f7e..c6ce004 100644 --- a/sequencer_rpc/Cargo.toml +++ b/sequencer_rpc/Cargo.toml @@ -22,8 +22,8 @@ tokio.workspace = true [dependencies.mempool] path = "../mempool" -[dependencies.accounts] -path = "../accounts" +[dependencies.key_protocol] +path = "../key_protocol" [dependencies.sequencer_core] path = "../sequencer_core" diff --git a/utxo/Cargo.toml b/utxo/Cargo.toml deleted file mode 100644 index 02b9ed8..0000000 --- a/utxo/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "utxo" -version = "0.1.0" -edition = "2021" - -[dependencies] -anyhow.workspace = true -serde_json.workspace = true -env_logger.workspace = true -log.workspace = true -serde.workspace = true -sha2.workspace = true -hex.workspace = true -rand.workspace = true - -[dependencies.common] -path = "../common" diff --git a/utxo/src/lib.rs b/utxo/src/lib.rs deleted file mode 100644 index 7baf984..0000000 --- a/utxo/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod utxo_core; diff --git a/utxo/src/utxo_core.rs b/utxo/src/utxo_core.rs deleted file mode 100644 index 07a8830..0000000 --- a/utxo/src/utxo_core.rs +++ /dev/null @@ -1,162 +0,0 @@ -use anyhow::Result; -use common::{merkle_tree_public::TreeHashType, AccountId}; -use log::info; -use rand::{rngs::OsRng, RngCore}; -use serde::{Deserialize, Serialize}; -use sha2::{digest::FixedOutput, Digest}; - -///Raw asset data -pub type Asset = Vec; -pub type Randomness = [u8; 32]; - -#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] -///Container for raw utxo payload -pub struct UTXO { - pub hash: TreeHashType, - pub owner: AccountId, - pub asset: Asset, - // TODO: change to u256 - pub amount: u128, - pub privacy_flag: bool, - pub randomness: Randomness, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct UTXOPayload { - pub owner: AccountId, - pub asset: Asset, - // TODO: change to u256 - pub amount: u128, - pub privacy_flag: bool, - pub randomness: Randomness, -} - -impl UTXOPayload { - fn to_bytes(&self) -> Vec { - let mut result = Vec::new(); - result.extend_from_slice(&self.owner); - result.extend_from_slice(&self.asset); - result.extend_from_slice(&self.amount.to_be_bytes()); - result.push(self.privacy_flag as u8); - result.extend_from_slice(&self.randomness); - result - } -} - -impl UTXO { - pub fn new(owner: AccountId, asset: Asset, amount: u128, privacy_flag: bool) -> Self { - let mut randomness = Randomness::default(); - OsRng.fill_bytes(&mut randomness); - let payload = UTXOPayload { - owner, - asset, - amount, - privacy_flag, - randomness, - }; - Self::create_utxo_from_payload(payload) - } - pub fn create_utxo_from_payload(payload_with_asset: UTXOPayload) -> Self { - let mut hasher = sha2::Sha256::new(); - hasher.update(payload_with_asset.to_bytes()); - let hash = ::from(hasher.finalize_fixed()); - - Self { - hash, - owner: payload_with_asset.owner, - asset: payload_with_asset.asset, - amount: payload_with_asset.amount, - privacy_flag: payload_with_asset.privacy_flag, - randomness: payload_with_asset.randomness, - } - } - - pub fn interpret_asset<'de, ToInterpret: Deserialize<'de>>(&'de self) -> Result { - Ok(serde_json::from_slice(&self.asset)?) - } - - pub fn into_payload(&self) -> UTXOPayload { - UTXOPayload { - owner: self.owner, - asset: self.asset.clone(), - amount: self.amount, - privacy_flag: self.privacy_flag, - randomness: self.randomness, - } - } - - pub fn log(&self) { - info!("UTXO hash is {:?}", hex::encode(self.hash)); - info!("UTXO owner is {:?}", hex::encode(self.owner)); - info!("UTXO asset is {:?}", hex::encode(self.asset.clone())); - info!("UTXO amount is {:?}", self.amount); - info!("UTXO privacy_flag is {:?}", self.privacy_flag); - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[derive(Serialize, Deserialize, PartialEq, Debug)] - struct TestAsset { - id: u32, - name: String, - } - - fn sample_account() -> AccountId { - AccountId::default() - } - - fn sample_payload() -> UTXOPayload { - UTXOPayload { - owner: sample_account(), - asset: serde_json::to_vec(&TestAsset { - id: 1, - name: "Test".to_string(), - }) - .unwrap(), - amount: 10, - privacy_flag: false, - randomness: Randomness::default(), - } - } - - #[test] - fn test_create_utxo_from_payload() { - let payload = sample_payload(); - let utxo = UTXO::create_utxo_from_payload(payload.clone()); - - // Ensure hash is created and the UTXO fields are correctly assigned - assert_eq!(utxo.owner, payload.owner); - assert_eq!(utxo.asset, payload.asset); - } - - #[test] - fn test_interpret_asset() { - let payload = sample_payload(); - let utxo = UTXO::create_utxo_from_payload(payload); - - // Interpret asset as TestAsset - let interpreted: TestAsset = utxo.interpret_asset().unwrap(); - - assert_eq!( - interpreted, - TestAsset { - id: 1, - name: "Test".to_string() - } - ); - } - - #[test] - fn test_interpret_invalid_asset() { - let mut payload = sample_payload(); - payload.asset = vec![0, 1, 2, 3]; // Invalid data for deserialization - let utxo = UTXO::create_utxo_from_payload(payload); - - // This should fail because the asset is not valid JSON for TestAsset - let result: Result = utxo.interpret_asset(); - assert!(result.is_err()); - } -} diff --git a/wallet/Cargo.toml b/wallet/Cargo.toml index db6d6e9..56974c4 100644 --- a/wallet/Cargo.toml +++ b/wallet/Cargo.toml @@ -22,12 +22,10 @@ risc0-zkvm = "3.0.3" hex.workspace = true actix-rt.workspace = true clap.workspace = true +nssa-core = { path = "../nssa/core" } -[dependencies.accounts] -path = "../accounts" - -[dependencies.utxo] -path = "../utxo" +[dependencies.key_protocol] +path = "../key_protocol" [dependencies.nssa] path = "../nssa" diff --git a/wallet/src/chain_storage/accounts_store.rs b/wallet/src/chain_storage/accounts_store.rs deleted file mode 100644 index 4dad474..0000000 --- a/wallet/src/chain_storage/accounts_store.rs +++ /dev/null @@ -1,112 +0,0 @@ -use accounts::account_core::Account; -use nssa::Address; -use std::collections::HashMap; - -pub struct WalletAccountsStore { - pub accounts: HashMap, -} - -impl WalletAccountsStore { - pub fn new() -> Self { - Self { - accounts: HashMap::new(), - } - } - - pub fn register_account(&mut self, account: Account) { - self.accounts.insert(account.address, account); - } - - pub fn unregister_account(&mut self, account_addr: Address) { - self.accounts.remove(&account_addr); - } -} - -impl Default for WalletAccountsStore { - fn default() -> Self { - Self::new() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use accounts::account_core::Account; - /// Helper function to create a sample account - fn create_sample_account(balance: u64) -> Account { - Account::new_with_balance(balance) - } - - fn pad_to_32(slice: &[u8]) -> [u8; 32] { - let mut padded = [0u8; 32]; - let len = slice.len().min(32); - padded[..len].copy_from_slice(&slice[..len]); - padded - } - - #[test] - fn test_create_empty_store() { - let store = WalletAccountsStore::new(); - assert!(store.accounts.is_empty()); - } - - #[test] - fn test_register_account() { - let mut store = WalletAccountsStore::new(); - - let account = create_sample_account(100); - let account_addr = account.address; - - store.register_account(account); - - assert_eq!(store.accounts.len(), 1); - let stored_account = store.accounts.get(&account_addr).unwrap(); - assert_eq!(stored_account.balance, 100); - } - - #[test] - fn test_unregister_account() { - let mut store = WalletAccountsStore::new(); - - let account = create_sample_account(100); - let account_addr = account.address; - store.register_account(account); - - assert_eq!(store.accounts.len(), 1); - - store.unregister_account(account_addr); - assert!(store.accounts.is_empty()); - } - - #[test] - fn test_unregister_nonexistent_account() { - let mut store = WalletAccountsStore::new(); - - let account_addr: [u8; 32] = pad_to_32("nonexistent".to_string().as_bytes()); - store.unregister_account(Address::new(account_addr)); - - assert!(store.accounts.is_empty()); - } - - #[test] - fn test_register_multiple_accounts() { - let mut store = WalletAccountsStore::new(); - - let account1 = create_sample_account(100); - let account2 = create_sample_account(200); - - let address_1 = account1.address; - let address_2 = account2.address; - - store.register_account(account1); - store.register_account(account2); - - assert_eq!(store.accounts.len(), 2); - - let stored_account1 = store.accounts.get(&address_1).unwrap(); - let stored_account2 = store.accounts.get(&address_2).unwrap(); - - assert_eq!(stored_account1.balance, 100); - assert_eq!(stored_account2.balance, 200); - } -} diff --git a/wallet/src/chain_storage/mod.rs b/wallet/src/chain_storage/mod.rs index 86bb37e..7dfb8a2 100644 --- a/wallet/src/chain_storage/mod.rs +++ b/wallet/src/chain_storage/mod.rs @@ -1,54 +1,30 @@ use std::collections::HashMap; -use accounts::account_core::Account; use anyhow::Result; use common::merkle_tree_public::merkle_tree::UTXOCommitmentsMerkleTree; -use nssa::Address; -use serde::{Deserialize, Serialize}; +use key_protocol::key_protocol_core::NSSAUserData; use crate::config::WalletConfig; -pub mod accounts_store; - -#[derive(Deserialize, Serialize)] -pub struct AccMap { - pub acc_map: HashMap, -} - -impl From> for AccMap { - fn from(value: HashMap<[u8; 32], Account>) -> Self { - AccMap { - acc_map: value - .into_iter() - .map(|(key, val)| (hex::encode(key), val)) - .collect(), - } - } -} - -impl From for HashMap<[u8; 32], Account> { - fn from(value: AccMap) -> Self { - value - .acc_map - .into_iter() - .map(|(key, val)| (hex::decode(key).unwrap().try_into().unwrap(), val)) - .collect() - } -} - pub struct WalletChainStore { - pub acc_map: HashMap, + pub user_data: NSSAUserData, pub utxo_commitments_store: UTXOCommitmentsMerkleTree, pub wallet_config: WalletConfig, } impl WalletChainStore { pub fn new(config: WalletConfig) -> Result { - let acc_map = HashMap::new(); + let accounts_keys: HashMap = config + .initial_accounts + .clone() + .into_iter() + .map(|init_acc_data| (init_acc_data.address, init_acc_data.pub_sign_key)) + .collect(); + let utxo_commitments_store = UTXOCommitmentsMerkleTree::new(vec![]); Ok(Self { - acc_map, + user_data: NSSAUserData::new_with_accounts(accounts_keys)?, utxo_commitments_store, wallet_config: config, }) @@ -57,48 +33,33 @@ impl WalletChainStore { #[cfg(test)] mod tests { + use crate::config::InitialAccountData; + use super::*; - use accounts::account_core::Account; use std::path::PathBuf; use tempfile::tempdir; - fn create_initial_accounts() -> Vec { + fn create_initial_accounts() -> Vec { let initial_acc1 = serde_json::from_str(r#"{ "address": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", - "balance": 100, - "nonce": 0, - "key_holder": { - "nullifer_public_key": "03A340BECA9FAAB444CED0140681D72EA1318B5C611704FEE017DA9836B17DB718", - "pub_account_signing_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], - "top_secret_key_holder": { - "secret_spending_key": "7BC46784DB1BC67825D8F029436846712BFDF9B5D79EA3AB11D39A52B9B229D4" - }, - "utxo_secret_key_holder": { - "nullifier_secret_key": "BB54A8D3C9C51B82C431082D1845A74677B0EF829A11B517E1D9885DE3139506", - "viewing_secret_key": "AD923E92F6A5683E30140CEAB2702AFB665330C1EE4EFA70FAF29767B6B52BAF" - }, - "viewing_public_key": "0361220C5D277E7A1709340FD31A52600C1432B9C45B9BCF88A43581D58824A8B6" - }, - "utxos": {} + "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], + "account": { + "program_owner": [0,0,0,0,0,0,0,0], + "balance": 100, + "nonce": 0, + "data": [] + } }"#).unwrap(); let initial_acc2 = serde_json::from_str(r#"{ "address": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", - "balance": 200, - "nonce": 0, - "key_holder": { - "nullifer_public_key": "02172F50274DE67C4087C344F5D58E11DF761D90285B095060E0994FAA6BCDE271", - "pub_account_signing_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], - "top_secret_key_holder": { - "secret_spending_key": "80A186737C8D38B4288A03F0F589957D9C040D79C19F3E0CC4BA80F8494E5179" - }, - "utxo_secret_key_holder": { - "nullifier_secret_key": "746928E63F0984F6F4818933493CE9C067562D9CB932FDC06D82C86CDF6D7122", - "viewing_secret_key": "89176CF4BC9E673807643FD52110EF99D4894335AFB10D881AC0B5041FE1FCB7" - }, - "viewing_public_key": "026072A8F83FEC3472E30CDD4767683F30B91661D25B1040AD9A5FC2E01D659F99" - }, - "utxos": {} + "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], + "account": { + "program_owner": [0,0,0,0,0,0,0,0], + "balance": 100, + "nonce": 0, + "data": [] + } }"#).unwrap(); let initial_accounts = vec![initial_acc1, initial_acc2]; @@ -125,7 +86,6 @@ mod tests { let store = WalletChainStore::new(config.clone()).unwrap(); - assert!(store.acc_map.is_empty()); assert_eq!( store.utxo_commitments_store.get_root().unwrap_or([0; 32]), [0; 32] diff --git a/wallet/src/config.rs b/wallet/src/config.rs index 9e174f3..21bd912 100644 --- a/wallet/src/config.rs +++ b/wallet/src/config.rs @@ -1,7 +1,12 @@ +use serde::{Deserialize, Serialize}; use std::path::PathBuf; -use accounts::account_core::Account; -use serde::{Deserialize, Serialize}; +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct InitialAccountData { + pub address: nssa::Address, + pub account: nssa_core::account::Account, + pub pub_sign_key: nssa::PrivateKey, +} #[derive(Debug, Clone, Serialize, Deserialize)] pub struct GasConfig { @@ -32,5 +37,5 @@ pub struct WalletConfig { ///Sequencer polling duration for new blocks in seconds pub seq_poll_timeout_secs: u64, ///Initial accounts for wallet - pub initial_accounts: Vec, + pub initial_accounts: Vec, } diff --git a/wallet/src/helperfunctions.rs b/wallet/src/helperfunctions.rs index 3a3cca5..51b9d9d 100644 --- a/wallet/src/helperfunctions.rs +++ b/wallet/src/helperfunctions.rs @@ -1,10 +1,12 @@ use std::{fs::File, io::BufReader, path::PathBuf, str::FromStr}; -use accounts::account_core::Account; use anyhow::Result; use nssa::Address; -use crate::{config::WalletConfig, HOME_DIR_ENV_VAR}; +use crate::{ + config::{InitialAccountData, WalletConfig}, + HOME_DIR_ENV_VAR, +}; ///Get home dir for wallet. Env var `NSSA_WALLET_HOME_DIR` must be set before execution to succeed. pub fn get_home() -> Result { @@ -28,7 +30,7 @@ pub fn produce_account_addr_from_hex(hex_str: String) -> Result
{ ///Fetch list of accounts stored at `NSSA_WALLET_HOME_DIR/curr_accounts.json` /// /// If file not present, it is considered as empty list of persistent accounts -pub fn fetch_persistent_accounts() -> Result> { +pub fn fetch_persistent_accounts() -> Result> { let home = get_home()?; let accs_path = home.join("curr_accounts.json"); diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 40ee615..e2213dc 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -5,7 +5,6 @@ use common::{ ExecutionFailureKind, }; -use accounts::account_core::Account; use anyhow::Result; use chain_storage::WalletChainStore; use config::WalletConfig; @@ -13,10 +12,9 @@ use log::info; use nssa::Address; use clap::{Parser, Subcommand}; +use nssa_core::account::Account; -use crate::helperfunctions::{ - fetch_config, fetch_persistent_accounts, produce_account_addr_from_hex, -}; +use crate::helperfunctions::{fetch_config, produce_account_addr_from_hex}; pub const HOME_DIR_ENV_VAR: &str = "NSSA_WALLET_HOME_DIR"; pub const BLOCK_GEN_DELAY_SECS: u64 = 20; @@ -27,41 +25,32 @@ pub mod helperfunctions; pub struct WalletCore { pub storage: WalletChainStore, - pub wallet_config: WalletConfig, pub sequencer_client: Arc, } impl WalletCore { - pub async fn start_from_config_update_chain(config: WalletConfig) -> Result { + pub fn start_from_config_update_chain(config: WalletConfig) -> Result { let client = Arc::new(SequencerClient::new(config.sequencer_addr.clone())?); - let mut storage = WalletChainStore::new(config.clone())?; - for acc in config.clone().initial_accounts { - storage.acc_map.insert(acc.address, acc); - } - - //Persistent accounts take precedence for initial accounts - let persistent_accounts = fetch_persistent_accounts()?; - for acc in persistent_accounts { - storage.acc_map.insert(acc.address, acc); - } + let storage = WalletChainStore::new(config)?; Ok(Self { storage, - wallet_config: config.clone(), sequencer_client: client.clone(), }) } - pub async fn create_new_account(&mut self) -> Address { - let account = Account::new(); - account.log(); + pub fn create_new_account(&mut self) -> Address { + self.storage.user_data.generate_new_account() + } - let addr = account.address; - - self.storage.acc_map.insert(account.address, account); - - addr + pub fn search_for_initial_account(&self, acc_addr: Address) -> Option { + for initial_acc in &self.storage.wallet_config.initial_accounts { + if initial_acc.address == acc_addr { + return Some(initial_acc.account.clone()); + } + } + None } pub async fn send_public_native_token_transfer( @@ -71,27 +60,36 @@ impl WalletCore { to: Address, balance_to_move: u128, ) -> Result { - let account = self.storage.acc_map.get(&from); + let account = self.search_for_initial_account(from); if let Some(account) = account { - let addresses = vec![from, to]; - let nonces = vec![nonce]; - let program_id = nssa::program::Program::authenticated_transfer_program().id(); - let message = nssa::public_transaction::Message::try_new( - program_id, - addresses, - nonces, - balance_to_move, - ) - .unwrap(); + if account.balance >= balance_to_move { + let addresses = vec![from, to]; + let nonces = vec![nonce]; + let program_id = nssa::program::Program::authenticated_transfer_program().id(); + let message = nssa::public_transaction::Message::try_new( + program_id, + addresses, + nonces, + balance_to_move, + ) + .unwrap(); - let signing_key = account.key_holder.get_pub_account_signing_key(); - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]); + let signing_key = self.storage.user_data.get_account_signing_key(&from); - let tx = nssa::PublicTransaction::new(message, witness_set); + if let Some(signing_key) = signing_key { + let witness_set = + nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]); - Ok(self.sequencer_client.send_tx(tx).await?) + let tx = nssa::PublicTransaction::new(message, witness_set); + + Ok(self.sequencer_client.send_tx(tx).await?) + } else { + Err(ExecutionFailureKind::KeyNotFoundError) + } + } else { + Err(ExecutionFailureKind::InsufficientFundsError) + } } else { Err(ExecutionFailureKind::AmountMismatchError) } @@ -141,7 +139,7 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { let from = produce_account_addr_from_hex(from)?; let to = produce_account_addr_from_hex(to)?; - let wallet_core = WalletCore::start_from_config_update_chain(wallet_config).await?; + let wallet_core = WalletCore::start_from_config_update_chain(wallet_config)?; let res = wallet_core .send_public_native_token_transfer(from, nonce, to, amount)