add get-account wallet command. Small refactor

This commit is contained in:
Sergio Chouhy 2025-09-05 22:50:10 -03:00
parent df2026f107
commit d796d7baf1
7 changed files with 97 additions and 49 deletions

View File

@ -49,7 +49,7 @@ pub struct GetAccountsNoncesRequest {
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct GetAccountDataRequest { pub struct GetAccountRequest {
pub address: String, pub address: String,
} }
@ -63,7 +63,7 @@ parse_request!(GetInitialTestnetAccountsRequest);
parse_request!(GetAccountBalanceRequest); parse_request!(GetAccountBalanceRequest);
parse_request!(GetTransactionByHashRequest); parse_request!(GetTransactionByHashRequest);
parse_request!(GetAccountsNoncesRequest); parse_request!(GetAccountsNoncesRequest);
parse_request!(GetAccountDataRequest); parse_request!(GetAccountRequest);
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct HelloResponse { pub struct HelloResponse {
@ -112,9 +112,6 @@ pub struct GetTransactionByHashResponse {
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct GetAccountDataResponse { pub struct GetAccountResponse {
pub balance: u128, pub account: nssa::Account,
pub nonce: u128,
pub program_owner: [u32; 8],
pub data: Vec<u8>,
} }

View File

@ -8,8 +8,8 @@ use reqwest::Client;
use serde_json::Value; use serde_json::Value;
use crate::rpc_primitives::requests::{ use crate::rpc_primitives::requests::{
GetAccountsNoncesRequest, GetAccountsNoncesResponse, GetTransactionByHashRequest, GetAccountRequest, GetAccountResponse, GetAccountsNoncesRequest,
GetTransactionByHashResponse, GetAccountsNoncesResponse, GetTransactionByHashRequest, GetTransactionByHashResponse,
}; };
use crate::sequencer_client::json::AccountInitialData; use crate::sequencer_client::json::AccountInitialData;
use crate::{SequencerClientError, SequencerRpcError}; use crate::{SequencerClientError, SequencerRpcError};
@ -108,6 +108,23 @@ impl SequencerClient {
Ok(resp_deser) Ok(resp_deser)
} }
pub async fn get_account(
&self,
address: String,
) -> Result<GetAccountResponse, SequencerClientError> {
let block_req = GetAccountRequest { address };
let req = serde_json::to_value(block_req)?;
let resp = self
.call_method_with_payload("get_account", req)
.await?;
let resp_deser = serde_json::from_value(resp)?;
Ok(resp_deser)
}
///Get transaction details for `hash`. ///Get transaction details for `hash`.
pub async fn get_transaction_by_hash( pub async fn get_transaction_by_hash(
&self, &self,

View File

@ -7,6 +7,7 @@ pub mod public_transaction;
mod signature; mod signature;
mod state; mod state;
pub use nssa_core::account::Account;
pub use address::Address; pub use address::Address;
pub use privacy_preserving_transaction::{ pub use privacy_preserving_transaction::{
PrivacyPreservingTransaction, circuit::execute_and_prove, PrivacyPreservingTransaction, circuit::execute_and_prove,

View File

@ -12,8 +12,8 @@ use common::{
message::{Message, Request}, message::{Message, Request},
parser::RpcRequest, parser::RpcRequest,
requests::{ requests::{
GetAccountBalanceRequest, GetAccountBalanceResponse, GetAccountDataRequest, GetAccountBalanceRequest, GetAccountBalanceResponse, GetAccountRequest,
GetAccountDataResponse, GetAccountsNoncesRequest, GetAccountsNoncesResponse, GetAccountResponse, GetAccountsNoncesRequest, GetAccountsNoncesResponse,
GetInitialTestnetAccountsRequest, GetTransactionByHashRequest, GetInitialTestnetAccountsRequest, GetTransactionByHashRequest,
GetTransactionByHashResponse, GetTransactionByHashResponse,
}, },
@ -36,7 +36,7 @@ pub const GET_LAST_BLOCK: &str = "get_last_block";
pub const GET_ACCOUNT_BALANCE: &str = "get_account_balance"; pub const GET_ACCOUNT_BALANCE: &str = "get_account_balance";
pub const GET_TRANSACTION_BY_HASH: &str = "get_transaction_by_hash"; pub const GET_TRANSACTION_BY_HASH: &str = "get_transaction_by_hash";
pub const GET_ACCOUNTS_NONCES: &str = "get_accounts_nonces"; pub const GET_ACCOUNTS_NONCES: &str = "get_accounts_nonces";
pub const GET_ACCOUNT_DATA: &str = "get_account_data"; pub const GET_ACCOUNT: &str = "get_account";
pub const HELLO_FROM_SEQUENCER: &str = "HELLO_FROM_SEQUENCER"; pub const HELLO_FROM_SEQUENCER: &str = "HELLO_FROM_SEQUENCER";
@ -204,10 +204,10 @@ impl JsonHandler {
respond(helperstruct) respond(helperstruct)
} }
///Returns account struct for give address. /// Returns account struct for given address.
/// Address must be a valid hex string of the correct length. /// Address must be a valid hex string of the correct length.
async fn process_get_account_data(&self, request: Request) -> Result<Value, RpcErr> { async fn process_get_account(&self, request: Request) -> Result<Value, RpcErr> {
let get_account_nonces_req = GetAccountDataRequest::parse(Some(request.params))?; let get_account_nonces_req = GetAccountRequest::parse(Some(request.params))?;
let address = get_account_nonces_req let address = get_account_nonces_req
.address .address
@ -220,12 +220,7 @@ impl JsonHandler {
state.store.state.get_account_by_address(&address) state.store.state.get_account_by_address(&address)
}; };
let helperstruct = GetAccountDataResponse { let helperstruct = GetAccountResponse { account };
balance: account.balance,
nonce: account.nonce,
program_owner: account.program_owner,
data: account.data,
};
respond(helperstruct) respond(helperstruct)
} }
@ -265,7 +260,7 @@ impl JsonHandler {
GET_INITIAL_TESTNET_ACCOUNTS => self.get_initial_testnet_accounts(request).await, GET_INITIAL_TESTNET_ACCOUNTS => self.get_initial_testnet_accounts(request).await,
GET_ACCOUNT_BALANCE => self.process_get_account_balance(request).await, GET_ACCOUNT_BALANCE => self.process_get_account_balance(request).await,
GET_ACCOUNTS_NONCES => self.process_get_accounts_nonces(request).await, GET_ACCOUNTS_NONCES => self.process_get_accounts_nonces(request).await,
GET_ACCOUNT_DATA => self.process_get_account_data(request).await, GET_ACCOUNT => self.process_get_account(request).await,
GET_TRANSACTION_BY_HASH => self.process_get_transaction_by_hash(request).await, GET_TRANSACTION_BY_HASH => self.process_get_transaction_by_hash(request).await,
_ => Err(RpcErr(RpcError::method_not_found(request.method))), _ => Err(RpcErr(RpcError::method_not_found(request.method))),
} }
@ -534,7 +529,7 @@ mod tests {
let (json_handler, _, _) = components_for_tests(); let (json_handler, _, _) = components_for_tests();
let request = serde_json::json!({ let request = serde_json::json!({
"jsonrpc": "2.0", "jsonrpc": "2.0",
"method": "get_account_data", "method": "get_account",
"params": { "address": "efac".repeat(16) }, "params": { "address": "efac".repeat(16) },
"id": 1 "id": 1
}); });
@ -542,10 +537,12 @@ mod tests {
"id": 1, "id": 1,
"jsonrpc": "2.0", "jsonrpc": "2.0",
"result": { "result": {
"balance": 0, "account": {
"nonce": 0, "balance": 0,
"program_owner": [ 0, 0, 0, 0, 0, 0, 0, 0], "nonce": 0,
"data": [], "program_owner": [ 0, 0, 0, 0, 0, 0, 0, 0],
"data": [],
}
} }
}); });

View File

@ -14,6 +14,7 @@ tempfile.workspace = true
clap.workspace = true clap.workspace = true
nssa-core = { path = "../nssa/core" } nssa-core = { path = "../nssa/core" }
base64.workspace = true base64.workspace = true
bytemuck = "1.23.2"
[dependencies.key_protocol] [dependencies.key_protocol]
path = "../key_protocol" path = "../key_protocol"

View File

@ -1,20 +1,22 @@
use base64::{Engine, engine::general_purpose::STANDARD as BASE64};
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 key_protocol::key_protocol_core::NSSAUserData;
use nssa::Address; use nssa::{Account, Address};
use serde::Serialize;
use crate::{ use crate::{
HOME_DIR_ENV_VAR, HOME_DIR_ENV_VAR,
config::{PersistentAccountData, WalletConfig}, config::{PersistentAccountData, WalletConfig},
}; };
///Get home dir for wallet. Env var `NSSA_WALLET_HOME_DIR` must be set before execution to succeed. /// Get home dir for wallet. Env var `NSSA_WALLET_HOME_DIR` must be set before execution to succeed.
pub fn get_home() -> Result<PathBuf> { pub fn get_home() -> Result<PathBuf> {
Ok(PathBuf::from_str(&std::env::var(HOME_DIR_ENV_VAR)?)?) Ok(PathBuf::from_str(&std::env::var(HOME_DIR_ENV_VAR)?)?)
} }
///Fetch config from `NSSA_WALLET_HOME_DIR` /// Fetch config from `NSSA_WALLET_HOME_DIR`
pub fn fetch_config() -> Result<WalletConfig> { pub fn fetch_config() -> Result<WalletConfig> {
let config_home = get_home()?; let config_home = get_home()?;
let file = File::open(config_home.join("wallet_config.json"))?; let file = File::open(config_home.join("wallet_config.json"))?;
@ -23,12 +25,12 @@ pub fn fetch_config() -> Result<WalletConfig> {
Ok(serde_json::from_reader(reader)?) Ok(serde_json::from_reader(reader)?)
} }
//ToDo: Replace with structures conversion in future // ToDo: Replace with structures conversion in future
pub fn produce_account_addr_from_hex(hex_str: String) -> Result<Address> { pub fn produce_account_addr_from_hex(hex_str: String) -> Result<Address> {
Ok(hex_str.parse()?) Ok(hex_str.parse()?)
} }
///Fetch list of accounts stored at `NSSA_WALLET_HOME_DIR/curr_accounts.json` /// 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 /// If file not present, it is considered as empty list of persistent accounts
pub fn fetch_persistent_accounts() -> Result<Vec<PersistentAccountData>> { pub fn fetch_persistent_accounts() -> Result<Vec<PersistentAccountData>> {
@ -49,7 +51,7 @@ pub fn fetch_persistent_accounts() -> Result<Vec<PersistentAccountData>> {
} }
} }
///Produces a list of accounts for storage /// Produces a list of accounts for storage
pub fn produce_data_for_storage(user_data: &NSSAUserData) -> Vec<PersistentAccountData> { pub fn produce_data_for_storage(user_data: &NSSAUserData) -> Vec<PersistentAccountData> {
let mut vec_for_storage = vec![]; let mut vec_for_storage = vec![];
@ -63,6 +65,28 @@ pub fn produce_data_for_storage(user_data: &NSSAUserData) -> Vec<PersistentAccou
vec_for_storage vec_for_storage
} }
/// Human-readable representation of an account.
#[derive(Serialize)]
pub(crate) struct HumanReadableAccount {
balance: u128,
program_owner_b64: String,
data_b64: String,
nonce: u128,
}
impl From<Account> for HumanReadableAccount {
fn from(account: Account) -> Self {
let program_owner_b64 = BASE64.encode(bytemuck::cast_slice(&account.program_owner));
let data_b64 = BASE64.encode(account.data);
Self {
balance: account.balance,
program_owner_b64,
data_b64,
nonce: account.nonce,
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -1,6 +1,6 @@
use std::{fs::File, io::Write, path::PathBuf, str::FromStr, sync::Arc}; use std::{fs::File, io::Write, path::PathBuf, str::FromStr, sync::Arc};
use base64::Engine; use base64::{Engine, engine::general_purpose::STANDARD as BASE64};
use common::{ use common::{
ExecutionFailureKind, ExecutionFailureKind,
sequencer_client::{SequencerClient, json::SendTxResponse}, sequencer_client::{SequencerClient, json::SendTxResponse},
@ -17,8 +17,8 @@ use nssa_core::account::Account;
use crate::{ use crate::{
helperfunctions::{ helperfunctions::{
fetch_config, fetch_persistent_accounts, get_home, produce_account_addr_from_hex, HumanReadableAccount, fetch_config, fetch_persistent_accounts, get_home,
produce_data_for_storage, produce_account_addr_from_hex, produce_data_for_storage,
}, },
poller::TxPoller, poller::TxPoller,
}; };
@ -144,14 +144,19 @@ impl WalletCore {
.nonces) .nonces)
} }
///Get account
pub async fn get_account(&self, addr: Address) -> Result<Account> {
let response = self.sequencer_client.get_account(addr.to_string()).await?;
Ok(response.account)
}
///Poll transactions ///Poll transactions
pub async fn poll_public_native_token_transfer( pub async fn poll_public_native_token_transfer(
&self, &self,
hash: String, hash: String,
) -> Result<nssa::PublicTransaction> { ) -> Result<nssa::PublicTransaction> {
let transaction_encoded = self.poller.poll_tx(hash).await?; let transaction_encoded = self.poller.poll_tx(hash).await?;
let tx_base64_decode = let tx_base64_decode = BASE64.decode(transaction_encoded)?;
base64::engine::general_purpose::STANDARD.decode(transaction_encoded)?;
let pub_tx = nssa::PublicTransaction::from_bytes(&tx_base64_decode)?; let pub_tx = nssa::PublicTransaction::from_bytes(&tx_base64_decode)?;
Ok(pub_tx) Ok(pub_tx)
@ -191,6 +196,11 @@ pub enum Command {
#[arg(short, long)] #[arg(short, long)]
addr: String, addr: String,
}, },
///Get account at address `addr`
GetAccount {
#[arg(short, long)]
addr: 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
@ -215,21 +225,19 @@ pub async fn execute_subcommand(command: Command) -> Result<()> {
.send_public_native_token_transfer(from, to, amount) .send_public_native_token_transfer(from, to, amount)
.await?; .await?;
info!("Results of tx send is {res:#?}"); println!("Results of tx send is {res:#?}");
let transfer_tx = wallet_core let transfer_tx = wallet_core
.poll_public_native_token_transfer(res.tx_hash) .poll_public_native_token_transfer(res.tx_hash)
.await?; .await?;
info!("Transaction data is {transfer_tx:?}"); println!("Transaction data is {transfer_tx:?}");
} }
Command::RegisterAccount {} => { Command::RegisterAccount {} => {
let addr = wallet_core.create_new_account(); let addr = wallet_core.create_new_account();
wallet_core.store_persistent_accounts()?;
let key = wallet_core.storage.user_data.get_account_signing_key(&addr); println!("Generated new account with addr {addr}");
info!("Generated new account with addr {addr:#?}");
info!("With key {key:#?}");
} }
Command::FetchTx { tx_hash } => { Command::FetchTx { tx_hash } => {
let tx_obj = wallet_core let tx_obj = wallet_core
@ -237,23 +245,26 @@ pub async fn execute_subcommand(command: Command) -> Result<()> {
.get_transaction_by_hash(tx_hash) .get_transaction_by_hash(tx_hash)
.await?; .await?;
info!("Transaction object {tx_obj:#?}"); println!("Transaction object {tx_obj:#?}");
} }
Command::GetAccountBalance { addr } => { Command::GetAccountBalance { addr } => {
let addr = Address::from_str(&addr)?; let addr = Address::from_str(&addr)?;
let balance = wallet_core.get_account_balance(addr).await?; let balance = wallet_core.get_account_balance(addr).await?;
info!("Accounts {addr:#?} balance is {balance}"); println!("Accounts {addr} balance is {balance}");
} }
Command::GetAccountNonce { addr } => { Command::GetAccountNonce { addr } => {
let addr = Address::from_str(&addr)?; let addr = Address::from_str(&addr)?;
let nonce = wallet_core.get_accounts_nonces(vec![addr]).await?[0]; let nonce = wallet_core.get_accounts_nonces(vec![addr]).await?[0];
info!("Accounts {addr:#?} nonce is {nonce}"); println!("Accounts {addr} nonce is {nonce}");
}
Command::GetAccount { addr } => {
let addr: Address = addr.parse()?;
let account: HumanReadableAccount = wallet_core.get_account(addr).await?.into();
println!("{}", serde_json::to_string(&account).unwrap());
} }
} }
wallet_core.store_persistent_accounts()?;
Ok(()) Ok(())
} }