From c47693b9b0f2da429c85294809c1dd38f532ac3b Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Thu, 23 Oct 2025 16:23:47 -0300 Subject: [PATCH 1/9] add tps integration test --- integration_tests/Cargo.toml | 3 + .../debug/sequencer/sequencer_config.json | 1 + integration_tests/src/lib.rs | 89 ++++++++- integration_tests/src/tps_test_utils.rs | 187 ++++++++++++++++++ sequencer_core/src/config.rs | 2 + sequencer_core/src/lib.rs | 5 +- sequencer_rpc/src/process.rs | 1 + .../configs/debug/sequencer_config.json | 1 + 8 files changed, 286 insertions(+), 3 deletions(-) create mode 100644 integration_tests/src/tps_test_utils.rs diff --git a/integration_tests/Cargo.toml b/integration_tests/Cargo.toml index f7bd131..9fabe82 100644 --- a/integration_tests/Cargo.toml +++ b/integration_tests/Cargo.toml @@ -37,3 +37,6 @@ path = "../common" [dependencies.nssa] path = "../nssa" features = ["no_docker"] + +[dependencies.key_protocol] +path = "../key_protocol" diff --git a/integration_tests/configs/debug/sequencer/sequencer_config.json b/integration_tests/configs/debug/sequencer/sequencer_config.json index 2a2037d..156121c 100644 --- a/integration_tests/configs/debug/sequencer/sequencer_config.json +++ b/integration_tests/configs/debug/sequencer/sequencer_config.json @@ -4,6 +4,7 @@ "genesis_id": 1, "is_genesis_random": true, "max_num_tx_in_block": 20, + "mempool_max_size": 10000, "block_create_timeout_millis": 10000, "port": 3040, "initial_accounts": [ diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index ec04130..e6e82d0 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -1,5 +1,8 @@ use base64::{Engine, engine::general_purpose::STANDARD as BASE64}; -use std::{path::PathBuf, time::Duration}; +use std::{ + path::PathBuf, + time::{Duration, Instant}, +}; use actix_web::dev::ServerHandle; use anyhow::Result; @@ -38,6 +41,10 @@ use wallet::{ helperfunctions::{fetch_config, fetch_persistent_accounts}, }; +use crate::tps_test_utils::TpsTestManager; + +mod tps_test_utils; + #[derive(Parser, Debug)] #[clap(version)] struct Args { @@ -81,6 +88,26 @@ pub async fn pre_test( )) } +#[allow(clippy::type_complexity)] +async fn pre_tps_test( + test: &TpsTestManager, +) -> Result<(ServerHandle, JoinHandle>, TempDir)> { + info!("Generating tps test config"); + let mut sequencer_config = test.generate_tps_test_config(); + info!("Done"); + + let temp_dir_sequencer = replace_home_dir_with_temp_dir_in_configs(&mut sequencer_config); + + let (seq_http_server_handle, sequencer_loop_handle) = + startup_sequencer(sequencer_config).await?; + + Ok(( + seq_http_server_handle, + sequencer_loop_handle, + temp_dir_sequencer, + )) +} + pub fn replace_home_dir_with_temp_dir_in_configs( sequencer_config: &mut SequencerConfig, ) -> TempDir { @@ -112,6 +139,63 @@ pub async fn post_test(residual: (ServerHandle, JoinHandle>, TempDir) //So they are dropped and tempdirs will be dropped too, } +pub async fn tps_test() { + let num_transactions = 300 * 5; + let target_tps = 12; + let tps_test = TpsTestManager::new(target_tps, num_transactions); + let target_time = tps_test.target_time(); + info!("Target time: {:?} seconds", target_time.as_secs()); + let res = pre_tps_test(&tps_test).await.unwrap(); + + let wallet_config = fetch_config().await.unwrap(); + let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); + + info!("TPS test begin"); + let txs = tps_test.build_public_txs(); + let now = Instant::now(); + + let mut tx_hashes = vec![]; + for (i, tx) in txs.into_iter().enumerate() { + let tx_hash = seq_client.send_tx_public(tx).await.unwrap().tx_hash; + info!("Sent tx {i}"); + tx_hashes.push(tx_hash); + } + + for (i, tx_hash) in tx_hashes.iter().enumerate() { + loop { + if now.elapsed().as_millis() > target_time.as_millis() { + panic!("TPS test failed by timout"); + } + + let tx_obj = seq_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 + && tx_obj.transaction.is_some() + { + info!("Found tx {i} with hash {tx_hash}"); + break; + } + } + } + let time_elapsed = now.elapsed().as_secs(); + + info!("TPS test finished successfully"); + info!("Target TPS: {}", target_tps); + info!( + "Processed {} transactions in {}s", + tx_hashes.len(), + time_elapsed + ); + info!("Target time: {:?}s", target_time.as_secs()); + + post_test(res).await; +} + pub async fn test_success() { info!("test_success"); let command = Command::Transfer(NativeTokenTransferProgramSubcommand::Public { @@ -1624,6 +1708,9 @@ pub async fn main_tests_runner() -> Result<()> { } = args; match test_name.as_str() { + "tps_test" => { + tps_test().await; + } "test_success_token_program" => { test_cleanup_wrap!(home_dir, test_success_token_program); } diff --git a/integration_tests/src/tps_test_utils.rs b/integration_tests/src/tps_test_utils.rs new file mode 100644 index 0000000..1e31c02 --- /dev/null +++ b/integration_tests/src/tps_test_utils.rs @@ -0,0 +1,187 @@ +use std::time::Duration; + +use key_protocol::key_management::ephemeral_key_holder::EphemeralKeyHolder; +use nssa::{ + Account, AccountId, Address, PrivacyPreservingTransaction, PrivateKey, PublicKey, + PublicTransaction, + privacy_preserving_transaction::{self as pptx, circuit}, + program::Program, + public_transaction as putx, +}; +use nssa_core::{ + MembershipProof, NullifierPublicKey, account::AccountWithMetadata, + encryption::IncomingViewingPublicKey, +}; +use sequencer_core::config::{AccountInitialData, CommitmentsInitialData, SequencerConfig}; + +pub(crate) struct TpsTestManager { + public_keypairs: Vec<(PrivateKey, Address)>, + target_tps: u64, +} + +impl TpsTestManager { + /// Generates public account keypairs. These are used to populate the config and to generate valid + /// public transactions for the tps test. + pub(crate) fn new(target_tps: u64, number_transactions: usize) -> Self { + let public_keypairs = (1..(number_transactions + 2)) + .map(|i| { + let mut private_key_bytes = [0u8; 32]; + private_key_bytes[..8].copy_from_slice(&i.to_le_bytes()); + let private_key = PrivateKey::try_new(private_key_bytes).unwrap(); + let public_key = PublicKey::new_from_private_key(&private_key); + let address = Address::from(&public_key); + (private_key, address) + }) + .collect::>(); + Self { + public_keypairs, + target_tps, + } + } + + pub(crate) fn target_time(&self) -> Duration { + let number_transactions = (self.public_keypairs.len() - 1) as u64; + Duration::from_secs_f64(number_transactions as f64 / self.target_tps as f64) + } + + /// + /// Build a batch of public transactions to submit to the node. + pub fn build_public_txs(&self) -> Vec { + // Create valid public transactions + let program = Program::authenticated_transfer_program(); + let public_txs: Vec = self + .public_keypairs + .windows(2) + .map(|pair| { + let amount: u128 = 1; + let message = putx::Message::try_new( + program.id(), + [pair[0].1, pair[1].1].to_vec(), + [0u128].to_vec(), + amount, + ) + .unwrap(); + let witness_set = + nssa::public_transaction::WitnessSet::for_message(&message, &[&pair[0].0]); + PublicTransaction::new(message, witness_set) + }) + .collect(); + + public_txs + } + + /// Generates a sequencer configuration with initial balance in a number of public accounts. + /// The transactions generated with the function `build_public_txs` will be valid in a node started + /// with the config from this method. + pub(crate) fn generate_tps_test_config(&self) -> SequencerConfig { + // Create public public keypairs + let initial_public_accounts = self + .public_keypairs + .iter() + .map(|(_, addr)| AccountInitialData { + addr: addr.to_string(), + balance: 10, + }) + .collect(); + + // Generate an initial commitment to be used with the privacy preserving transaction + // created with the `build_privacy_transaction` function. + let sender_nsk = [1; 32]; + let sender_npk = NullifierPublicKey::from(&sender_nsk); + let account = Account { + balance: 100, + nonce: 0xdeadbeef, + program_owner: Program::authenticated_transfer_program().id(), + data: vec![], + }; + let initial_commitment = CommitmentsInitialData { + npk: sender_npk, + account, + }; + + SequencerConfig { + home: ".".into(), + override_rust_log: None, + genesis_id: 1, + is_genesis_random: true, + max_num_tx_in_block: 300, + mempool_max_size: 10000, + block_create_timeout_millis: 12000, + port: 3040, + initial_accounts: initial_public_accounts, + initial_commitments: vec![initial_commitment], + signing_key: [37; 32], + } + } +} + +/// Builds a single privacy transaction to use in stress tests. This involves generating a proof so +/// it may take a while to run. In normal execution of the node this transaction will be accepted +/// only once. Disabling the node's nullifier uniqueness check allows to submit this transaction +/// multiple times with the purpose of testing the node's processing performance. +#[allow(unused)] +fn build_privacy_transaction() -> PrivacyPreservingTransaction { + let program = Program::authenticated_transfer_program(); + let sender_nsk = [1; 32]; + let sender_isk = [99; 32]; + let sender_ipk = IncomingViewingPublicKey::from_scalar(sender_isk); + let sender_npk = NullifierPublicKey::from(&sender_nsk); + let sender_pre = AccountWithMetadata::new( + Account { + balance: 100, + nonce: 0xdeadbeef, + program_owner: program.id(), + data: vec![], + }, + true, + AccountId::from(&sender_npk), + ); + let recipient_nsk = [2; 32]; + let recipient_isk = [99; 32]; + let recipient_ipk = IncomingViewingPublicKey::from_scalar(recipient_isk); + let recipient_npk = NullifierPublicKey::from(&recipient_nsk); + let recipient_pre = + AccountWithMetadata::new(Account::default(), false, AccountId::from(&recipient_npk)); + + let eph_holder_from = EphemeralKeyHolder::new(&sender_npk); + let sender_ss = eph_holder_from.calculate_shared_secret_sender(&sender_ipk); + let sender_epk = eph_holder_from.generate_ephemeral_public_key(); + + let eph_holder_to = EphemeralKeyHolder::new(&recipient_npk); + let recipient_ss = eph_holder_to.calculate_shared_secret_sender(&recipient_ipk); + let recipient_epk = eph_holder_from.generate_ephemeral_public_key(); + + let balance_to_move: u128 = 1; + let proof: MembershipProof = ( + 1, + vec![[ + 170, 10, 217, 228, 20, 35, 189, 177, 238, 235, 97, 129, 132, 89, 96, 247, 86, 91, 222, + 214, 38, 194, 216, 67, 56, 251, 208, 226, 0, 117, 149, 39, + ]], + ); + let (output, proof) = circuit::execute_and_prove( + &[sender_pre, recipient_pre], + &Program::serialize_instruction(balance_to_move).unwrap(), + &[1, 2], + &[0xdeadbeef1, 0xdeadbeef2], + &[ + (sender_npk.clone(), sender_ss), + (recipient_npk.clone(), recipient_ss), + ], + &[(sender_nsk, proof)], + &program, + ) + .unwrap(); + let message = pptx::message::Message::try_from_circuit_output( + vec![], + vec![], + vec![ + (sender_npk, sender_ipk, sender_epk), + (recipient_npk, recipient_ipk, recipient_epk), + ], + output, + ) + .unwrap(); + let witness_set = pptx::witness_set::WitnessSet::for_message(&message, proof, &[]); + pptx::PrivacyPreservingTransaction::new(message, witness_set) +} diff --git a/sequencer_core/src/config.rs b/sequencer_core/src/config.rs index 2f57f72..a86bcb3 100644 --- a/sequencer_core/src/config.rs +++ b/sequencer_core/src/config.rs @@ -28,6 +28,8 @@ pub struct SequencerConfig { pub is_genesis_random: bool, ///Maximum number of transactions in block pub max_num_tx_in_block: usize, + ///Mempool maximum size + pub mempool_max_size: usize, ///Interval in which blocks produced pub block_create_timeout_millis: u64, ///Port to listen diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 7366471..0c83f33 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -103,7 +103,7 @@ impl SequencerCore { })?; let mempool_size = self.mempool.len(); - if mempool_size >= self.sequencer_config.max_num_tx_in_block { + if mempool_size >= self.sequencer_config.mempool_max_size { return Err(TransactionMalformationError::MempoolFullForRound); } @@ -218,6 +218,7 @@ mod tests { genesis_id: 1, is_genesis_random: false, max_num_tx_in_block: 10, + mempool_max_size: 10000, block_create_timeout_millis: 1000, port: 8080, initial_accounts, @@ -511,7 +512,7 @@ mod tests { #[test] fn test_push_tx_into_mempool_fails_mempool_full() { let config = SequencerConfig { - max_num_tx_in_block: 1, + mempool_max_size: 1, ..setup_sequencer_config() }; let mut sequencer = SequencerCore::start_from_config(config); diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index f8c583f..b7e501a 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -357,6 +357,7 @@ mod tests { genesis_id: 1, is_genesis_random: false, max_num_tx_in_block: 10, + mempool_max_size: 1000, block_create_timeout_millis: 1000, port: 8080, initial_accounts, diff --git a/sequencer_runner/configs/debug/sequencer_config.json b/sequencer_runner/configs/debug/sequencer_config.json index 19ff458..9abbd9a 100644 --- a/sequencer_runner/configs/debug/sequencer_config.json +++ b/sequencer_runner/configs/debug/sequencer_config.json @@ -4,6 +4,7 @@ "genesis_id": 1, "is_genesis_random": true, "max_num_tx_in_block": 20, + "mempool_max_size": 1000, "block_create_timeout_millis": 10000, "port": 3040, "initial_accounts": [ From 4648f46f5134102470715fb078e94caa5022c431 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Fri, 24 Oct 2025 22:18:41 -0300 Subject: [PATCH 2/9] minor changes --- integration_tests/src/lib.rs | 1 + sequencer_core/src/lib.rs | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index e6e82d0..e455bd3 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -1842,6 +1842,7 @@ pub async fn main_tests_runner() -> Result<()> { home_dir, test_success_private_transfer_to_another_owned_account_cont_run_path ); + tps_test().await; } "all_private" => { test_cleanup_wrap!( diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 0c83f33..644f99e 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -1,4 +1,4 @@ -use std::fmt::Display; +use std::{fmt::Display, time::Instant}; use anyhow::Result; use common::{ @@ -146,6 +146,7 @@ impl SequencerCore { ///Produces new block from transactions in mempool pub fn produce_new_block_with_mempool_transactions(&mut self) -> Result { + let now = Instant::now(); let new_block_height = self.chain_height + 1; let mut num_valid_transactions_in_block = 0; @@ -175,6 +176,8 @@ impl SequencerCore { let curr_time = chrono::Utc::now().timestamp_millis() as u64; + let num_txs_in_block = valid_transactions.len(); + let hashable_data = HashableBlockData { block_id: new_block_height, transactions: valid_transactions, @@ -188,6 +191,12 @@ impl SequencerCore { self.chain_height = new_block_height; + log::info!( + "Created block with {} transactions in {} seconds", + num_txs_in_block, + now.elapsed().as_secs() + ); + Ok(self.chain_height) } } From 7f075fcdd3104d4138176651d6e542e265f032b1 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Sat, 25 Oct 2025 00:30:04 -0300 Subject: [PATCH 3/9] wip --- sequencer_core/src/lib.rs | 40 ++++++++++++++----- .../src/sequencer_store/block_store.rs | 6 +-- sequencer_core/src/sequencer_store/mod.rs | 19 +++------ sequencer_runner/Cargo.toml | 1 + storage/src/lib.rs | 3 +- 5 files changed, 41 insertions(+), 28 deletions(-) diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 7366471..6bd10de 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -12,6 +12,8 @@ use mempool::MemPool; use sequencer_store::SequecerChainStore; use serde::{Deserialize, Serialize}; +use crate::sequencer_store::block_store::block_to_transactions_map; + pub mod config; pub mod sequencer_store; @@ -53,19 +55,39 @@ impl SequencerCore { initial_commitments.push(comm); } - Self { - store: SequecerChainStore::new_with_genesis( - &config.home, - config.genesis_id, - config.is_genesis_random, - &config.initial_accounts, - &initial_commitments, - nssa::PrivateKey::try_new(config.signing_key).unwrap(), - ), + let store = SequecerChainStore::new_with_genesis( + &config.home, + config.genesis_id, + config.is_genesis_random, + &config.initial_accounts, + &initial_commitments, + nssa::PrivateKey::try_new(config.signing_key).unwrap(), + ); + + let mut block_id = config.genesis_id; + + let mut this = Self { + store, mempool: MemPool::default(), chain_height: config.genesis_id, sequencer_config: config, + }; + + loop { + let Ok(block) = this.store.block_store.get_block_at_id(block_id) else { + break; + }; + for encoded_transaction in block.body.transactions { + let transaction = NSSATransaction::try_from(&encoded_transaction).unwrap(); + let transaction = this.transaction_pre_check(transaction).unwrap(); + this.execute_check_transaction_on_state(transaction).unwrap(); + this.store.block_store.tx_hash_to_block_map.insert(encoded_transaction.hash(), block_id); + + } + block_id +=1; } + + this } pub fn transaction_pre_check( diff --git a/sequencer_core/src/sequencer_store/block_store.rs b/sequencer_core/src/sequencer_store/block_store.rs index e1dbb2b..6fd9fb7 100644 --- a/sequencer_core/src/sequencer_store/block_store.rs +++ b/sequencer_core/src/sequencer_store/block_store.rs @@ -7,7 +7,7 @@ use storage::RocksDBIO; pub struct SequecerBlockStore { dbio: RocksDBIO, // TODO: Consider adding the hashmap to the database for faster recovery. - tx_hash_to_block_map: HashMap, + pub tx_hash_to_block_map: HashMap, pub genesis_id: u64, pub signing_key: nssa::PrivateKey, } @@ -28,7 +28,7 @@ impl SequecerBlockStore { HashMap::new() }; - let dbio = RocksDBIO::new(location, genesis_block)?; + let dbio = RocksDBIO::open_or_create(location, genesis_block)?; let genesis_id = dbio.get_meta_first_block_in_db()?; @@ -71,7 +71,7 @@ impl SequecerBlockStore { } } -fn block_to_transactions_map(block: &Block) -> HashMap { +pub(crate) fn block_to_transactions_map(block: &Block) -> HashMap { block .body .transactions diff --git a/sequencer_core/src/sequencer_store/mod.rs b/sequencer_core/src/sequencer_store/mod.rs index 4f18405..f6ca478 100644 --- a/sequencer_core/src/sequencer_store/mod.rs +++ b/sequencer_core/src/sequencer_store/mod.rs @@ -1,11 +1,11 @@ use std::path::Path; use block_store::SequecerBlockStore; -use common::block::HashableBlockData; +use common::{block::HashableBlockData, transaction::{self, NSSATransaction}}; use nssa::{self, Address}; use rand::{RngCore, rngs::OsRng}; -use crate::config::AccountInitialData; +use crate::{config::AccountInitialData, sequencer_store::block_store::block_to_transactions_map}; pub mod block_store; @@ -39,21 +39,11 @@ impl SequecerChainStore { this }; - let mut data = [0; 32]; - let mut prev_block_hash = [0; 32]; - - if is_genesis_random { - OsRng.fill_bytes(&mut data); - OsRng.fill_bytes(&mut prev_block_hash); - } - - let curr_time = chrono::Utc::now().timestamp_millis() as u64; - let hashable_data = HashableBlockData { block_id: genesis_id, transactions: vec![], - prev_block_hash, - timestamp: curr_time, + prev_block_hash: [0; 32], + timestamp: 0, }; let genesis_block = hashable_data.into_block(&signing_key); @@ -67,6 +57,7 @@ impl SequecerChainStore { ) .unwrap(); + Self { state, block_store } } } diff --git a/sequencer_runner/Cargo.toml b/sequencer_runner/Cargo.toml index 4da788d..2f105ee 100644 --- a/sequencer_runner/Cargo.toml +++ b/sequencer_runner/Cargo.toml @@ -22,6 +22,7 @@ path = "../sequencer_rpc" [dependencies.sequencer_core] path = "../sequencer_core" +features = ["testnet"] [dependencies.common] path = "../common" diff --git a/storage/src/lib.rs b/storage/src/lib.rs index d69c521..cbce767 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -44,7 +44,7 @@ pub struct RocksDBIO { } impl RocksDBIO { - pub fn new(path: &Path, start_block: Option) -> DbResult { + pub fn open_or_create(path: &Path, start_block: Option) -> DbResult { let mut cf_opts = Options::default(); cf_opts.set_max_write_buffer_number(16); //ToDo: Add more column families for different data @@ -74,7 +74,6 @@ impl RocksDBIO { let block_id = block.header.block_id; dbio.put_meta_first_block_in_db(block)?; dbio.put_meta_is_first_block_set()?; - dbio.put_meta_last_block_in_db(block_id)?; Ok(dbio) From 1ad93c2977e5e68e8b25d1cf28c78dfe9a666880 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Mon, 27 Oct 2025 16:04:12 -0300 Subject: [PATCH 4/9] fmt --- sequencer_core/src/lib.rs | 11 +++++++---- sequencer_core/src/sequencer_store/mod.rs | 6 ++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 6bd10de..ac4fe72 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -80,11 +80,14 @@ impl SequencerCore { for encoded_transaction in block.body.transactions { let transaction = NSSATransaction::try_from(&encoded_transaction).unwrap(); let transaction = this.transaction_pre_check(transaction).unwrap(); - this.execute_check_transaction_on_state(transaction).unwrap(); - this.store.block_store.tx_hash_to_block_map.insert(encoded_transaction.hash(), block_id); - + this.execute_check_transaction_on_state(transaction) + .unwrap(); + this.store + .block_store + .tx_hash_to_block_map + .insert(encoded_transaction.hash(), block_id); } - block_id +=1; + block_id += 1; } this diff --git a/sequencer_core/src/sequencer_store/mod.rs b/sequencer_core/src/sequencer_store/mod.rs index f6ca478..1081960 100644 --- a/sequencer_core/src/sequencer_store/mod.rs +++ b/sequencer_core/src/sequencer_store/mod.rs @@ -1,7 +1,10 @@ use std::path::Path; use block_store::SequecerBlockStore; -use common::{block::HashableBlockData, transaction::{self, NSSATransaction}}; +use common::{ + block::HashableBlockData, + transaction::{self, NSSATransaction}, +}; use nssa::{self, Address}; use rand::{RngCore, rngs::OsRng}; @@ -57,7 +60,6 @@ impl SequecerChainStore { ) .unwrap(); - Self { state, block_store } } } From 6b7560901262437e23d0b3a0f956967953a7132a Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Mon, 27 Oct 2025 20:07:15 -0300 Subject: [PATCH 5/9] fix block id --- sequencer_core/src/lib.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index ac4fe72..25e0390 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -73,10 +73,7 @@ impl SequencerCore { sequencer_config: config, }; - loop { - let Ok(block) = this.store.block_store.get_block_at_id(block_id) else { - break; - }; + while let Ok(block) = this.store.block_store.get_block_at_id(block_id) { for encoded_transaction in block.body.transactions { let transaction = NSSATransaction::try_from(&encoded_transaction).unwrap(); let transaction = this.transaction_pre_check(transaction).unwrap(); @@ -87,6 +84,7 @@ impl SequencerCore { .tx_hash_to_block_map .insert(encoded_transaction.hash(), block_id); } + this.chain_height = block_id; block_id += 1; } From 4a23accbde81edce9a3283440ee02c22b7ec48b7 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 28 Oct 2025 13:01:54 -0300 Subject: [PATCH 6/9] refactor --- .../src/{sequencer_store => }/block_store.rs | 0 sequencer_core/src/lib.rs | 101 ++++++++++-------- sequencer_core/src/sequencer_store/mod.rs | 65 ----------- sequencer_rpc/src/process.rs | 11 +- 4 files changed, 59 insertions(+), 118 deletions(-) rename sequencer_core/src/{sequencer_store => }/block_store.rs (100%) delete mode 100644 sequencer_core/src/sequencer_store/mod.rs diff --git a/sequencer_core/src/sequencer_store/block_store.rs b/sequencer_core/src/block_store.rs similarity index 100% rename from sequencer_core/src/sequencer_store/block_store.rs rename to sequencer_core/src/block_store.rs diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 25e0390..3bf7b7c 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -9,16 +9,16 @@ use common::{ use config::SequencerConfig; use log::warn; use mempool::MemPool; -use sequencer_store::SequecerChainStore; use serde::{Deserialize, Serialize}; -use crate::sequencer_store::block_store::block_to_transactions_map; +use crate::block_store::SequecerBlockStore; pub mod config; -pub mod sequencer_store; +pub mod block_store; pub struct SequencerCore { - pub store: SequecerChainStore, + pub state: nssa::V02State, + pub block_store: SequecerBlockStore, pub mempool: MemPool, pub sequencer_config: SequencerConfig, pub chain_height: u64, @@ -41,6 +41,24 @@ impl std::error::Error for TransactionMalformationError {} impl SequencerCore { pub fn start_from_config(config: SequencerConfig) -> Self { + let hashable_data = HashableBlockData { + block_id: config.genesis_id, + transactions: vec![], + prev_block_hash: [0; 32], + timestamp: 0, + }; + + let signing_key = nssa::PrivateKey::try_new(config.signing_key).unwrap(); + let genesis_block = hashable_data.into_block(&signing_key); + + //Sequencer should panic if unable to open db, + //as fixing this issue may require actions non-native to program scope + let block_store = SequecerBlockStore::open_db_with_genesis( + &config.home.join("rocksdb"), + Some(genesis_block), + signing_key, + ) + .unwrap(); let mut initial_commitments = vec![]; for init_comm_data in config.initial_commitments.clone() { @@ -55,42 +73,46 @@ impl SequencerCore { initial_commitments.push(comm); } - let store = SequecerChainStore::new_with_genesis( - &config.home, - config.genesis_id, - config.is_genesis_random, - &config.initial_accounts, - &initial_commitments, - nssa::PrivateKey::try_new(config.signing_key).unwrap(), - ); + let init_accs: Vec<(nssa::Address, u128)> = config + .initial_accounts + .iter() + .map(|acc_data| (acc_data.addr.parse().unwrap(), acc_data.balance)) + .collect(); - let mut block_id = config.genesis_id; + let mut state = nssa::V02State::new_with_genesis_accounts(&init_accs, &initial_commitments); + + #[cfg(feature = "testnet")] + state.add_pinata_program("cafe".repeat(16).parse().unwrap()); let mut this = Self { - store, + state, + block_store, mempool: MemPool::default(), chain_height: config.genesis_id, sequencer_config: config, }; - while let Ok(block) = this.store.block_store.get_block_at_id(block_id) { - for encoded_transaction in block.body.transactions { - let transaction = NSSATransaction::try_from(&encoded_transaction).unwrap(); - let transaction = this.transaction_pre_check(transaction).unwrap(); - this.execute_check_transaction_on_state(transaction) - .unwrap(); - this.store - .block_store - .tx_hash_to_block_map - .insert(encoded_transaction.hash(), block_id); - } - this.chain_height = block_id; - block_id += 1; - } + this.sync_state_with_stored_blocks(); this } + fn sync_state_with_stored_blocks(&mut self) { + let mut next_block_id = self.sequencer_config.genesis_id + 1; + while let Ok(block) = self.block_store.get_block_at_id(next_block_id) { + for encoded_transaction in block.body.transactions { + let transaction = NSSATransaction::try_from(&encoded_transaction).unwrap(); + self.execute_check_transaction_on_state(transaction) + .unwrap(); + self.block_store + .tx_hash_to_block_map + .insert(encoded_transaction.hash(), next_block_id); + } + self.chain_height = next_block_id; + next_block_id += 1; + } + } + pub fn transaction_pre_check( &mut self, tx: NSSATransaction, @@ -145,20 +167,17 @@ impl SequencerCore { ) -> Result { match &tx { NSSATransaction::Public(tx) => { - self.store - .state + self.state .transition_from_public_transaction(tx) .inspect_err(|err| warn!("Error at transition {err:#?}"))?; } NSSATransaction::PrivacyPreserving(tx) => { - self.store - .state + self.state .transition_from_privacy_preserving_transaction(tx) .inspect_err(|err| warn!("Error at transition {err:#?}"))?; } NSSATransaction::ProgramDeployment(tx) => { - self.store - .state + self.state .transition_from_program_deployment_transaction(tx) .inspect_err(|err| warn!("Error at transition {err:#?}"))?; } @@ -190,7 +209,6 @@ impl SequencerCore { } let prev_block_hash = self - .store .block_store .get_block_at_id(self.chain_height)? .header @@ -205,9 +223,9 @@ impl SequencerCore { timestamp: curr_time, }; - let block = hashable_data.into_block(&self.store.block_store.signing_key); + let block = hashable_data.into_block(&self.block_store.signing_key); - self.store.block_store.put_block_at_id(block)?; + self.block_store.put_block_at_id(block)?; self.chain_height = new_block_height; @@ -311,12 +329,10 @@ mod tests { .unwrap(); let balance_acc_1 = sequencer - .store .state .get_account_by_address(&nssa::Address::new(acc1_addr)) .balance; let balance_acc_2 = sequencer - .store .state .get_account_by_address(&nssa::Address::new(acc2_addr)) .balance; @@ -364,7 +380,6 @@ mod tests { assert_eq!( 10000, sequencer - .store .state .get_account_by_address(&nssa::Address::new(acc1_addr)) .balance @@ -372,7 +387,6 @@ mod tests { assert_eq!( 20000, sequencer - .store .state .get_account_by_address(&nssa::Address::new(acc2_addr)) .balance @@ -517,12 +531,10 @@ mod tests { .unwrap(); let bal_from = sequencer - .store .state .get_account_by_address(&nssa::Address::new(acc1)) .balance; let bal_to = sequencer - .store .state .get_account_by_address(&nssa::Address::new(acc2)) .balance; @@ -615,7 +627,6 @@ mod tests { .produce_new_block_with_mempool_transactions() .unwrap(); let block = sequencer - .store .block_store .get_block_at_id(current_height) .unwrap(); @@ -652,7 +663,6 @@ mod tests { .produce_new_block_with_mempool_transactions() .unwrap(); let block = sequencer - .store .block_store .get_block_at_id(current_height) .unwrap(); @@ -664,7 +674,6 @@ mod tests { .produce_new_block_with_mempool_transactions() .unwrap(); let block = sequencer - .store .block_store .get_block_at_id(current_height) .unwrap(); diff --git a/sequencer_core/src/sequencer_store/mod.rs b/sequencer_core/src/sequencer_store/mod.rs deleted file mode 100644 index 1081960..0000000 --- a/sequencer_core/src/sequencer_store/mod.rs +++ /dev/null @@ -1,65 +0,0 @@ -use std::path::Path; - -use block_store::SequecerBlockStore; -use common::{ - block::HashableBlockData, - transaction::{self, NSSATransaction}, -}; -use nssa::{self, Address}; -use rand::{RngCore, rngs::OsRng}; - -use crate::{config::AccountInitialData, sequencer_store::block_store::block_to_transactions_map}; - -pub mod block_store; - -pub struct SequecerChainStore { - pub state: nssa::V02State, - pub block_store: SequecerBlockStore, -} - -impl SequecerChainStore { - pub fn new_with_genesis( - home_dir: &Path, - genesis_id: u64, - is_genesis_random: bool, - initial_accounts: &[AccountInitialData], - initial_commitments: &[nssa_core::Commitment], - signing_key: nssa::PrivateKey, - ) -> Self { - let init_accs: Vec<(Address, u128)> = initial_accounts - .iter() - .map(|acc_data| (acc_data.addr.parse().unwrap(), acc_data.balance)) - .collect(); - - #[cfg(not(feature = "testnet"))] - let state = nssa::V02State::new_with_genesis_accounts(&init_accs, initial_commitments); - - #[cfg(feature = "testnet")] - let state = { - let mut this = - nssa::V02State::new_with_genesis_accounts(&init_accs, initial_commitments); - this.add_pinata_program("cafe".repeat(16).parse().unwrap()); - this - }; - - let hashable_data = HashableBlockData { - block_id: genesis_id, - transactions: vec![], - prev_block_hash: [0; 32], - timestamp: 0, - }; - - let genesis_block = hashable_data.into_block(&signing_key); - - //Sequencer should panic if unable to open db, - //as fixing this issue may require actions non-native to program scope - let block_store = SequecerBlockStore::open_db_with_genesis( - &home_dir.join("rocksdb"), - Some(genesis_block), - signing_key, - ) - .unwrap(); - - Self { state, block_store } - } -} diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index f8c583f..5e03f58 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -104,7 +104,6 @@ impl JsonHandler { let state = self.sequencer_state.lock().await; state - .store .block_store .get_block_at_id(get_block_req.block_id)? }; @@ -122,7 +121,7 @@ impl JsonHandler { let genesis_id = { let state = self.sequencer_state.lock().await; - state.store.block_store.genesis_id + state.block_store.genesis_id }; let helperstruct = GetGenesisIdResponse { genesis_id }; @@ -173,7 +172,7 @@ impl JsonHandler { let balance = { let state = self.sequencer_state.lock().await; - let account = state.store.state.get_account_by_address(&address); + let account = state.state.get_account_by_address(&address); account.balance }; @@ -200,7 +199,7 @@ impl JsonHandler { addresses .into_iter() - .map(|addr| state.store.state.get_account_by_address(&addr).nonce) + .map(|addr| state.state.get_account_by_address(&addr).nonce) .collect() }; @@ -222,7 +221,7 @@ impl JsonHandler { let account = { let state = self.sequencer_state.lock().await; - state.store.state.get_account_by_address(&address) + state.state.get_account_by_address(&address) }; let helperstruct = GetAccountResponse { account }; @@ -243,7 +242,6 @@ impl JsonHandler { let transaction = { let state = self.sequencer_state.lock().await; state - .store .block_store .get_transaction_by_hash(hash) .map(|tx| borsh::to_vec(&tx).unwrap()) @@ -262,7 +260,6 @@ impl JsonHandler { let membership_proof = { let state = self.sequencer_state.lock().await; state - .store .state .get_proof_for_commitment(&get_proof_req.commitment) }; From 719a8dcafe09ecdaa1be418642f3776ae961c588 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 28 Oct 2025 16:43:13 -0300 Subject: [PATCH 7/9] refactor tps test --- integration_tests/src/lib.rs | 6 +- integration_tests/src/test_suite_map.rs | 177 +++++++++++++----------- 2 files changed, 98 insertions(+), 85 deletions(-) diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index 9317d57..a4ae0db 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -16,7 +16,7 @@ use sequencer_runner::startup_sequencer; use tempfile::TempDir; use tokio::task::JoinHandle; -use crate::test_suite_map::prepare_function_map; +use crate::test_suite_map::{prepare_function_map, tps_test}; #[macro_use] extern crate proc_macro_test_attribute; @@ -99,7 +99,6 @@ pub async fn post_test(residual: (ServerHandle, JoinHandle>, TempDir) //So they are dropped and tempdirs will be dropped too, } - pub async fn main_tests_runner() -> Result<()> { env_logger::init(); @@ -113,9 +112,12 @@ pub async fn main_tests_runner() -> Result<()> { match test_name.as_str() { "all" => { + // Tests that use default config for (_, fn_pointer) in function_map { fn_pointer(home_dir.clone()).await; } + // Run TPS test with its own specific config + tps_test().await; } _ => { let fn_pointer = function_map.get(&test_name).expect("Unknown test name"); diff --git a/integration_tests/src/test_suite_map.rs b/integration_tests/src/test_suite_map.rs index 999f6af..1956cf6 100644 --- a/integration_tests/src/test_suite_map.rs +++ b/integration_tests/src/test_suite_map.rs @@ -1,10 +1,17 @@ -use std::{collections::HashMap, path::PathBuf, pin::Pin, time::Duration}; +use anyhow::Result; +use std::{ + collections::HashMap, + path::PathBuf, + pin::Pin, + time::{Duration, Instant}, +}; use actix_web::dev::ServerHandle; use common::sequencer_client::SequencerClient; use log::info; use nssa::{Address, ProgramDeploymentTransaction, program::Program}; use nssa_core::{NullifierPublicKey, encryption::shared_key_derivation::Secp256k1Point}; +use sequencer_runner::startup_sequencer; use tempfile::TempDir; use tokio::task::JoinHandle; use wallet::{ @@ -29,7 +36,10 @@ use wallet::{ }; use crate::{ - fetch_privacy_preserving_tx, tps_test_utils::TpsTestManager, ACC_RECEIVER, ACC_RECEIVER_PRIVATE, ACC_SENDER, ACC_SENDER_PRIVATE, NSSA_PROGRAM_FOR_TEST_DATA_CHANGER, TIME_TO_WAIT_FOR_BLOCK_SECONDS + ACC_RECEIVER, ACC_RECEIVER_PRIVATE, ACC_SENDER, ACC_SENDER_PRIVATE, + NSSA_PROGRAM_FOR_TEST_DATA_CHANGER, TIME_TO_WAIT_FOR_BLOCK_SECONDS, + fetch_privacy_preserving_tx, replace_home_dir_with_temp_dir_in_configs, + tps_test_utils::TpsTestManager, }; use crate::{post_test, pre_test, verify_commitment_is_in_state}; @@ -1572,84 +1582,85 @@ pub fn prepare_function_map() -> HashMap { function_map } -// -// #[allow(clippy::type_complexity)] -// async fn pre_tps_test( -// test: &TpsTestManager, -// ) -> Result<(ServerHandle, JoinHandle>, TempDir)> { -// info!("Generating tps test config"); -// let mut sequencer_config = test.generate_tps_test_config(); -// info!("Done"); -// -// let temp_dir_sequencer = replace_home_dir_with_temp_dir_in_configs(&mut sequencer_config); -// -// let (seq_http_server_handle, sequencer_loop_handle) = -// startup_sequencer(sequencer_config).await?; -// -// Ok(( -// seq_http_server_handle, -// sequencer_loop_handle, -// temp_dir_sequencer, -// )) -// } -// -// -// pub async fn tps_test() { -// let num_transactions = 300 * 5; -// let target_tps = 12; -// let tps_test = TpsTestManager::new(target_tps, num_transactions); -// -// let res = pre_tps_test(&tps_test); -// -// let target_time = tps_test.target_time(); -// info!("Target time: {:?} seconds", target_time.as_secs()); -// let res = pre_tps_test(&tps_test).await.unwrap(); -// -// let wallet_config = fetch_config().await.unwrap(); -// let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); -// -// info!("TPS test begin"); -// let txs = tps_test.build_public_txs(); -// let now = Instant::now(); -// -// let mut tx_hashes = vec![]; -// for (i, tx) in txs.into_iter().enumerate() { -// let tx_hash = seq_client.send_tx_public(tx).await.unwrap().tx_hash; -// info!("Sent tx {i}"); -// tx_hashes.push(tx_hash); -// } -// -// for (i, tx_hash) in tx_hashes.iter().enumerate() { -// loop { -// if now.elapsed().as_millis() > target_time.as_millis() { -// panic!("TPS test failed by timout"); -// } -// -// let tx_obj = seq_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 -// && tx_obj.transaction.is_some() -// { -// info!("Found tx {i} with hash {tx_hash}"); -// break; -// } -// } -// } -// let time_elapsed = now.elapsed().as_secs(); -// -// info!("TPS test finished successfully"); -// info!("Target TPS: {}", target_tps); -// info!( -// "Processed {} transactions in {}s", -// tx_hashes.len(), -// time_elapsed -// ); -// info!("Target time: {:?}s", target_time.as_secs()); -// -// post_test(res).await; -// } + +#[allow(clippy::type_complexity)] +async fn pre_tps_test( + test: &TpsTestManager, +) -> Result<(ServerHandle, JoinHandle>, TempDir)> { + info!("Generating tps test config"); + let mut sequencer_config = test.generate_tps_test_config(); + info!("Done"); + + let temp_dir_sequencer = replace_home_dir_with_temp_dir_in_configs(&mut sequencer_config); + + let (seq_http_server_handle, sequencer_loop_handle) = + startup_sequencer(sequencer_config).await?; + + Ok(( + seq_http_server_handle, + sequencer_loop_handle, + temp_dir_sequencer, + )) +} + +pub async fn tps_test() { + let num_transactions = 300 * 5; + let target_tps = 12; + let tps_test = TpsTestManager::new(target_tps, num_transactions); + + let res = pre_tps_test(&tps_test); + + let target_time = tps_test.target_time(); + info!("Target time: {:?} seconds", target_time.as_secs()); + let res = pre_tps_test(&tps_test).await.unwrap(); + + let wallet_config = fetch_config().await.unwrap(); + let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); + + info!("TPS test begin"); + let txs = tps_test.build_public_txs(); + let now = Instant::now(); + + let mut tx_hashes = vec![]; + for (i, tx) in txs.into_iter().enumerate() { + let tx_hash = seq_client.send_tx_public(tx).await.unwrap().tx_hash; + info!("Sent tx {i}"); + tx_hashes.push(tx_hash); + } + + for (i, tx_hash) in tx_hashes.iter().enumerate() { + loop { + if now.elapsed().as_millis() > target_time.as_millis() { + panic!("TPS test failed by timout"); + } + + let tx_obj = seq_client + .get_transaction_by_hash(tx_hash.clone()) + .await + .inspect_err(|err| { + log::warn!( + "Failed to get transaction by hash {tx_hash:#?} with error: {err:#?}" + ) + }); + + if let Ok(tx_obj) = tx_obj + && tx_obj.transaction.is_some() + { + info!("Found tx {i} with hash {tx_hash}"); + break; + } + } + } + let time_elapsed = now.elapsed().as_secs(); + + info!("TPS test finished successfully"); + info!("Target TPS: {}", target_tps); + info!( + "Processed {} transactions in {}s", + tx_hashes.len(), + time_elapsed + ); + info!("Target time: {:?}s", target_time.as_secs()); + + post_test(res).await; +} From e96bbb84a41cb761a812bb20cb01d2b7d63e4afb Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 28 Oct 2025 23:35:16 -0300 Subject: [PATCH 8/9] remove unnecesary line --- integration_tests/src/test_suite_map.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/integration_tests/src/test_suite_map.rs b/integration_tests/src/test_suite_map.rs index 1956cf6..21a488a 100644 --- a/integration_tests/src/test_suite_map.rs +++ b/integration_tests/src/test_suite_map.rs @@ -1608,8 +1608,6 @@ pub async fn tps_test() { let target_tps = 12; let tps_test = TpsTestManager::new(target_tps, num_transactions); - let res = pre_tps_test(&tps_test); - let target_time = tps_test.target_time(); info!("Target time: {:?} seconds", target_time.as_secs()); let res = pre_tps_test(&tps_test).await.unwrap(); From a048fad7cd860ae9dc711f5cb25baf68e566160a Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Fri, 7 Nov 2025 13:13:16 -0300 Subject: [PATCH 9/9] Update readme --- README.md | 468 ++---------------------------------------------------- 1 file changed, 16 insertions(+), 452 deletions(-) diff --git a/README.md b/README.md index c7536ef..e89cedd 100644 --- a/README.md +++ b/README.md @@ -1,469 +1,33 @@ # nescience-testnet -This repo serves for Nescience Node testnet +This repo serves for Nescience testnet -For more details you can read [blogpost](https://vac.dev/rlog/Nescience-state-separation-architecture/) +For more details you can read [here](https://notes.status.im/Ya2wDpIyQquoiRiuEIM8hQ?view). -For more details on node functionality [here](https://www.notion.so/5-Testnet-initial-results-analysis-18e8f96fb65c808a835cc43b7a84cddf) +# Install dependencies -# How to run -Node and sequecer require Rust installation to build. Preferable latest stable version. - -Rust can be installed as +Install build dependencies +- On Linux +```sh +apt install build-essential clang libssl-dev pkg-config +``` +- On Mac +```sh +xcode-select --install +brew install pkg-config openssl +``` +Install Rust ```sh curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh ``` -Node needs RISC0 toolchain to run. - -It can be installed as +Install Risc0 ```sh curl -L https://risczero.com/install | bash ``` -After that, before next step, you may need to restart your console, as script updates PATH variable. Next: - +Then restart your shell and run ```sh rzup install ``` - -After cloning this repository the following actions need to be done: - -Entrypoints to node and sequencer are `node_runner` and `sequencer_runner`. Both of them have a configuration of similar manner. Path to configs need to be given into runner binaries as first arguent. No other arguments have to be given. We search given directory for files "node_config.json" for node and "sequencer_config.json" for sequencer. - -With repository debug configs at `node_runner/configs/debug` and `sequencer_runner/configs/debug` are provided, you can use them, or modify as you wish. - -For sequencer: - -```yaml -{ - "home": ".", - "override_rust_log": null, - "genesis_id": 1, - "is_genesis_random": true, - "max_num_tx_in_block": 20, - "block_create_timeout_millis": 10000, - "port": 3040 -} -``` - -* "home" shows relative path to directory with datebase. -* "override_rust_log" sets env var "RUST_LOG" to achieve different log levels(if null, using present "RUST_LOG" value). -* "genesis_id" is id of genesis block. -* "is_genesis_random" - flag to randomise forst block. -* "max_num_tx_in_block" - transaction mempool limit. -* "block_create_timeout_millis" - block timeout. -* "port" - port, which sequencer will listen. - -For node: - -```yaml -{ - "home": ".", - "override_rust_log": null, - "sequencer_addr": "http://127.0.0.1:3040", - "seq_poll_timeout_secs": 10, - "port": 3041 -} -``` - -* "home" shows relative path to directory with datebase. -* "override_rust_log" sets env var "RUST_LOG" to achieve different log levels(if null, using present "RUST_LOG" value). -* "sequencer_addr" - address of sequencer. -* "seq_poll_timeout_secs" - polling interval on sequencer, in seconds. -* "port" - port, which sequencer will listen. - -To run: - -_FIRSTLY_ in sequencer_runner directory: - -```sh -RUST_LOG=info cargo run -``` - -_SECONDLY_ in node_runner directory - -```sh -RUST_LOG=info cargo run -``` - -# Node Public API - -Node exposes public API with mutable and immutable methods to create and send transactions. - -## Standards - -Node supports JSON RPC 2.0 standard, details can be seen [there](https://www.jsonrpc.org/specification). - -## API Structure - -Right now API has only one endpoint for every request('/'), and JSON RPC 2.0 standard request structure is fairly simple - -```yaml -{ - "jsonrpc": "2.0", - "id": $number_or_dontcare, - "method": $string, - "params": $object -} -``` - -Response strucuture will look as follows: - -Success: - -```yaml -{ - "jsonrpc": "2.0", - "result": $object, - "id": "dontcare" -} -``` - -There $number - integer or string "dontcare", $string - string and $object - is some JSON object. - -## Methods - -* get_block - -Get block data for specific block number. - -Request: - -```yaml -{ - "jsonrpc": "2.0", - "id": $number_or_dontcare, - "method": "get_block", - "params": { - "block_id": $number - } -} -``` - -Response: - -```yaml -{ - "jsonrpc": "2.0", - "result": { - "block": $block - }, - "id": $number_or_dontcare -} -``` - -There "block" field returns block for requested block id - -* get_last_block - -Get last block number. - -Request: - -```yaml -{ - "jsonrpc": "2.0", - "id": $number_or_dontcare, - "method": "get_last_block", - "params": {} -} -``` - -Response: - -```yaml -{ - "jsonrpc": "2.0", - "result": { - "last_block": $number - }, - "id": $number_or_dontcare -} -``` - -There "last_block" field returns number of last block - -* write_register_account - -Create new acccount with 0 public balance and no private UTXO. - -Request: - -```yaml -{ - "jsonrpc": "2.0", - "id": $number_or_dontcare, - "method": "write_register_account", - "params": {} -} -``` - -Response: - -```yaml -{ - "jsonrpc": "2.0", - "result": { - "status": $string - }, - "id": $number_or_dontcare -} -``` - -There "status" field shows address of generated account - -* show_account_public_balance - -Show account public balance, field "account_addr" can be taken from response in "write_register_account" request. - -Request: - -```yaml -{ - "jsonrpc": "2.0", - "id": $number_or_dontcare, - "method": "show_account_public_balance", - "params": { - "account_addr": $string - } -} -``` - -Response: - -```yaml -{ - "jsonrpc": "2.0", - "result": { - "addr": $string, - "balance": $number - }, - "id": $number_or_dontcare -} -``` - -Fields in response is self-explanatory. - -* write_deposit_public_balance - -Deposit public balance into account. Any amount under u64::MAX can be deposited, can overflow. -Due to hashing process(transactions currently does not have randomization factor), we can not send two deposits with same amount to one account. - -Request: - -```yaml -{ - "jsonrpc": "2.0", - "id": $number_or_dontcare, - "method": "write_deposit_public_balance", - "params": { - "account_addr": $string, - "amount": $number - } -} -``` - -Response: - -```yaml -{ - "jsonrpc": "2.0", - "result": { - "status": "success" - }, - "id": $number_or_dontcare -} -``` - -Fields in response is self-explanatory. - -* write_mint_utxo - -Mint private UTXO for account. -Due to hashing process(transactions currently does not have randomization factor), we can not send two mints with same amount to one account. - -Request: - -```yaml -{ - "jsonrpc": "2.0", - "id": $number_or_dontcare, - "method": "write_mint_utxo", - "params": { - "account_addr": $string, - "amount": $number - } -} -``` - -Response: - -```yaml -{ - "jsonrpc": "2.0", - "result": { - "status": "success", - "utxo": { - "asset": [$number], - "commitment_hash": $string, - "hash": $string - } - }, - "id": $number_or_dontcare -} -``` - -There in "utxo" field "hash" is used for viewing purposes, field "commitment_hash" is used for sending purposes. - -* show_account_utxo - -Show UTXO data for account. "utxo_hash" there can be taken from "hash" field in response for "write_mint_utxo" request - -Request: - -```yaml -{ - "jsonrpc": "2.0", - "id": $number_or_dontcare, - "method": "show_account_utxo", - "params": { - "account_addr": $string, - "utxo_hash": $string - } -} -``` - -Response: - -```yaml -{ - "jsonrpc": "2.0", - "result": { - "amount": $number, - "asset": [$number], - "hash": $string - }, - "id": $number_or_dontcare -} -``` - -Fields in response is self-explanatory. - -* write_send_utxo_private - -Send utxo from one account private balance into another(need to be different) private balance. - -Both parties are is hidden. - -Request: - -```yaml -{ - "jsonrpc": "2.0", - "id": $number_or_dontcare, - "method": "write_send_utxo_private", - "params": { - "account_addr_sender": $string, - "account_addr_receiver": $string, - "utxo_hash": $string, - "utxo_commitment": $string - } -} -``` - -Response: - -```yaml -{ - "jsonrpc": "2.0", - "result": { - "status": "success", - "utxo_result": { - "asset": [$number], - "commitment_hash": $string, - "hash": $string - } - }, - "id": $number_or_dontcare -} -``` - -Be aware, that during this action old UTXO is nullified, hence can not be used anymore, even if present in owner private state. - -* write_send_utxo_deshielded - -Send utxo from one account private balance into another(not neccesary different account) public balance. - -Sender is hidden. - -Request: - -```yaml -{ - "jsonrpc": "2.0", - "id": $number_or_dontcare, - "method": "write_send_utxo_deshielded", - "params": { - "account_addr_sender": $string, - "account_addr_receiver": $string, - "utxo_hash": $string, - "utxo_commitment": $string - } -} -``` - -Response: - -```yaml -{ - "jsonrpc": "2.0", - "result": { - "status": "success" - }, - "id": $number_or_dontcare -} -``` - -Fields in response is self-explanatory. - -* write_send_utxo_shielded - -Send amount from one account public balance into another(not neccesary different account) private balance. - -Receiver is hidden. - -Request: - -```yaml -{ - "jsonrpc": "2.0", - "id": $number_or_dontcare, - "method": "write_send_utxo_shielded", - "params": { - "account_addr_sender": $string, - "account_addr_receiver": $string, - "amount": $number - } -} -``` - -Response: - -```yaml -{ - "jsonrpc": "2.0", - "result": { - "status": "success", - "utxo_result": { - "asset": [$number], - "commitment_hash": $string, - "hash": $string - } - }, - "id": $number_or_dontcare -} -``` - -Fields in response is self-explanatory.