mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-01-04 06:13:10 +00:00
Merge branch 'main' into schouhy/add-pinata-program
This commit is contained in:
commit
e2494467ea
@ -1,5 +1,5 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "3"
|
||||||
members = [
|
members = [
|
||||||
"integration_tests",
|
"integration_tests",
|
||||||
"sequencer_runner",
|
"sequencer_runner",
|
||||||
@ -25,7 +25,7 @@ futures = "0.3"
|
|||||||
actix-rt = "*"
|
actix-rt = "*"
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
env_logger = "0.10"
|
env_logger = "0.10"
|
||||||
log = "0.4"
|
log = "0.4.28"
|
||||||
lru = "0.7.8"
|
lru = "0.7.8"
|
||||||
thiserror = "2.0.12"
|
thiserror = "2.0.12"
|
||||||
rs_merkle = "1.4"
|
rs_merkle = "1.4"
|
||||||
|
|||||||
@ -7,4 +7,5 @@ source env.sh
|
|||||||
RISC0_DEV_MODE=1 cargo test --release
|
RISC0_DEV_MODE=1 cargo test --release
|
||||||
cd integration_tests
|
cd integration_tests
|
||||||
export NSSA_WALLET_HOME_DIR=$(pwd)/configs/debug/wallet/
|
export NSSA_WALLET_HOME_DIR=$(pwd)/configs/debug/wallet/
|
||||||
|
export RUST_LOG=info
|
||||||
cargo run $(pwd)/configs/debug all
|
cargo run $(pwd)/configs/debug all
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "common"
|
name = "common"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
|
|||||||
@ -66,6 +66,8 @@ pub enum ExecutionFailureKind {
|
|||||||
ProveError(anyhow::Error),
|
ProveError(anyhow::Error),
|
||||||
#[error("Failed to decode data from VM: {0:?}")]
|
#[error("Failed to decode data from VM: {0:?}")]
|
||||||
DecodeError(String),
|
DecodeError(String),
|
||||||
|
#[error("Failed to get account data from sequencer")]
|
||||||
|
SequencerError,
|
||||||
#[error("Inputs amounts does not match outputs")]
|
#[error("Inputs amounts does not match outputs")]
|
||||||
AmountMismatchError,
|
AmountMismatchError,
|
||||||
#[error("Accounts key not found")]
|
#[error("Accounts key not found")]
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
use rs_merkle::Hasher;
|
use rs_merkle::Hasher;
|
||||||
use sha2::{digest::FixedOutput, Digest, Sha256};
|
use sha2::{Digest, Sha256, digest::FixedOutput};
|
||||||
|
|
||||||
use super::TreeHashType;
|
use super::TreeHashType;
|
||||||
|
|
||||||
|
|||||||
@ -2,14 +2,14 @@ use std::{collections::HashMap, fmt, marker::PhantomData};
|
|||||||
|
|
||||||
use rs_merkle::{MerkleProof, MerkleTree};
|
use rs_merkle::{MerkleProof, MerkleTree};
|
||||||
use serde::{
|
use serde::{
|
||||||
|
Deserialize, Deserializer, Serialize,
|
||||||
de::{SeqAccess, Visitor},
|
de::{SeqAccess, Visitor},
|
||||||
ser::SerializeSeq,
|
ser::SerializeSeq,
|
||||||
Deserialize, Deserializer, Serialize,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{transaction::Transaction, utxo_commitment::UTXOCommitment};
|
use crate::{transaction::Transaction, utxo_commitment::UTXOCommitment};
|
||||||
|
|
||||||
use super::{hasher::OwnHasher, tree_leav_item::TreeLeavItem, TreeHashType};
|
use super::{TreeHashType, hasher::OwnHasher, tree_leav_item::TreeLeavItem};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct HashStorageMerkleTree<Leav: TreeLeavItem + Clone> {
|
pub struct HashStorageMerkleTree<Leav: TreeLeavItem + Clone> {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use serde_json::{to_value, Value};
|
use serde_json::{Value, to_value};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(serde::Serialize)]
|
#[derive(serde::Serialize)]
|
||||||
@ -65,7 +65,7 @@ impl RpcError {
|
|||||||
return Self::server_error(Some(format!(
|
return Self::server_error(Some(format!(
|
||||||
"Failed to serialize invalid parameters error: {:?}",
|
"Failed to serialize invalid parameters error: {:?}",
|
||||||
err.to_string()
|
err.to_string()
|
||||||
)))
|
)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
RpcError::new(-32_602, "Invalid params".to_owned(), Some(value))
|
RpcError::new(-32_602, "Invalid params".to_owned(), Some(value))
|
||||||
@ -178,7 +178,7 @@ impl From<ServerError> for RpcError {
|
|||||||
let error_data = match to_value(&e) {
|
let error_data = match to_value(&e) {
|
||||||
Ok(value) => value,
|
Ok(value) => value,
|
||||||
Err(_err) => {
|
Err(_err) => {
|
||||||
return RpcError::new_internal_error(None, "Failed to serialize ServerError")
|
return RpcError::new_internal_error(None, "Failed to serialize ServerError");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
RpcError::new_internal_error(Some(error_data), e.to_string().as_str())
|
RpcError::new_internal_error(Some(error_data), e.to_string().as_str())
|
||||||
|
|||||||
@ -315,10 +315,10 @@ impl From<Message> for Vec<u8> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use serde_json::Value;
|
||||||
use serde_json::de::from_slice;
|
use serde_json::de::from_slice;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use serde_json::ser::to_vec;
|
use serde_json::ser::to_vec;
|
||||||
use serde_json::Value;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
use crate::parse_request;
|
use crate::parse_request;
|
||||||
|
|
||||||
use super::errors::RpcParseError;
|
use super::errors::RpcParseError;
|
||||||
use super::parser::parse_params;
|
|
||||||
use super::parser::RpcRequest;
|
use super::parser::RpcRequest;
|
||||||
|
use super::parser::parse_params;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
@ -43,6 +43,16 @@ pub struct GetTransactionByHashRequest {
|
|||||||
pub hash: String,
|
pub hash: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct GetAccountsNoncesRequest {
|
||||||
|
pub addresses: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct GetAccountDataRequest {
|
||||||
|
pub address: String,
|
||||||
|
}
|
||||||
|
|
||||||
parse_request!(HelloRequest);
|
parse_request!(HelloRequest);
|
||||||
parse_request!(RegisterAccountRequest);
|
parse_request!(RegisterAccountRequest);
|
||||||
parse_request!(SendTxRequest);
|
parse_request!(SendTxRequest);
|
||||||
@ -52,6 +62,8 @@ parse_request!(GetLastBlockRequest);
|
|||||||
parse_request!(GetInitialTestnetAccountsRequest);
|
parse_request!(GetInitialTestnetAccountsRequest);
|
||||||
parse_request!(GetAccountBalanceRequest);
|
parse_request!(GetAccountBalanceRequest);
|
||||||
parse_request!(GetTransactionByHashRequest);
|
parse_request!(GetTransactionByHashRequest);
|
||||||
|
parse_request!(GetAccountsNoncesRequest);
|
||||||
|
parse_request!(GetAccountDataRequest);
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct HelloResponse {
|
pub struct HelloResponse {
|
||||||
@ -66,6 +78,7 @@ pub struct RegisterAccountResponse {
|
|||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct SendTxResponse {
|
pub struct SendTxResponse {
|
||||||
pub status: String,
|
pub status: String,
|
||||||
|
pub tx_hash: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
@ -88,7 +101,20 @@ pub struct GetAccountBalanceResponse {
|
|||||||
pub balance: u128,
|
pub balance: u128,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct GetAccountsNoncesResponse {
|
||||||
|
pub nonces: Vec<u128>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct GetTransactionByHashResponse {
|
pub struct GetTransactionByHashResponse {
|
||||||
pub transaction: Option<String>,
|
pub transaction: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct GetAccountDataResponse {
|
||||||
|
pub balance: u128,
|
||||||
|
pub nonce: u128,
|
||||||
|
pub program_owner: [u32; 8],
|
||||||
|
pub data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@ pub struct SendTxRequest {
|
|||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct SendTxResponse {
|
pub struct SendTxResponse {
|
||||||
pub status: String,
|
pub status: String,
|
||||||
pub additional_data: Option<String>,
|
pub tx_hash: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
//General
|
//General
|
||||||
|
|||||||
@ -7,6 +7,10 @@ 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::{
|
||||||
|
GetAccountsNoncesRequest, GetAccountsNoncesResponse, GetTransactionByHashRequest,
|
||||||
|
GetTransactionByHashResponse,
|
||||||
|
};
|
||||||
use crate::sequencer_client::json::AccountInitialData;
|
use crate::sequencer_client::json::AccountInitialData;
|
||||||
use crate::{SequencerClientError, SequencerRpcError};
|
use crate::{SequencerClientError, SequencerRpcError};
|
||||||
|
|
||||||
@ -86,6 +90,42 @@ impl SequencerClient {
|
|||||||
Ok(resp_deser)
|
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
|
///Send transaction to sequencer
|
||||||
pub async fn send_tx(
|
pub async fn send_tx(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
use k256::ecdsa::{
|
use k256::ecdsa::{
|
||||||
signature::{Signer, Verifier},
|
|
||||||
Signature, SigningKey, VerifyingKey,
|
Signature, SigningKey, VerifyingKey,
|
||||||
|
signature::{Signer, Verifier},
|
||||||
};
|
};
|
||||||
use log::info;
|
use log::info;
|
||||||
use secp256k1_zkp::{PedersenCommitment, Tweak};
|
use secp256k1_zkp::{PedersenCommitment, Tweak};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use sha2::{digest::FixedOutput, Digest};
|
use sha2::{Digest, digest::FixedOutput};
|
||||||
|
|
||||||
use crate::merkle_tree_public::TreeHashType;
|
use crate::merkle_tree_public::TreeHashType;
|
||||||
|
|
||||||
@ -297,9 +297,9 @@ impl AuthenticatedTransaction {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use k256::{ecdsa::signature::Signer, FieldBytes};
|
use k256::{FieldBytes, ecdsa::signature::Signer};
|
||||||
use secp256k1_zkp::{constants::SECRET_KEY_SIZE, Tweak};
|
use secp256k1_zkp::{Tweak, constants::SECRET_KEY_SIZE};
|
||||||
use sha2::{digest::FixedOutput, Digest};
|
use sha2::{Digest, digest::FixedOutput};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
merkle_tree_public::TreeHashType,
|
merkle_tree_public::TreeHashType,
|
||||||
@ -378,11 +378,13 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(authenticated_tx.transaction(), &transaction);
|
assert_eq!(authenticated_tx.transaction(), &transaction);
|
||||||
assert_eq!(hash, &transaction.body.hash());
|
assert_eq!(hash, &transaction.body.hash());
|
||||||
assert!(authenticated_tx
|
assert!(
|
||||||
.transaction()
|
authenticated_tx
|
||||||
.public_key
|
.transaction()
|
||||||
.verify(&transaction.body.to_bytes(), &signature)
|
.public_key
|
||||||
.is_ok());
|
.verify(&transaction.body.to_bytes(), &signature)
|
||||||
|
.is_ok()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -2,7 +2,10 @@
|
|||||||
"home": "./node",
|
"home": "./node",
|
||||||
"override_rust_log": null,
|
"override_rust_log": null,
|
||||||
"sequencer_addr": "http://127.0.0.1:3040",
|
"sequencer_addr": "http://127.0.0.1:3040",
|
||||||
"seq_poll_timeout_secs": 10,
|
"seq_poll_timeout_millis": 12000,
|
||||||
|
"seq_poll_max_blocks": 5,
|
||||||
|
"seq_poll_max_retries": 5,
|
||||||
|
"seq_poll_retry_delay_millis": 500,
|
||||||
"initial_accounts": [
|
"initial_accounts": [
|
||||||
{
|
{
|
||||||
"address": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f",
|
"address": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f",
|
||||||
|
|||||||
@ -4,12 +4,15 @@ use actix_web::dev::ServerHandle;
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use common::sequencer_client::SequencerClient;
|
use common::sequencer_client::SequencerClient;
|
||||||
use log::info;
|
use log::{info, warn};
|
||||||
use sequencer_core::config::SequencerConfig;
|
use sequencer_core::config::SequencerConfig;
|
||||||
use sequencer_runner::startup_sequencer;
|
use sequencer_runner::startup_sequencer;
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use wallet::{Command, helperfunctions::fetch_config};
|
use wallet::{
|
||||||
|
Command,
|
||||||
|
helperfunctions::{fetch_config, fetch_persistent_accounts},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[clap(version)]
|
#[clap(version)]
|
||||||
@ -66,6 +69,14 @@ pub async fn post_test(residual: (ServerHandle, JoinHandle<Result<()>>, TempDir)
|
|||||||
sequencer_loop_handle.abort();
|
sequencer_loop_handle.abort();
|
||||||
seq_http_server_handle.stop(true).await;
|
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");
|
||||||
|
|
||||||
|
//Removing persistent accounts after run to not affect other executions
|
||||||
|
//Not necessary an error, if fails as there is tests for failure scenario
|
||||||
|
let _ = std::fs::remove_file(persistent_data_home)
|
||||||
|
.inspect_err(|err| warn!("Failed to remove persistent data with err {err:#?}"));
|
||||||
|
|
||||||
//At this point all of the references to sequencer_core must be lost.
|
//At this point all of the references to sequencer_core must be lost.
|
||||||
//So they are dropped and tempdirs will be dropped too,
|
//So they are dropped and tempdirs will be dropped too,
|
||||||
}
|
}
|
||||||
@ -73,7 +84,6 @@ pub async fn post_test(residual: (ServerHandle, JoinHandle<Result<()>>, TempDir)
|
|||||||
pub async fn test_success() {
|
pub async fn test_success() {
|
||||||
let command = Command::SendNativeTokenTransfer {
|
let command = Command::SendNativeTokenTransfer {
|
||||||
from: ACC_SENDER.to_string(),
|
from: ACC_SENDER.to_string(),
|
||||||
nonce: 0,
|
|
||||||
to: ACC_RECEIVER.to_string(),
|
to: ACC_RECEIVER.to_string(),
|
||||||
amount: 100,
|
amount: 100,
|
||||||
};
|
};
|
||||||
@ -107,14 +117,7 @@ pub async fn test_success() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn test_success_move_to_another_account() {
|
pub async fn test_success_move_to_another_account() {
|
||||||
let hex_acc_receiver_new_acc = hex::encode([42; 32]);
|
let command = Command::RegisterAccount {};
|
||||||
|
|
||||||
let command = Command::SendNativeTokenTransfer {
|
|
||||||
from: ACC_SENDER.to_string(),
|
|
||||||
nonce: 0,
|
|
||||||
to: hex_acc_receiver_new_acc.clone(),
|
|
||||||
amount: 100,
|
|
||||||
};
|
|
||||||
|
|
||||||
let wallet_config = fetch_config().unwrap();
|
let wallet_config = fetch_config().unwrap();
|
||||||
|
|
||||||
@ -122,6 +125,30 @@ pub async fn test_success_move_to_another_account() {
|
|||||||
|
|
||||||
wallet::execute_subcommand(command).await.unwrap();
|
wallet::execute_subcommand(command).await.unwrap();
|
||||||
|
|
||||||
|
let persistent_accounts = fetch_persistent_accounts().unwrap();
|
||||||
|
|
||||||
|
let mut new_persistent_account_addr = String::new();
|
||||||
|
|
||||||
|
for per_acc in persistent_accounts {
|
||||||
|
if (per_acc.address.to_string() != ACC_RECEIVER)
|
||||||
|
&& (per_acc.address.to_string() != ACC_SENDER)
|
||||||
|
{
|
||||||
|
new_persistent_account_addr = per_acc.address.to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if new_persistent_account_addr == String::new() {
|
||||||
|
panic!("Failed to produce new account, not present in persistent accounts");
|
||||||
|
}
|
||||||
|
|
||||||
|
let command = Command::SendNativeTokenTransfer {
|
||||||
|
from: ACC_SENDER.to_string(),
|
||||||
|
to: new_persistent_account_addr.clone(),
|
||||||
|
amount: 100,
|
||||||
|
};
|
||||||
|
|
||||||
|
wallet::execute_subcommand(command).await.unwrap();
|
||||||
|
|
||||||
info!("Waiting for next block creation");
|
info!("Waiting for next block creation");
|
||||||
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
||||||
|
|
||||||
@ -131,7 +158,7 @@ pub async fn test_success_move_to_another_account() {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let acc_2_balance = seq_client
|
let acc_2_balance = seq_client
|
||||||
.get_account_balance(hex_acc_receiver_new_acc)
|
.get_account_balance(new_persistent_account_addr)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -147,7 +174,6 @@ pub async fn test_success_move_to_another_account() {
|
|||||||
pub async fn test_failure() {
|
pub async fn test_failure() {
|
||||||
let command = Command::SendNativeTokenTransfer {
|
let command = Command::SendNativeTokenTransfer {
|
||||||
from: ACC_SENDER.to_string(),
|
from: ACC_SENDER.to_string(),
|
||||||
nonce: 0,
|
|
||||||
to: ACC_RECEIVER.to_string(),
|
to: ACC_RECEIVER.to_string(),
|
||||||
amount: 1000000,
|
amount: 1000000,
|
||||||
};
|
};
|
||||||
@ -185,7 +211,6 @@ pub async fn test_failure() {
|
|||||||
pub async fn test_success_two_transactions() {
|
pub async fn test_success_two_transactions() {
|
||||||
let command = Command::SendNativeTokenTransfer {
|
let command = Command::SendNativeTokenTransfer {
|
||||||
from: ACC_SENDER.to_string(),
|
from: ACC_SENDER.to_string(),
|
||||||
nonce: 0,
|
|
||||||
to: ACC_RECEIVER.to_string(),
|
to: ACC_RECEIVER.to_string(),
|
||||||
amount: 100,
|
amount: 100,
|
||||||
};
|
};
|
||||||
@ -219,7 +244,6 @@ pub async fn test_success_two_transactions() {
|
|||||||
|
|
||||||
let command = Command::SendNativeTokenTransfer {
|
let command = Command::SendNativeTokenTransfer {
|
||||||
from: ACC_SENDER.to_string(),
|
from: ACC_SENDER.to_string(),
|
||||||
nonce: 1,
|
|
||||||
to: ACC_RECEIVER.to_string(),
|
to: ACC_RECEIVER.to_string(),
|
||||||
amount: 100,
|
amount: 100,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "key_protocol"
|
name = "key_protocol"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
use aes_gcm::{aead::Aead, AeadCore, Aes256Gcm, KeyInit};
|
use aes_gcm::{AeadCore, Aes256Gcm, KeyInit, aead::Aead};
|
||||||
use elliptic_curve::point::AffineCoordinates;
|
|
||||||
use elliptic_curve::PrimeField;
|
use elliptic_curve::PrimeField;
|
||||||
|
use elliptic_curve::point::AffineCoordinates;
|
||||||
use k256::{AffinePoint, FieldBytes, Scalar};
|
use k256::{AffinePoint, FieldBytes, Scalar};
|
||||||
use log::info;
|
use log::info;
|
||||||
use rand::{rngs::OsRng, RngCore};
|
use rand::{RngCore, rngs::OsRng};
|
||||||
|
|
||||||
use super::constants_types::{CipherText, Nonce};
|
use super::constants_types::{CipherText, Nonce};
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit};
|
use aes_gcm::{Aes256Gcm, KeyInit, aead::Aead};
|
||||||
use constants_types::{CipherText, Nonce};
|
use constants_types::{CipherText, Nonce};
|
||||||
use elliptic_curve::point::AffineCoordinates;
|
use elliptic_curve::point::AffineCoordinates;
|
||||||
use k256::AffinePoint;
|
use k256::AffinePoint;
|
||||||
@ -21,7 +21,7 @@ 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
|
///Map for all users accounts
|
||||||
pub_account_signing_keys: HashMap<nssa::Address, nssa::PrivateKey>,
|
pub 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,
|
||||||
}
|
}
|
||||||
@ -136,8 +136,8 @@ impl KeyChain {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use aes_gcm::{
|
use aes_gcm::{
|
||||||
aead::{Aead, KeyInit, OsRng},
|
|
||||||
Aes256Gcm,
|
Aes256Gcm,
|
||||||
|
aead::{Aead, KeyInit, OsRng},
|
||||||
};
|
};
|
||||||
use constants_types::{CipherText, Nonce};
|
use constants_types::{CipherText, Nonce};
|
||||||
use constants_types::{NULLIFIER_SECRET_CONST, VIEWING_SECRET_CONST};
|
use constants_types::{NULLIFIER_SECRET_CONST, VIEWING_SECRET_CONST};
|
||||||
@ -341,19 +341,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();
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
use common::merkle_tree_public::TreeHashType;
|
use common::merkle_tree_public::TreeHashType;
|
||||||
use elliptic_curve::PrimeField;
|
use elliptic_curve::PrimeField;
|
||||||
use k256::{AffinePoint, FieldBytes, Scalar};
|
use k256::{AffinePoint, FieldBytes, Scalar};
|
||||||
use rand::{rngs::OsRng, RngCore};
|
use rand::{RngCore, rngs::OsRng};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sha2::{digest::FixedOutput, Digest};
|
use sha2::{Digest, digest::FixedOutput};
|
||||||
|
|
||||||
use super::constants_types::{NULLIFIER_SECRET_CONST, VIEWING_SECRET_CONST};
|
use super::constants_types::{NULLIFIER_SECRET_CONST, VIEWING_SECRET_CONST};
|
||||||
|
|
||||||
|
|||||||
@ -36,7 +36,9 @@ impl NSSAUserData {
|
|||||||
accounts_keys: HashMap<nssa::Address, nssa::PrivateKey>,
|
accounts_keys: HashMap<nssa::Address, nssa::PrivateKey>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
if !Self::valid_key_transaction_pairing_check(&accounts_keys) {
|
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");
|
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);
|
let key_holder = KeyChain::new_os_random_with_accounts(accounts_keys);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mempool"
|
name = "mempool"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "program-methods"
|
name = "program-methods"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
risc0-build = { version = "3.0.3" }
|
risc0-build = { version = "3.0.3" }
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "programs"
|
name = "programs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "test-program-methods"
|
name = "test-program-methods"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
risc0-build = { version = "3.0.3" }
|
risc0-build = { version = "3.0.3" }
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "programs"
|
name = "programs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
|
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "sequencer_core"
|
name = "sequencer_core"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
hex.workspace = true
|
hex.workspace = true
|
||||||
@ -9,6 +9,7 @@ anyhow.workspace = true
|
|||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
rand.workspace = true
|
rand.workspace = true
|
||||||
tempfile.workspace = true
|
tempfile.workspace = true
|
||||||
|
log.workspace = true
|
||||||
|
|
||||||
[dependencies.storage]
|
[dependencies.storage]
|
||||||
path = "../storage"
|
path = "../storage"
|
||||||
|
|||||||
@ -3,6 +3,7 @@ use std::fmt::Display;
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use common::{block::HashableBlockData, merkle_tree_public::TreeHashType};
|
use common::{block::HashableBlockData, merkle_tree_public::TreeHashType};
|
||||||
use config::SequencerConfig;
|
use config::SequencerConfig;
|
||||||
|
use log::warn;
|
||||||
use mempool::MemPool;
|
use mempool::MemPool;
|
||||||
use sequencer_store::SequecerChainStore;
|
use sequencer_store::SequecerChainStore;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -78,7 +79,9 @@ impl SequencerCore {
|
|||||||
return Err(TransactionMalformationErrorKind::MempoolFullForRound);
|
return Err(TransactionMalformationErrorKind::MempoolFullForRound);
|
||||||
}
|
}
|
||||||
|
|
||||||
let authenticated_tx = self.transaction_pre_check(transaction)?;
|
let authenticated_tx = self
|
||||||
|
.transaction_pre_check(transaction)
|
||||||
|
.inspect_err(|err| warn!("Error at pre_check {err:#?}"))?;
|
||||||
|
|
||||||
self.mempool.push_item(authenticated_tx);
|
self.mempool.push_item(authenticated_tx);
|
||||||
|
|
||||||
@ -89,7 +92,10 @@ impl SequencerCore {
|
|||||||
&mut self,
|
&mut self,
|
||||||
tx: nssa::PublicTransaction,
|
tx: nssa::PublicTransaction,
|
||||||
) -> Result<nssa::PublicTransaction, nssa::error::NssaError> {
|
) -> Result<nssa::PublicTransaction, nssa::error::NssaError> {
|
||||||
self.store.state.transition_from_public_transaction(&tx)?;
|
self.store
|
||||||
|
.state
|
||||||
|
.transition_from_public_transaction(&tx)
|
||||||
|
.inspect_err(|err| warn!("Error at transition {err:#?}"))?;
|
||||||
|
|
||||||
Ok(tx)
|
Ok(tx)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ use std::path::Path;
|
|||||||
use block_store::SequecerBlockStore;
|
use block_store::SequecerBlockStore;
|
||||||
use common::block::HashableBlockData;
|
use common::block::HashableBlockData;
|
||||||
use nssa::{self, Address};
|
use nssa::{self, Address};
|
||||||
use rand::{rngs::OsRng, RngCore};
|
use rand::{RngCore, rngs::OsRng};
|
||||||
|
|
||||||
use crate::config::AccountInitialData;
|
use crate::config::AccountInitialData;
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "sequencer_rpc"
|
name = "sequencer_rpc"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
|
|||||||
@ -5,8 +5,8 @@ pub mod types;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use common::rpc_primitives::{
|
use common::rpc_primitives::{
|
||||||
errors::{RpcError, RpcErrorKind},
|
|
||||||
RpcPollingConfig,
|
RpcPollingConfig,
|
||||||
|
errors::{RpcError, RpcErrorKind},
|
||||||
};
|
};
|
||||||
use sequencer_core::SequencerCore;
|
use sequencer_core::SequencerCore;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|||||||
@ -2,13 +2,13 @@ use std::io;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use actix_cors::Cors;
|
use actix_cors::Cors;
|
||||||
use actix_web::{http, middleware, web, App, Error as HttpError, HttpResponse, HttpServer};
|
use actix_web::{App, Error as HttpError, HttpResponse, HttpServer, http, middleware, web};
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
use common::rpc_primitives::message::Message;
|
|
||||||
use common::rpc_primitives::RpcConfig;
|
use common::rpc_primitives::RpcConfig;
|
||||||
|
use common::rpc_primitives::message::Message;
|
||||||
use sequencer_core::SequencerCore;
|
use sequencer_core::SequencerCore;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
use actix_web::Error as HttpError;
|
use actix_web::Error as HttpError;
|
||||||
use base64::{engine::general_purpose, Engine};
|
use base64::{Engine, engine::general_purpose};
|
||||||
use nssa;
|
use nssa;
|
||||||
use sequencer_core::config::AccountInitialData;
|
use sequencer_core::config::AccountInitialData;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
@ -12,8 +12,10 @@ use common::{
|
|||||||
message::{Message, Request},
|
message::{Message, Request},
|
||||||
parser::RpcRequest,
|
parser::RpcRequest,
|
||||||
requests::{
|
requests::{
|
||||||
GetAccountBalanceRequest, GetAccountBalanceResponse, GetInitialTestnetAccountsRequest,
|
GetAccountBalanceRequest, GetAccountBalanceResponse, GetAccountDataRequest,
|
||||||
GetTransactionByHashRequest, GetTransactionByHashResponse,
|
GetAccountDataResponse, GetAccountsNoncesRequest, GetAccountsNoncesResponse,
|
||||||
|
GetInitialTestnetAccountsRequest, GetTransactionByHashRequest,
|
||||||
|
GetTransactionByHashResponse,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -24,7 +26,7 @@ use common::rpc_primitives::requests::{
|
|||||||
SendTxResponse,
|
SendTxResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{respond, types::err_rpc::RpcErr, JsonHandler};
|
use super::{JsonHandler, respond, types::err_rpc::RpcErr};
|
||||||
|
|
||||||
pub const HELLO: &str = "hello";
|
pub const HELLO: &str = "hello";
|
||||||
pub const SEND_TX: &str = "send_tx";
|
pub const SEND_TX: &str = "send_tx";
|
||||||
@ -33,10 +35,12 @@ pub const GET_GENESIS: &str = "get_genesis";
|
|||||||
pub const GET_LAST_BLOCK: &str = "get_last_block";
|
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_ACCOUNT_DATA: &str = "get_account_data";
|
||||||
|
|
||||||
pub const HELLO_FROM_SEQUENCER: &str = "HELLO_FROM_SEQUENCER";
|
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";
|
pub const GET_INITIAL_TESTNET_ACCOUNTS: &str = "get_initial_testnet_accounts";
|
||||||
|
|
||||||
@ -72,6 +76,7 @@ impl JsonHandler {
|
|||||||
let send_tx_req = SendTxRequest::parse(Some(request.params))?;
|
let send_tx_req = SendTxRequest::parse(Some(request.params))?;
|
||||||
let tx = nssa::PublicTransaction::from_bytes(&send_tx_req.transaction)
|
let tx = nssa::PublicTransaction::from_bytes(&send_tx_req.transaction)
|
||||||
.map_err(|e| RpcError::serialization_error(&e.to_string()))?;
|
.map_err(|e| RpcError::serialization_error(&e.to_string()))?;
|
||||||
|
let tx_hash = hex::encode(tx.hash());
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut state = self.sequencer_state.lock().await;
|
let mut state = self.sequencer_state.lock().await;
|
||||||
@ -80,7 +85,8 @@ impl JsonHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let helperstruct = SendTxResponse {
|
let helperstruct = SendTxResponse {
|
||||||
status: SUCCESS.to_string(),
|
status: TRANSACTION_SUBMITTED.to_string(),
|
||||||
|
tx_hash,
|
||||||
};
|
};
|
||||||
|
|
||||||
respond(helperstruct)
|
respond(helperstruct)
|
||||||
@ -171,6 +177,59 @@ impl JsonHandler {
|
|||||||
respond(helperstruct)
|
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 = address_raw
|
||||||
|
.parse::<nssa::Address>()
|
||||||
|
.map_err(|e| RpcError::invalid_params(e.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 account struct for give address.
|
||||||
|
/// Address must be a valid hex string of the correct length.
|
||||||
|
async fn process_get_account_data(&self, request: Request) -> Result<Value, RpcErr> {
|
||||||
|
let get_account_nonces_req = GetAccountDataRequest::parse(Some(request.params))?;
|
||||||
|
|
||||||
|
let address = get_account_nonces_req
|
||||||
|
.address
|
||||||
|
.parse::<nssa::Address>()
|
||||||
|
.map_err(|e| RpcError::invalid_params(e.to_string()))?;
|
||||||
|
|
||||||
|
let account = {
|
||||||
|
let state = self.sequencer_state.lock().await;
|
||||||
|
|
||||||
|
state.store.state.get_account_by_address(&address)
|
||||||
|
};
|
||||||
|
|
||||||
|
let helperstruct = GetAccountDataResponse {
|
||||||
|
balance: account.balance,
|
||||||
|
nonce: account.nonce,
|
||||||
|
program_owner: account.program_owner,
|
||||||
|
data: account.data,
|
||||||
|
};
|
||||||
|
|
||||||
|
respond(helperstruct)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the transaction corresponding to the given hash, if it exists in the blockchain.
|
/// 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.
|
/// 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> {
|
async fn process_get_transaction_by_hash(&self, request: Request) -> Result<Value, RpcErr> {
|
||||||
@ -205,6 +264,8 @@ impl JsonHandler {
|
|||||||
GET_LAST_BLOCK => self.process_get_last_block(request).await,
|
GET_LAST_BLOCK => self.process_get_last_block(request).await,
|
||||||
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_ACCOUNT_DATA => self.process_get_account_data(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))),
|
||||||
}
|
}
|
||||||
@ -215,13 +276,13 @@ impl JsonHandler {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::{rpc_handler, JsonHandler};
|
use crate::{JsonHandler, rpc_handler};
|
||||||
use base64::{engine::general_purpose, Engine};
|
use base64::{Engine, engine::general_purpose};
|
||||||
use common::rpc_primitives::RpcPollingConfig;
|
use common::rpc_primitives::RpcPollingConfig;
|
||||||
|
|
||||||
use sequencer_core::{
|
use sequencer_core::{
|
||||||
config::{AccountInitialData, SequencerConfig},
|
|
||||||
SequencerCore,
|
SequencerCore,
|
||||||
|
config::{AccountInitialData, SequencerConfig},
|
||||||
};
|
};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
@ -307,7 +368,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn call_rpc_handler_with_json(handler: JsonHandler, request_json: Value) -> Value {
|
async fn call_rpc_handler_with_json(handler: JsonHandler, request_json: Value) -> Value {
|
||||||
use actix_web::{test, web, App};
|
use actix_web::{App, test, web};
|
||||||
|
|
||||||
let app = test::init_service(
|
let app = test::init_service(
|
||||||
App::new()
|
App::new()
|
||||||
@ -420,6 +481,79 @@ mod tests {
|
|||||||
assert_eq!(response, expected_response);
|
assert_eq!(response, expected_response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[actix_web::test]
|
||||||
|
async fn test_get_accounts_nonces_for_non_existent_account() {
|
||||||
|
let (json_handler, _, _) = components_for_tests();
|
||||||
|
let request = serde_json::json!({
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "get_accounts_nonces",
|
||||||
|
"params": { "addresses": ["efac".repeat(16)] },
|
||||||
|
"id": 1
|
||||||
|
});
|
||||||
|
let expected_response = serde_json::json!({
|
||||||
|
"id": 1,
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"result": {
|
||||||
|
"nonces": [ 0 ]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = call_rpc_handler_with_json(json_handler, request).await;
|
||||||
|
|
||||||
|
assert_eq!(response, expected_response);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_web::test]
|
||||||
|
async fn test_get_accounts_nonces_for_existent_account() {
|
||||||
|
let (json_handler, initial_accounts, _) = components_for_tests();
|
||||||
|
|
||||||
|
let acc_1_addr = initial_accounts[0].addr.clone();
|
||||||
|
let acc_2_addr = initial_accounts[1].addr.clone();
|
||||||
|
|
||||||
|
let request = serde_json::json!({
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "get_accounts_nonces",
|
||||||
|
"params": { "addresses": [acc_1_addr, acc_2_addr] },
|
||||||
|
"id": 1
|
||||||
|
});
|
||||||
|
let expected_response = serde_json::json!({
|
||||||
|
"id": 1,
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"result": {
|
||||||
|
"nonces": [ 1, 0 ]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = call_rpc_handler_with_json(json_handler, request).await;
|
||||||
|
|
||||||
|
assert_eq!(response, expected_response);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_web::test]
|
||||||
|
async fn test_get_account_data_for_non_existent_account() {
|
||||||
|
let (json_handler, _, _) = components_for_tests();
|
||||||
|
let request = serde_json::json!({
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "get_account_data",
|
||||||
|
"params": { "address": "efac".repeat(16) },
|
||||||
|
"id": 1
|
||||||
|
});
|
||||||
|
let expected_response = serde_json::json!({
|
||||||
|
"id": 1,
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"result": {
|
||||||
|
"balance": 0,
|
||||||
|
"nonce": 0,
|
||||||
|
"program_owner": [ 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
"data": [],
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let response = call_rpc_handler_with_json(json_handler, request).await;
|
||||||
|
|
||||||
|
assert_eq!(response, expected_response);
|
||||||
|
}
|
||||||
|
|
||||||
#[actix_web::test]
|
#[actix_web::test]
|
||||||
async fn test_get_transaction_by_hash_for_non_existent_hash() {
|
async fn test_get_transaction_by_hash_for_non_existent_hash() {
|
||||||
let (json_handler, _, _) = components_for_tests();
|
let (json_handler, _, _) = components_for_tests();
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "sequencer_runner"
|
name = "sequencer_runner"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
|
|||||||
@ -5,7 +5,7 @@ use anyhow::Result;
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use common::rpc_primitives::RpcConfig;
|
use common::rpc_primitives::RpcConfig;
|
||||||
use log::info;
|
use log::info;
|
||||||
use sequencer_core::{config::SequencerConfig, SequencerCore};
|
use sequencer_core::{SequencerCore, config::SequencerConfig};
|
||||||
use sequencer_rpc::new_http_server;
|
use sequencer_rpc::new_http_server;
|
||||||
use tokio::{sync::Mutex, task::JoinHandle};
|
use tokio::{sync::Mutex, task::JoinHandle};
|
||||||
|
|
||||||
@ -71,7 +71,9 @@ pub async fn main_runner() -> Result<()> {
|
|||||||
if let Some(ref rust_log) = app_config.override_rust_log {
|
if let Some(ref rust_log) = app_config.override_rust_log {
|
||||||
info!("RUST_LOG env var set to {rust_log:?}");
|
info!("RUST_LOG env var set to {rust_log:?}");
|
||||||
|
|
||||||
std::env::set_var(RUST_LOG, rust_log);
|
unsafe {
|
||||||
|
std::env::set_var(RUST_LOG, rust_log);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//ToDo: Add restart on failures
|
//ToDo: Add restart on failures
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "storage"
|
name = "storage"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
|
|||||||
@ -5,7 +5,7 @@ use error::DbError;
|
|||||||
use rocksdb::{
|
use rocksdb::{
|
||||||
BoundColumnFamily, ColumnFamilyDescriptor, DBWithThreadMode, MultiThreaded, Options,
|
BoundColumnFamily, ColumnFamilyDescriptor, DBWithThreadMode, MultiThreaded, Options,
|
||||||
};
|
};
|
||||||
use sc_db_utils::{produce_blob_from_fit_vec, DataBlob, DataBlobChangeVariant};
|
use sc_db_utils::{DataBlob, DataBlobChangeVariant, produce_blob_from_fit_vec};
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod sc_db_utils;
|
pub mod sc_db_utils;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use serde::{de::Error, Deserialize, Serialize};
|
use serde::{Deserialize, Serialize, de::Error};
|
||||||
|
|
||||||
use crate::SC_DATA_BLOB_SIZE;
|
use crate::SC_DATA_BLOB_SIZE;
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wallet"
|
name = "wallet"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
@ -13,6 +13,7 @@ tokio.workspace = true
|
|||||||
tempfile.workspace = true
|
tempfile.workspace = true
|
||||||
clap.workspace = true
|
clap.workspace = true
|
||||||
nssa-core = { path = "../nssa/core" }
|
nssa-core = { path = "../nssa/core" }
|
||||||
|
base64.workspace = true
|
||||||
|
|
||||||
[dependencies.key_protocol]
|
[dependencies.key_protocol]
|
||||||
path = "../key_protocol"
|
path = "../key_protocol"
|
||||||
|
|||||||
@ -4,7 +4,7 @@ use anyhow::Result;
|
|||||||
use common::merkle_tree_public::merkle_tree::UTXOCommitmentsMerkleTree;
|
use common::merkle_tree_public::merkle_tree::UTXOCommitmentsMerkleTree;
|
||||||
use key_protocol::key_protocol_core::NSSAUserData;
|
use key_protocol::key_protocol_core::NSSAUserData;
|
||||||
|
|
||||||
use crate::config::WalletConfig;
|
use crate::config::{PersistentAccountData, WalletConfig};
|
||||||
|
|
||||||
pub struct WalletChainStore {
|
pub struct WalletChainStore {
|
||||||
pub user_data: NSSAUserData,
|
pub user_data: NSSAUserData,
|
||||||
@ -29,6 +29,13 @@ impl WalletChainStore {
|
|||||||
wallet_config: config,
|
wallet_config: config,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn insert_account_data(&mut self, acc_data: PersistentAccountData) {
|
||||||
|
self.user_data
|
||||||
|
.key_holder
|
||||||
|
.pub_account_signing_keys
|
||||||
|
.insert(acc_data.address, acc_data.pub_sign_key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -72,7 +79,10 @@ mod tests {
|
|||||||
home,
|
home,
|
||||||
override_rust_log: None,
|
override_rust_log: None,
|
||||||
sequencer_addr: "http://127.0.0.1".to_string(),
|
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(),
|
initial_accounts: create_initial_accounts(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,12 @@ pub struct InitialAccountData {
|
|||||||
pub pub_sign_key: nssa::PrivateKey,
|
pub pub_sign_key: nssa::PrivateKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct PersistentAccountData {
|
||||||
|
pub address: nssa::Address,
|
||||||
|
pub pub_sign_key: nssa::PrivateKey,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct GasConfig {
|
pub struct GasConfig {
|
||||||
/// Gas spent per deploying one byte of data
|
/// Gas spent per deploying one byte of data
|
||||||
@ -34,8 +40,14 @@ pub struct WalletConfig {
|
|||||||
pub override_rust_log: Option<String>,
|
pub override_rust_log: Option<String>,
|
||||||
///Sequencer URL
|
///Sequencer URL
|
||||||
pub sequencer_addr: String,
|
pub sequencer_addr: String,
|
||||||
///Sequencer polling duration for new blocks in seconds
|
///Sequencer polling duration for new blocks in milliseconds
|
||||||
pub seq_poll_timeout_secs: u64,
|
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
|
///Initial accounts for wallet
|
||||||
pub initial_accounts: Vec<InitialAccountData>,
|
pub initial_accounts: Vec<InitialAccountData>,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
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::{
|
||||||
config::{InitialAccountData, WalletConfig},
|
|
||||||
HOME_DIR_ENV_VAR,
|
HOME_DIR_ENV_VAR,
|
||||||
|
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.
|
||||||
@ -30,7 +31,7 @@ pub fn produce_account_addr_from_hex(hex_str: String) -> Result<Address> {
|
|||||||
///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<InitialAccountData>> {
|
pub fn fetch_persistent_accounts() -> Result<Vec<PersistentAccountData>> {
|
||||||
let home = get_home()?;
|
let home = get_home()?;
|
||||||
let accs_path = home.join("curr_accounts.json");
|
let accs_path = home.join("curr_accounts.json");
|
||||||
|
|
||||||
@ -47,3 +48,37 @@ pub fn fetch_persistent_accounts() -> Result<Vec<InitialAccountData>> {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///Produces a list of accounts for storage
|
||||||
|
pub fn produce_data_for_storage(user_data: &NSSAUserData) -> Vec<PersistentAccountData> {
|
||||||
|
let mut vec_for_storage = vec![];
|
||||||
|
|
||||||
|
for (addr, key) in &user_data.key_holder.pub_account_signing_keys {
|
||||||
|
vec_for_storage.push(PersistentAccountData {
|
||||||
|
address: *addr,
|
||||||
|
pub_sign_key: key.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
vec_for_storage
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_home_get_env_var() {
|
||||||
|
unsafe {
|
||||||
|
std::env::set_var(HOME_DIR_ENV_VAR, "/path/to/configs");
|
||||||
|
}
|
||||||
|
|
||||||
|
let home = get_home().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(PathBuf::from_str("/path/to/configs").unwrap(), home);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
std::env::remove_var(HOME_DIR_ENV_VAR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
use std::sync::Arc;
|
use std::{fs::File, io::Write, path::PathBuf, str::FromStr, sync::Arc};
|
||||||
|
|
||||||
|
use base64::Engine;
|
||||||
use common::{
|
use common::{
|
||||||
sequencer_client::{json::SendTxResponse, SequencerClient},
|
|
||||||
ExecutionFailureKind,
|
ExecutionFailureKind,
|
||||||
|
sequencer_client::{SequencerClient, json::SendTxResponse},
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
@ -14,32 +15,62 @@ use nssa::Address;
|
|||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use nssa_core::account::Account;
|
use nssa_core::account::Account;
|
||||||
|
|
||||||
use crate::helperfunctions::{fetch_config, produce_account_addr_from_hex};
|
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";
|
pub const HOME_DIR_ENV_VAR: &str = "NSSA_WALLET_HOME_DIR";
|
||||||
pub const BLOCK_GEN_DELAY_SECS: u64 = 20;
|
|
||||||
|
|
||||||
pub mod chain_storage;
|
pub mod chain_storage;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod helperfunctions;
|
pub mod helperfunctions;
|
||||||
|
pub mod poller;
|
||||||
|
|
||||||
pub struct WalletCore {
|
pub struct WalletCore {
|
||||||
pub storage: WalletChainStore,
|
pub storage: WalletChainStore,
|
||||||
|
pub poller: TxPoller,
|
||||||
pub sequencer_client: Arc<SequencerClient>,
|
pub sequencer_client: Arc<SequencerClient>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WalletCore {
|
impl WalletCore {
|
||||||
pub fn start_from_config_update_chain(config: WalletConfig) -> Result<Self> {
|
pub fn start_from_config_update_chain(config: WalletConfig) -> Result<Self> {
|
||||||
let client = Arc::new(SequencerClient::new(config.sequencer_addr.clone())?);
|
let client = Arc::new(SequencerClient::new(config.sequencer_addr.clone())?);
|
||||||
|
let tx_poller = TxPoller::new(config.clone(), client.clone());
|
||||||
|
|
||||||
let storage = WalletChainStore::new(config)?;
|
let mut storage = WalletChainStore::new(config)?;
|
||||||
|
|
||||||
|
let persistent_accounts = fetch_persistent_accounts()?;
|
||||||
|
for pers_acc_data in persistent_accounts {
|
||||||
|
storage.insert_account_data(pers_acc_data);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
storage,
|
storage,
|
||||||
|
poller: tx_poller,
|
||||||
sequencer_client: client.clone(),
|
sequencer_client: client.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///Store persistent accounts at home
|
||||||
|
pub fn store_persistent_accounts(&self) -> Result<PathBuf> {
|
||||||
|
let home = get_home()?;
|
||||||
|
let accs_path = home.join("curr_accounts.json");
|
||||||
|
|
||||||
|
let data = produce_data_for_storage(&self.storage.user_data);
|
||||||
|
let accs = serde_json::to_vec_pretty(&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 {
|
pub fn create_new_account(&mut self) -> Address {
|
||||||
self.storage.user_data.generate_new_account()
|
self.storage.user_data.generate_new_account()
|
||||||
}
|
}
|
||||||
@ -56,44 +87,75 @@ impl WalletCore {
|
|||||||
pub async fn send_public_native_token_transfer(
|
pub async fn send_public_native_token_transfer(
|
||||||
&self,
|
&self,
|
||||||
from: Address,
|
from: Address,
|
||||||
nonce: u128,
|
|
||||||
to: Address,
|
to: Address,
|
||||||
balance_to_move: u128,
|
balance_to_move: u128,
|
||||||
) -> Result<SendTxResponse, ExecutionFailureKind> {
|
) -> Result<SendTxResponse, ExecutionFailureKind> {
|
||||||
let account = self.search_for_initial_account(from);
|
let Ok(balance) = self.get_account_balance(from).await else {
|
||||||
|
return Err(ExecutionFailureKind::SequencerError);
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(account) = account {
|
if balance >= balance_to_move {
|
||||||
if account.balance >= balance_to_move {
|
let Ok(nonces) = self.get_accounts_nonces(vec![from]).await else {
|
||||||
let addresses = vec![from, to];
|
return Err(ExecutionFailureKind::SequencerError);
|
||||||
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 = self.storage.user_data.get_account_signing_key(&from);
|
let addresses = vec![from, to];
|
||||||
|
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 let Some(signing_key) = signing_key {
|
let signing_key = self.storage.user_data.get_account_signing_key(&from);
|
||||||
let witness_set =
|
|
||||||
nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]);
|
|
||||||
|
|
||||||
let tx = nssa::PublicTransaction::new(message, witness_set);
|
let Some(signing_key) = signing_key else {
|
||||||
|
return Err(ExecutionFailureKind::KeyNotFoundError);
|
||||||
|
};
|
||||||
|
|
||||||
Ok(self.sequencer_client.send_tx(tx).await?)
|
let witness_set =
|
||||||
} else {
|
nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]);
|
||||||
Err(ExecutionFailureKind::KeyNotFoundError)
|
|
||||||
}
|
let tx = nssa::PublicTransaction::new(message, witness_set);
|
||||||
} else {
|
|
||||||
Err(ExecutionFailureKind::InsufficientFundsError)
|
Ok(self.sequencer_client.send_tx(tx).await?)
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Err(ExecutionFailureKind::AmountMismatchError)
|
Err(ExecutionFailureKind::InsufficientFundsError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///Get account balance
|
||||||
|
pub async fn get_account_balance(&self, acc: Address) -> Result<u128> {
|
||||||
|
Ok(self
|
||||||
|
.sequencer_client
|
||||||
|
.get_account_balance(acc.to_string())
|
||||||
|
.await?
|
||||||
|
.balance)
|
||||||
|
}
|
||||||
|
|
||||||
|
///Get accounts nonces
|
||||||
|
pub async fn get_accounts_nonces(&self, accs: Vec<Address>) -> Result<Vec<u128>> {
|
||||||
|
Ok(self
|
||||||
|
.sequencer_client
|
||||||
|
.get_accounts_nonces(accs.into_iter().map(|acc| acc.to_string()).collect())
|
||||||
|
.await?
|
||||||
|
.nonces)
|
||||||
|
}
|
||||||
|
|
||||||
|
///Poll transactions
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///Represents CLI command for a wallet
|
///Represents CLI command for a wallet
|
||||||
@ -105,9 +167,6 @@ pub enum Command {
|
|||||||
///from - valid 32 byte hex string
|
///from - valid 32 byte hex string
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
from: String,
|
from: String,
|
||||||
///nonce - u128 integer
|
|
||||||
#[arg(long)]
|
|
||||||
nonce: u128,
|
|
||||||
///to - valid 32 byte hex string
|
///to - valid 32 byte hex string
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
to: String,
|
to: String,
|
||||||
@ -115,6 +174,23 @@ 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,
|
||||||
|
},
|
||||||
|
///Get account `addr` balance
|
||||||
|
GetAccountBalance {
|
||||||
|
#[arg(short, long)]
|
||||||
|
addr: String,
|
||||||
|
},
|
||||||
|
///Get account `addr` nonce
|
||||||
|
GetAccountNonce {
|
||||||
|
#[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
|
||||||
@ -127,29 +203,57 @@ pub struct Args {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn execute_subcommand(command: Command) -> Result<()> {
|
pub async fn execute_subcommand(command: Command) -> Result<()> {
|
||||||
match command {
|
let wallet_config = fetch_config()?;
|
||||||
Command::SendNativeTokenTransfer {
|
let mut wallet_core = WalletCore::start_from_config_update_chain(wallet_config)?;
|
||||||
from,
|
|
||||||
nonce,
|
|
||||||
to,
|
|
||||||
amount,
|
|
||||||
} => {
|
|
||||||
let wallet_config = fetch_config()?;
|
|
||||||
|
|
||||||
|
match command {
|
||||||
|
Command::SendNativeTokenTransfer { from, to, amount } => {
|
||||||
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)?;
|
|
||||||
|
|
||||||
let res = wallet_core
|
let res = wallet_core
|
||||||
.send_public_native_token_transfer(from, nonce, to, amount)
|
.send_public_native_token_transfer(from, to, amount)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
info!("Results of tx send is {res:#?}");
|
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:?}");
|
||||||
|
}
|
||||||
|
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:#?}");
|
||||||
|
}
|
||||||
|
Command::GetAccountBalance { addr } => {
|
||||||
|
let addr = Address::from_str(&addr)?;
|
||||||
|
|
||||||
|
let balance = wallet_core.get_account_balance(addr).await?;
|
||||||
|
info!("Accounts {addr:#?} balance is {balance}");
|
||||||
|
}
|
||||||
|
Command::GetAccountNonce { addr } => {
|
||||||
|
let addr = Address::from_str(&addr)?;
|
||||||
|
|
||||||
|
let nonce = wallet_core.get_accounts_nonces(vec![addr]).await?[0];
|
||||||
|
info!("Accounts {addr:#?} nonce is {nonce}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wallet_core.store_persistent_accounts()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use tokio::runtime::Builder;
|
use tokio::runtime::Builder;
|
||||||
use wallet::{execute_subcommand, Args};
|
use wallet::{Args, execute_subcommand};
|
||||||
|
|
||||||
pub const NUM_THREADS: usize = 2;
|
pub const NUM_THREADS: usize = 2;
|
||||||
|
|
||||||
|
|||||||
68
wallet/src/poller.rs
Normal file
68
wallet/src/poller.rs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use common::sequencer_client::SequencerClient;
|
||||||
|
use log::{info, warn};
|
||||||
|
|
||||||
|
use crate::config::WalletConfig;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
///Helperstruct to poll transactions
|
||||||
|
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 fn new(config: WalletConfig, client: Arc<SequencerClient>) -> Self {
|
||||||
|
Self {
|
||||||
|
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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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