mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-01-07 07:43:11 +00:00
fix: complete cli intefaces
This commit is contained in:
parent
0c7456d7a0
commit
914cbfb9dc
@ -43,6 +43,11 @@ pub struct GetTransactionByHashRequest {
|
||||
pub hash: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetAccountsNoncesRequest {
|
||||
pub addresses: Vec<String>,
|
||||
}
|
||||
|
||||
parse_request!(HelloRequest);
|
||||
parse_request!(RegisterAccountRequest);
|
||||
parse_request!(SendTxRequest);
|
||||
@ -52,6 +57,7 @@ parse_request!(GetLastBlockRequest);
|
||||
parse_request!(GetInitialTestnetAccountsRequest);
|
||||
parse_request!(GetAccountBalanceRequest);
|
||||
parse_request!(GetTransactionByHashRequest);
|
||||
parse_request!(GetAccountsNoncesRequest);
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct HelloResponse {
|
||||
@ -66,6 +72,7 @@ pub struct RegisterAccountResponse {
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct SendTxResponse {
|
||||
pub status: String,
|
||||
pub tx_hash: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
@ -88,6 +95,11 @@ pub struct GetAccountBalanceResponse {
|
||||
pub balance: u128,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetAccountsNoncesResponse {
|
||||
pub nonces: Vec<u128>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetTransactionByHashResponse {
|
||||
pub transaction: Option<String>,
|
||||
|
||||
@ -12,7 +12,7 @@ pub struct SendTxRequest {
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct SendTxResponse {
|
||||
pub status: String,
|
||||
pub additional_data: Option<String>,
|
||||
pub tx_hash: String,
|
||||
}
|
||||
|
||||
//General
|
||||
|
||||
@ -7,7 +7,10 @@ use json::{SendTxRequest, SendTxResponse, SequencerRpcRequest, SequencerRpcRespo
|
||||
use reqwest::Client;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::rpc_primitives::requests::{GetTransactionByHashRequest, GetTransactionByHashResponse};
|
||||
use crate::rpc_primitives::requests::{
|
||||
GetAccountsNoncesRequest, GetAccountsNoncesResponse, GetTransactionByHashRequest,
|
||||
GetTransactionByHashResponse,
|
||||
};
|
||||
use crate::sequencer_client::json::AccountInitialData;
|
||||
use crate::{SequencerClientError, SequencerRpcError};
|
||||
|
||||
@ -87,6 +90,42 @@ impl SequencerClient {
|
||||
Ok(resp_deser)
|
||||
}
|
||||
|
||||
///Get accounts nonces for `addresses`. `addresses` must be a list of valid hex-strings for 32 bytes.
|
||||
pub async fn get_accounts_nonces(
|
||||
&self,
|
||||
addresses: Vec<String>,
|
||||
) -> Result<GetAccountsNoncesResponse, SequencerClientError> {
|
||||
let block_req = GetAccountsNoncesRequest { addresses };
|
||||
|
||||
let req = serde_json::to_value(block_req)?;
|
||||
|
||||
let resp = self
|
||||
.call_method_with_payload("get_accounts_nonces", req)
|
||||
.await?;
|
||||
|
||||
let resp_deser = serde_json::from_value(resp)?;
|
||||
|
||||
Ok(resp_deser)
|
||||
}
|
||||
|
||||
///Get transaction details for `hash`.
|
||||
pub async fn get_transaction_by_hash(
|
||||
&self,
|
||||
hash: String,
|
||||
) -> Result<GetTransactionByHashResponse, SequencerClientError> {
|
||||
let block_req = GetTransactionByHashRequest { 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)
|
||||
}
|
||||
|
||||
///Send transaction to sequencer
|
||||
pub async fn send_tx(
|
||||
&self,
|
||||
@ -138,22 +177,4 @@ impl SequencerClient {
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,6 +116,12 @@ impl NSSAUserData {
|
||||
.and_modify(|(_, acc)| acc.balance = new_balance);
|
||||
}
|
||||
|
||||
pub fn increment_account_nonce(&mut self, address: nssa::Address) {
|
||||
self.accounts
|
||||
.entry(address)
|
||||
.and_modify(|(_, acc)| acc.nonce += 1);
|
||||
}
|
||||
|
||||
//ToDo: Part of a private keys update
|
||||
// pub fn make_tag(&self) -> Tag {
|
||||
// self.address.value()[0]
|
||||
|
||||
@ -12,7 +12,8 @@ use common::{
|
||||
message::{Message, Request},
|
||||
parser::RpcRequest,
|
||||
requests::{
|
||||
GetAccountBalanceRequest, GetAccountBalanceResponse, GetInitialTestnetAccountsRequest,
|
||||
GetAccountBalanceRequest, GetAccountBalanceResponse, GetAccountsNoncesRequest,
|
||||
GetAccountsNoncesResponse, GetInitialTestnetAccountsRequest,
|
||||
GetTransactionByHashRequest, GetTransactionByHashResponse,
|
||||
},
|
||||
},
|
||||
@ -33,10 +34,11 @@ pub const GET_GENESIS: &str = "get_genesis";
|
||||
pub const GET_LAST_BLOCK: &str = "get_last_block";
|
||||
pub const GET_ACCOUNT_BALANCE: &str = "get_account_balance";
|
||||
pub const GET_TRANSACTION_BY_HASH: &str = "get_transaction_by_hash";
|
||||
pub const GET_ACCOUNTS_NONCES: &str = "get_accounts_nonces";
|
||||
|
||||
pub const HELLO_FROM_SEQUENCER: &str = "HELLO_FROM_SEQUENCER";
|
||||
|
||||
pub const SUCCESS: &str = "Success";
|
||||
pub const TRANSACTION_SUBMITTED: &str = "Transaction submitted";
|
||||
|
||||
pub const GET_INITIAL_TESTNET_ACCOUNTS: &str = "get_initial_testnet_accounts";
|
||||
|
||||
@ -72,6 +74,7 @@ impl JsonHandler {
|
||||
let send_tx_req = SendTxRequest::parse(Some(request.params))?;
|
||||
let tx = nssa::PublicTransaction::from_bytes(&send_tx_req.transaction)
|
||||
.map_err(|e| RpcError::serialization_error(&e.to_string()))?;
|
||||
let tx_hash = hex::encode(tx.hash());
|
||||
|
||||
{
|
||||
let mut state = self.sequencer_state.lock().await;
|
||||
@ -80,7 +83,8 @@ impl JsonHandler {
|
||||
}
|
||||
|
||||
let helperstruct = SendTxResponse {
|
||||
status: SUCCESS.to_string(),
|
||||
status: TRANSACTION_SUBMITTED.to_string(),
|
||||
tx_hash,
|
||||
};
|
||||
|
||||
respond(helperstruct)
|
||||
@ -171,6 +175,38 @@ impl JsonHandler {
|
||||
respond(helperstruct)
|
||||
}
|
||||
|
||||
/// Returns the nonces of the accounts at the given addresses.
|
||||
/// Each address must be a valid hex string of the correct length.
|
||||
async fn process_get_accounts_nonces(&self, request: Request) -> Result<Value, RpcErr> {
|
||||
let get_account_nonces_req = GetAccountsNoncesRequest::parse(Some(request.params))?;
|
||||
let mut addresses = vec![];
|
||||
for address_raw in get_account_nonces_req.addresses {
|
||||
let address_bytes = hex::decode(address_raw)
|
||||
.map_err(|_| RpcError::invalid_params("invalid hex".to_string()))?;
|
||||
|
||||
let address = nssa::Address::new(
|
||||
address_bytes
|
||||
.try_into()
|
||||
.map_err(|_| RpcError::invalid_params("invalid length".to_string()))?,
|
||||
);
|
||||
|
||||
addresses.push(address);
|
||||
}
|
||||
|
||||
let nonces = {
|
||||
let state = self.sequencer_state.lock().await;
|
||||
|
||||
addresses
|
||||
.into_iter()
|
||||
.map(|addr| state.store.state.get_account_by_address(&addr).nonce)
|
||||
.collect()
|
||||
};
|
||||
|
||||
let helperstruct = GetAccountsNoncesResponse { nonces };
|
||||
|
||||
respond(helperstruct)
|
||||
}
|
||||
|
||||
/// Returns the transaction corresponding to the given hash, if it exists in the blockchain.
|
||||
/// The hash must be a valid hex string of the correct length.
|
||||
async fn process_get_transaction_by_hash(&self, request: Request) -> Result<Value, RpcErr> {
|
||||
@ -205,6 +241,7 @@ impl JsonHandler {
|
||||
GET_LAST_BLOCK => self.process_get_last_block(request).await,
|
||||
GET_INITIAL_TESTNET_ACCOUNTS => self.get_initial_testnet_accounts(request).await,
|
||||
GET_ACCOUNT_BALANCE => self.process_get_account_balance(request).await,
|
||||
GET_ACCOUNTS_NONCES => self.process_get_accounts_nonces(request).await,
|
||||
GET_TRANSACTION_BY_HASH => self.process_get_transaction_by_hash(request).await,
|
||||
_ => Err(RpcErr(RpcError::method_not_found(request.method))),
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@ hex.workspace = true
|
||||
actix-rt.workspace = true
|
||||
clap.workspace = true
|
||||
nssa-core = { path = "../nssa/core" }
|
||||
base64.workspace = true
|
||||
|
||||
[dependencies.key_protocol]
|
||||
path = "../key_protocol"
|
||||
|
||||
@ -78,7 +78,10 @@ mod tests {
|
||||
home,
|
||||
override_rust_log: None,
|
||||
sequencer_addr: "http://127.0.0.1".to_string(),
|
||||
seq_poll_timeout_secs: 1,
|
||||
seq_poll_timeout_millis: 12000,
|
||||
seq_poll_max_blocks: 5,
|
||||
seq_poll_max_retries: 10,
|
||||
seq_poll_retry_delay_millis: 500,
|
||||
initial_accounts: create_initial_accounts(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,8 +34,14 @@ pub struct WalletConfig {
|
||||
pub override_rust_log: Option<String>,
|
||||
///Sequencer URL
|
||||
pub sequencer_addr: String,
|
||||
///Sequencer polling duration for new blocks in seconds
|
||||
pub seq_poll_timeout_secs: u64,
|
||||
///Sequencer polling duration for new blocks in milliseconds
|
||||
pub seq_poll_timeout_millis: u64,
|
||||
///Sequencer polling max number of blocks
|
||||
pub seq_poll_max_blocks: usize,
|
||||
///Sequencer polling max number error retries
|
||||
pub seq_poll_max_retries: u64,
|
||||
///Sequencer polling error retry delay in milliseconds
|
||||
pub seq_poll_retry_delay_millis: u64,
|
||||
///Initial accounts for wallet
|
||||
pub initial_accounts: Vec<InitialAccountData>,
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use std::{fs::File, io::Write, path::PathBuf, sync::Arc};
|
||||
|
||||
use base64::Engine;
|
||||
use common::{
|
||||
sequencer_client::{json::SendTxResponse, SequencerClient},
|
||||
ExecutionFailureKind,
|
||||
@ -13,9 +14,12 @@ use nssa::Address;
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
use crate::helperfunctions::{
|
||||
fetch_config, fetch_persistent_accounts, get_home, produce_account_addr_from_hex,
|
||||
produce_data_for_storage,
|
||||
use crate::{
|
||||
helperfunctions::{
|
||||
fetch_config, fetch_persistent_accounts, get_home, produce_account_addr_from_hex,
|
||||
produce_data_for_storage,
|
||||
},
|
||||
poller::TxPoller,
|
||||
};
|
||||
|
||||
pub const HOME_DIR_ENV_VAR: &str = "NSSA_WALLET_HOME_DIR";
|
||||
@ -24,15 +28,24 @@ pub const BLOCK_GEN_DELAY_SECS: u64 = 20;
|
||||
pub mod chain_storage;
|
||||
pub mod config;
|
||||
pub mod helperfunctions;
|
||||
pub mod poller;
|
||||
|
||||
pub struct WalletCore {
|
||||
pub storage: WalletChainStore,
|
||||
pub poller: TxPoller,
|
||||
pub sequencer_client: Arc<SequencerClient>,
|
||||
}
|
||||
|
||||
impl WalletCore {
|
||||
pub async fn start_from_config_update_chain(config: WalletConfig) -> Result<Self> {
|
||||
let client = Arc::new(SequencerClient::new(config.sequencer_addr.clone())?);
|
||||
let tx_poller = TxPoller {
|
||||
polling_delay_millis: config.seq_poll_timeout_millis,
|
||||
polling_max_blocks_to_query: config.seq_poll_max_blocks,
|
||||
polling_max_error_attempts: config.seq_poll_max_retries,
|
||||
polling_error_delay_millis: config.seq_poll_retry_delay_millis,
|
||||
client: client.clone(),
|
||||
};
|
||||
|
||||
let mut storage = WalletChainStore::new(config)?;
|
||||
|
||||
@ -48,6 +61,7 @@ impl WalletCore {
|
||||
|
||||
Ok(Self {
|
||||
storage,
|
||||
poller: tx_poller,
|
||||
sequencer_client: client.clone(),
|
||||
})
|
||||
}
|
||||
@ -110,6 +124,38 @@ impl WalletCore {
|
||||
Err(ExecutionFailureKind::AmountMismatchError)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn poll_public_native_token_transfer(
|
||||
&self,
|
||||
hash: String,
|
||||
) -> Result<nssa::PublicTransaction> {
|
||||
let transaction_encoded = self.poller.poll_tx(hash).await?;
|
||||
let tx_base64_decode =
|
||||
base64::engine::general_purpose::STANDARD.decode(transaction_encoded)?;
|
||||
let pub_tx = nssa::PublicTransaction::from_bytes(&tx_base64_decode)?;
|
||||
|
||||
Ok(pub_tx)
|
||||
}
|
||||
|
||||
pub fn execute_native_token_transfer(
|
||||
&mut self,
|
||||
from: Address,
|
||||
to: Address,
|
||||
balance_to_move: u128,
|
||||
) {
|
||||
self.storage.user_data.increment_account_nonce(from);
|
||||
self.storage.user_data.increment_account_nonce(to);
|
||||
|
||||
let from_bal = self.storage.user_data.get_account_balance(&from);
|
||||
let to_bal = self.storage.user_data.get_account_balance(&to);
|
||||
|
||||
self.storage
|
||||
.user_data
|
||||
.update_account_balance(from, from_bal - balance_to_move);
|
||||
self.storage
|
||||
.user_data
|
||||
.update_account_balance(to, to_bal + balance_to_move);
|
||||
}
|
||||
}
|
||||
|
||||
///Represents CLI command for a wallet
|
||||
@ -169,7 +215,13 @@ pub async fn execute_subcommand(command: Command) -> Result<()> {
|
||||
|
||||
info!("Results of tx send is {res:#?}");
|
||||
|
||||
//ToDo: Insert transaction polling logic here
|
||||
let transfer_tx = wallet_core
|
||||
.poll_public_native_token_transfer(res.tx_hash)
|
||||
.await?;
|
||||
|
||||
info!("Transaction data is {transfer_tx:#?}");
|
||||
|
||||
wallet_core.execute_native_token_transfer(from, to, amount);
|
||||
}
|
||||
Command::RegisterAccount {} => {
|
||||
let addr = wallet_core.create_new_account();
|
||||
|
||||
55
wallet/src/poller.rs
Normal file
55
wallet/src/poller.rs
Normal file
@ -0,0 +1,55 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use common::sequencer_client::SequencerClient;
|
||||
use log::{info, warn};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TxPoller {
|
||||
pub polling_max_blocks_to_query: usize,
|
||||
pub polling_max_error_attempts: u64,
|
||||
pub polling_error_delay_millis: u64,
|
||||
pub polling_delay_millis: u64,
|
||||
pub client: Arc<SequencerClient>,
|
||||
}
|
||||
|
||||
impl TxPoller {
|
||||
pub async fn poll_tx(&self, tx_hash: String) -> Result<String> {
|
||||
let max_blocks_to_query = self.polling_max_blocks_to_query;
|
||||
|
||||
info!("Starting poll for transaction {tx_hash:#?}");
|
||||
for poll_id in 1..max_blocks_to_query {
|
||||
info!("Poll {poll_id}");
|
||||
|
||||
let mut try_error_counter = 0;
|
||||
|
||||
let tx_obj = loop {
|
||||
let tx_obj = self
|
||||
.client
|
||||
.get_transaction_by_hash(tx_hash.clone())
|
||||
.await
|
||||
.inspect_err(|err| {
|
||||
warn!("Failed to get transaction by hash {tx_hash:#?} with error: {err:#?}")
|
||||
});
|
||||
|
||||
if let Ok(tx_obj) = tx_obj {
|
||||
break tx_obj;
|
||||
} else {
|
||||
try_error_counter += 1;
|
||||
}
|
||||
|
||||
if try_error_counter > self.polling_max_error_attempts {
|
||||
anyhow::bail!("Number of retries exceeded");
|
||||
}
|
||||
};
|
||||
|
||||
if tx_obj.transaction.is_some() {
|
||||
return Ok(tx_obj.transaction.unwrap());
|
||||
}
|
||||
|
||||
tokio::time::sleep(std::time::Duration::from_millis(self.polling_delay_millis)).await;
|
||||
}
|
||||
|
||||
anyhow::bail!("Transaction not found in preconfigured amount of blocks");
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user