fix: first batch of wallet methods

This commit is contained in:
Oleksandr Pravdyvyi 2025-08-20 17:16:51 +03:00
parent 0abb23e26a
commit 0c7456d7a0
No known key found for this signature in database
GPG Key ID: 9F8955C63C443871
6 changed files with 109 additions and 89 deletions

View File

@ -7,6 +7,7 @@ use json::{SendTxRequest, SendTxResponse, SequencerRpcRequest, SequencerRpcRespo
use reqwest::Client; use reqwest::Client;
use serde_json::Value; use serde_json::Value;
use crate::rpc_primitives::requests::{GetTransactionByHashRequest, GetTransactionByHashResponse};
use crate::sequencer_client::json::AccountInitialData; use crate::sequencer_client::json::AccountInitialData;
use crate::{SequencerClientError, SequencerRpcError}; use crate::{SequencerClientError, SequencerRpcError};
@ -137,4 +138,22 @@ impl SequencerClient {
Ok(resp_deser) Ok(resp_deser)
} }
///Get tx data for `tx_hash` from sequencer
pub async fn get_transaction_by_hash(
&self,
tx_hash: String,
) -> Result<GetTransactionByHashResponse, SequencerClientError> {
let block_req = GetTransactionByHashRequest { hash: tx_hash };
let req = serde_json::to_value(block_req)?;
let resp = self
.call_method_with_payload("get_transaction_by_hash", req)
.await?;
let resp_deser = serde_json::from_value(resp)?;
Ok(resp_deser)
}
} }

View File

@ -1,5 +1,3 @@
use std::collections::HashMap;
use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit}; use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit};
use constants_types::{CipherText, Nonce}; use constants_types::{CipherText, Nonce};
use elliptic_curve::point::AffineCoordinates; use elliptic_curve::point::AffineCoordinates;
@ -20,8 +18,6 @@ pub mod secret_holders;
pub struct KeyChain { pub struct KeyChain {
top_secret_key_holder: TopSecretKeyHolder, top_secret_key_holder: TopSecretKeyHolder,
pub utxo_secret_key_holder: UTXOSecretKeyHolder, pub utxo_secret_key_holder: UTXOSecretKeyHolder,
///Map for all users accounts
pub_account_signing_keys: HashMap<nssa::Address, nssa::PrivateKey>,
pub nullifer_public_key: PublicKey, pub nullifer_public_key: PublicKey,
pub viewing_public_key: PublicKey, pub viewing_public_key: PublicKey,
} }
@ -43,47 +39,9 @@ impl KeyChain {
utxo_secret_key_holder, utxo_secret_key_holder,
nullifer_public_key, nullifer_public_key,
viewing_public_key, viewing_public_key,
pub_account_signing_keys: HashMap::new(),
} }
} }
pub fn new_os_random_with_accounts(accounts: HashMap<nssa::Address, nssa::PrivateKey>) -> 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_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,
address: &nssa::Address,
) -> Option<&nssa::PrivateKey> {
self.pub_account_signing_keys.get(address)
}
pub fn calculate_shared_secret_receiver( pub fn calculate_shared_secret_receiver(
&self, &self,
ephemeral_public_key_sender: AffinePoint, ephemeral_public_key_sender: AffinePoint,
@ -341,19 +299,6 @@ mod tests {
assert_eq!(decrypted_data, plaintext); assert_eq!(decrypted_data, plaintext);
} }
#[test]
fn test_get_public_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] #[test]
fn key_generation_test() { fn key_generation_test() {
let seed_holder = SeedHolder::new_os_random(); let seed_holder = SeedHolder::new_os_random();

View File

@ -15,7 +15,8 @@ pub type PublicKey = AffinePoint;
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct NSSAUserData { pub struct NSSAUserData {
pub key_holder: KeyChain, pub key_holder: KeyChain,
pub accounts: HashMap<nssa::Address, nssa_core::account::Account>, ///Map for all users accounts
pub accounts: HashMap<nssa::Address, (nssa::PrivateKey, nssa_core::account::Account)>,
} }
///A strucure, which represents all the visible(public) information ///A strucure, which represents all the visible(public) information
@ -56,21 +57,22 @@ impl NSSAUserData {
} }
pub fn new_with_accounts( pub fn new_with_accounts(
accounts_keys: HashMap<nssa::Address, nssa::PrivateKey>, accounts_data: HashMap<nssa::Address, (nssa::PrivateKey, nssa_core::account::Account)>,
accounts: HashMap<nssa::Address, nssa_core::account::Account>,
) -> Self { ) -> Self {
let key_holder = KeyChain::new_os_random_with_accounts(accounts_keys); let key_holder = KeyChain::new_os_random();
Self { Self {
key_holder, key_holder,
accounts, accounts: accounts_data,
} }
} }
pub fn generate_new_account(&mut self) -> nssa::Address { pub fn generate_new_account(&mut self) -> nssa::Address {
let address = self.key_holder.generate_new_private_key(); let private_key = nssa::PrivateKey::new_os_random();
self.accounts let address = nssa::Address::from(&nssa::PublicKey::new_from_private_key(&private_key));
.insert(address, nssa_core::account::Account::default()); let account = nssa_core::account::Account::default();
self.accounts.insert(address, (private_key, account));
address address
} }
@ -78,16 +80,16 @@ impl NSSAUserData {
pub fn get_account_balance(&self, address: &nssa::Address) -> u128 { pub fn get_account_balance(&self, address: &nssa::Address) -> u128 {
self.accounts self.accounts
.get(address) .get(address)
.map(|acc| acc.balance) .map(|(_, acc)| acc.balance)
.unwrap_or(0) .unwrap_or(0)
} }
pub fn get_account(&self, address: &nssa::Address) -> Option<&nssa_core::account::Account> { pub fn get_account(&self, address: &nssa::Address) -> Option<&nssa_core::account::Account> {
self.accounts.get(address) self.accounts.get(address).map(|(_, acc)| acc)
} }
pub fn get_account_signing_key(&self, address: &nssa::Address) -> Option<&nssa::PrivateKey> { pub fn get_account_signing_key(&self, address: &nssa::Address) -> Option<&nssa::PrivateKey> {
self.key_holder.get_pub_account_signing_key(address) self.accounts.get(address).map(|(key, _)| key)
} }
pub fn encrypt_data( pub fn encrypt_data(
@ -111,8 +113,7 @@ impl NSSAUserData {
pub fn update_account_balance(&mut self, address: nssa::Address, new_balance: u128) { pub fn update_account_balance(&mut self, address: nssa::Address, new_balance: u128) {
self.accounts self.accounts
.entry(address) .entry(address)
.and_modify(|acc| acc.balance = new_balance) .and_modify(|(_, acc)| acc.balance = new_balance);
.or_default();
} }
//ToDo: Part of a private keys update //ToDo: Part of a private keys update

View File

@ -14,24 +14,23 @@ pub struct WalletChainStore {
impl WalletChainStore { impl WalletChainStore {
pub fn new(config: WalletConfig) -> Result<Self> { pub fn new(config: WalletConfig) -> Result<Self> {
let accounts: HashMap<nssa::Address, nssa_core::account::Account> = config let accounts_data: HashMap<nssa::Address, (nssa::PrivateKey, nssa_core::account::Account)> =
.initial_accounts config
.clone() .initial_accounts
.into_iter() .clone()
.map(|init_acc_data| (init_acc_data.address, init_acc_data.account)) .into_iter()
.collect(); .map(|init_acc_data| {
(
let accounts_keys: HashMap<nssa::Address, nssa::PrivateKey> = config init_acc_data.address,
.initial_accounts (init_acc_data.pub_sign_key, init_acc_data.account),
.clone() )
.into_iter() })
.map(|init_acc_data| (init_acc_data.address, init_acc_data.pub_sign_key)) .collect();
.collect();
let utxo_commitments_store = UTXOCommitmentsMerkleTree::new(vec![]); let utxo_commitments_store = UTXOCommitmentsMerkleTree::new(vec![]);
Ok(Self { Ok(Self {
user_data: NSSAUserData::new_with_accounts(accounts_keys, accounts), user_data: NSSAUserData::new_with_accounts(accounts_data),
utxo_commitments_store, utxo_commitments_store,
wallet_config: config, wallet_config: config,
}) })

View File

@ -1,6 +1,7 @@
use std::{fs::File, io::BufReader, path::PathBuf, str::FromStr}; use std::{fs::File, io::BufReader, path::PathBuf, str::FromStr};
use anyhow::Result; use anyhow::Result;
use key_protocol::key_protocol_core::NSSAUserData;
use nssa::Address; use nssa::Address;
use crate::{ use crate::{
@ -47,3 +48,17 @@ pub fn fetch_persistent_accounts() -> Result<Vec<InitialAccountData>> {
}, },
} }
} }
pub fn produce_data_for_storage(user_data: &NSSAUserData) -> Vec<InitialAccountData> {
let mut vec_for_storage = vec![];
for (addr, (key, account)) in &user_data.accounts {
vec_for_storage.push(InitialAccountData {
address: *addr,
account: account.clone(),
pub_sign_key: key.clone(),
});
}
vec_for_storage
}

View File

@ -1,4 +1,4 @@
use std::sync::Arc; use std::{fs::File, io::Write, path::PathBuf, sync::Arc};
use common::{ use common::{
sequencer_client::{json::SendTxResponse, SequencerClient}, sequencer_client::{json::SendTxResponse, SequencerClient},
@ -14,7 +14,8 @@ use nssa::Address;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use crate::helperfunctions::{ use crate::helperfunctions::{
fetch_config, fetch_persistent_accounts, produce_account_addr_from_hex, fetch_config, fetch_persistent_accounts, get_home, produce_account_addr_from_hex,
produce_data_for_storage,
}; };
pub const HOME_DIR_ENV_VAR: &str = "NSSA_WALLET_HOME_DIR"; pub const HOME_DIR_ENV_VAR: &str = "NSSA_WALLET_HOME_DIR";
@ -35,6 +36,9 @@ impl WalletCore {
let mut storage = WalletChainStore::new(config)?; let mut storage = WalletChainStore::new(config)?;
//Updating user data with stored accounts
//We do not store/update any key data connected to private executions
//ToDo: Add this into persistent data
let persistent_accounts = fetch_persistent_accounts()?; let persistent_accounts = fetch_persistent_accounts()?;
for acc in persistent_accounts { for acc in persistent_accounts {
storage storage
@ -48,7 +52,20 @@ impl WalletCore {
}) })
} }
pub async fn create_new_account(&mut self) -> Address { pub fn store_persistent_accounts(&self) -> Result<PathBuf> {
let home = get_home()?;
let accs_path = home.join("curr_accounts.json");
let accs = serde_json::to_vec(&produce_data_for_storage(&self.storage.user_data))?;
let mut accs_file = File::create(accs_path.as_path())?;
accs_file.write_all(&accs)?;
info!("Stored accounts data at {accs_path:#?}");
Ok(accs_path)
}
pub fn create_new_account(&mut self) -> Address {
self.storage.user_data.generate_new_account() self.storage.user_data.generate_new_account()
} }
@ -114,6 +131,13 @@ pub enum Command {
#[arg(long)] #[arg(long)]
amount: u128, amount: u128,
}, },
///Register new account
RegisterAccount {},
///Fetch transaction by `hash`
FetchTx {
#[arg(short, long)]
tx_hash: String,
},
} }
///To execute commands, env var NSSA_WALLET_HOME_DIR must be set into directory with config ///To execute commands, env var NSSA_WALLET_HOME_DIR must be set into directory with config
@ -126,6 +150,9 @@ pub struct Args {
} }
pub async fn execute_subcommand(command: Command) -> Result<()> { pub async fn execute_subcommand(command: Command) -> Result<()> {
let wallet_config = fetch_config()?;
let mut wallet_core = WalletCore::start_from_config_update_chain(wallet_config).await?;
match command { match command {
Command::SendNativeTokenTransfer { Command::SendNativeTokenTransfer {
from, from,
@ -133,13 +160,9 @@ pub async fn execute_subcommand(command: Command) -> Result<()> {
to, to,
amount, amount,
} => { } => {
let wallet_config = fetch_config()?;
let from = produce_account_addr_from_hex(from)?; let from = produce_account_addr_from_hex(from)?;
let to = produce_account_addr_from_hex(to)?; let to = produce_account_addr_from_hex(to)?;
let wallet_core = WalletCore::start_from_config_update_chain(wallet_config).await?;
let res = wallet_core let res = wallet_core
.send_public_native_token_transfer(from, nonce, to, amount) .send_public_native_token_transfer(from, nonce, to, amount)
.await?; .await?;
@ -148,7 +171,25 @@ pub async fn execute_subcommand(command: Command) -> Result<()> {
//ToDo: Insert transaction polling logic here //ToDo: Insert transaction polling logic here
} }
Command::RegisterAccount {} => {
let addr = wallet_core.create_new_account();
let key = wallet_core.storage.user_data.get_account_signing_key(&addr);
info!("Generated new account with addr {addr:#?}");
info!("With key {key:#?}");
}
Command::FetchTx { tx_hash } => {
let tx_obj = wallet_core
.sequencer_client
.get_transaction_by_hash(tx_hash)
.await?;
info!("Transaction object {tx_obj:#?}");
}
} }
wallet_core.store_persistent_accounts()?;
Ok(()) Ok(())
} }