575 lines
18 KiB
Rust
Raw Normal View History

use std::fmt::Display;
2024-11-25 07:26:16 +02:00
use anyhow::Result;
2025-08-09 19:05:49 -03:00
use common::{block::HashableBlockData, merkle_tree_public::TreeHashType};
2025-04-16 16:17:53 +03:00
use config::SequencerConfig;
use mempool::MemPool;
use sequencer_store::SequecerChainStore;
2025-04-16 16:17:53 +03:00
use serde::{Deserialize, Serialize};
2024-11-25 07:26:16 +02:00
pub mod config;
2025-04-16 16:17:53 +03:00
pub mod sequencer_store;
2024-11-25 07:26:16 +02:00
pub struct SequencerCore {
pub store: SequecerChainStore,
2025-08-08 16:53:15 -03:00
pub mempool: MemPool<nssa::PublicTransaction>,
2024-11-25 07:26:16 +02:00
pub sequencer_config: SequencerConfig,
pub chain_height: u64,
}
2025-07-29 14:20:03 +03:00
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum TransactionMalformationErrorKind {
PublicTransactionChangedPrivateData { tx: TreeHashType },
PrivateTransactionChangedPublicData { tx: TreeHashType },
TxHashAlreadyPresentInTree { tx: TreeHashType },
NullifierAlreadyPresentInTree { tx: TreeHashType },
UTXOCommitmentAlreadyPresentInTree { tx: TreeHashType },
2025-08-07 15:19:06 -03:00
MempoolFullForRound,
2025-01-24 09:10:42 +02:00
ChainStateFurtherThanTransactionState { tx: TreeHashType },
FailedToInsert { tx: TreeHashType, details: String },
InvalidSignature,
2025-07-25 10:00:27 +03:00
IncorrectSender,
2025-07-22 15:22:20 +03:00
BalanceMismatch { tx: TreeHashType },
2025-07-28 16:09:03 -03:00
NonceMismatch { tx: TreeHashType },
2025-07-22 15:22:20 +03:00
FailedToDecode { tx: TreeHashType },
}
impl Display for TransactionMalformationErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:#?}")
}
}
impl std::error::Error for TransactionMalformationErrorKind {}
2024-11-25 07:26:16 +02:00
impl SequencerCore {
pub fn start_from_config(config: SequencerConfig) -> Self {
Self {
store: SequecerChainStore::new_with_genesis(
&config.home,
config.genesis_id,
config.is_genesis_random,
2025-07-14 10:43:35 +03:00
&config.initial_accounts,
2024-11-25 07:26:16 +02:00
),
2025-08-08 16:53:15 -03:00
mempool: MemPool::default(),
2024-11-25 07:26:16 +02:00
chain_height: config.genesis_id,
sequencer_config: config,
}
}
pub fn transaction_pre_check(
&mut self,
2025-08-07 15:19:06 -03:00
tx: nssa::PublicTransaction,
) -> Result<nssa::PublicTransaction, TransactionMalformationErrorKind> {
2025-08-09 19:05:49 -03:00
// Stateless checks here
2025-08-11 20:38:29 -03:00
if tx.witness_set().is_valid_for(tx.message()) {
2025-08-09 19:05:49 -03:00
Ok(tx)
} else {
Err(TransactionMalformationErrorKind::InvalidSignature)
}
}
pub fn push_tx_into_mempool_pre_check(
&mut self,
2025-08-07 15:19:06 -03:00
transaction: nssa::PublicTransaction,
) -> Result<(), TransactionMalformationErrorKind> {
let mempool_size = self.mempool.len();
if mempool_size >= self.sequencer_config.max_num_tx_in_block {
2025-08-07 15:19:06 -03:00
return Err(TransactionMalformationErrorKind::MempoolFullForRound);
}
2025-08-07 15:19:06 -03:00
let authenticated_tx = self.transaction_pre_check(transaction)?;
2025-08-09 18:40:32 -03:00
self.mempool.push_item(authenticated_tx);
Ok(())
}
fn execute_check_transaction_on_state(
&mut self,
2025-08-08 16:53:15 -03:00
tx: nssa::PublicTransaction,
2025-08-09 19:49:07 -03:00
) -> Result<nssa::PublicTransaction, nssa::error::NssaError> {
2025-08-07 15:19:06 -03:00
self.store.state.transition_from_public_transaction(&tx)?;
2025-08-07 15:19:06 -03:00
Ok(tx)
}
///Produces new block from transactions in mempool
pub fn produce_new_block_with_mempool_transactions(&mut self) -> Result<u64> {
let new_block_height = self.chain_height + 1;
2024-11-25 07:26:16 +02:00
let transactions = self
.mempool
.pop_size(self.sequencer_config.max_num_tx_in_block);
let valid_transactions: Vec<_> = transactions
.into_iter()
2025-08-08 16:53:15 -03:00
.filter_map(|tx| self.execute_check_transaction_on_state(tx).ok())
.collect();
let prev_block_hash = self
.store
.block_store
.get_block_at_id(self.chain_height)?
.hash;
2024-11-25 07:26:16 +02:00
let hashable_data = HashableBlockData {
block_id: new_block_height,
prev_block_id: self.chain_height,
transactions: valid_transactions,
prev_block_hash,
2024-11-25 07:26:16 +02:00
};
2025-08-05 14:59:20 +03:00
let block = hashable_data.into();
2024-11-25 07:26:16 +02:00
self.store.block_store.put_block_at_id(block)?;
2025-07-28 15:21:35 -03:00
self.chain_height = new_block_height;
2024-11-25 07:26:16 +02:00
2025-07-28 15:21:35 -03:00
Ok(self.chain_height)
2024-11-25 07:26:16 +02:00
}
}
2025-01-27 13:42:11 +01:00
#[cfg(test)]
mod tests {
2025-07-29 14:20:03 +03:00
use crate::config::AccountInitialData;
2025-01-27 13:42:11 +01:00
use super::*;
fn setup_sequencer_config_variable_initial_accounts(
2025-07-29 14:20:03 +03:00
initial_accounts: Vec<AccountInitialData>,
) -> SequencerConfig {
2025-07-29 15:29:00 -03:00
let tempdir = tempfile::tempdir().unwrap();
let home = tempdir.path().to_path_buf();
2025-01-27 13:42:11 +01:00
SequencerConfig {
2025-07-29 15:29:00 -03:00
home,
2025-01-27 13:42:11 +01:00
override_rust_log: Some("info".to_string()),
genesis_id: 1,
is_genesis_random: false,
max_num_tx_in_block: 10,
block_create_timeout_millis: 1000,
port: 8080,
initial_accounts,
2025-01-27 13:42:11 +01:00
}
}
2025-07-17 08:09:27 +03:00
fn setup_sequencer_config() -> SequencerConfig {
2025-07-29 14:20:03 +03:00
let acc1_addr = vec![
27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24,
52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143,
2025-07-29 14:20:03 +03:00
];
let acc2_addr = vec![
77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234,
216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102,
2025-07-29 14:20:03 +03:00
];
let initial_acc1 = AccountInitialData {
addr: hex::encode(acc1_addr),
balance: 10000,
};
let initial_acc2 = AccountInitialData {
addr: hex::encode(acc2_addr),
balance: 20000,
};
2025-07-25 10:00:27 +03:00
let initial_accounts = vec![initial_acc1, initial_acc2];
2025-07-17 08:09:27 +03:00
setup_sequencer_config_variable_initial_accounts(initial_accounts)
}
2025-08-07 15:19:06 -03:00
fn create_signing_key_for_account1() -> nssa::PrivateKey {
2025-08-11 20:38:29 -03:00
nssa::PrivateKey::try_new([1; 32]).unwrap()
2025-07-29 14:20:03 +03:00
}
2025-08-07 15:19:06 -03:00
fn create_signing_key_for_account2() -> nssa::PrivateKey {
2025-08-11 20:38:29 -03:00
nssa::PrivateKey::try_new([2; 32]).unwrap()
2025-07-29 14:20:03 +03:00
}
2025-06-18 13:56:09 +03:00
fn common_setup(sequencer: &mut SequencerCore) {
let tx = common::test_utils::produce_dummy_empty_transaction();
2025-08-08 16:53:15 -03:00
sequencer.mempool.push_item(tx);
2025-01-27 13:42:43 +01:00
2025-06-18 13:56:09 +03:00
sequencer
.produce_new_block_with_mempool_transactions()
.unwrap();
2025-01-27 13:42:43 +01:00
}
2025-01-27 13:42:11 +01:00
2025-01-27 13:43:17 +01:00
#[test]
fn test_start_from_config() {
let config = setup_sequencer_config();
let sequencer = SequencerCore::start_from_config(config.clone());
assert_eq!(sequencer.chain_height, config.genesis_id);
assert_eq!(sequencer.sequencer_config.max_num_tx_in_block, 10);
assert_eq!(sequencer.sequencer_config.port, 8080);
2025-07-14 10:43:35 +03:00
2025-07-29 14:20:03 +03:00
let acc1_addr = hex::decode(config.initial_accounts[0].addr.clone())
.unwrap()
.try_into()
.unwrap();
let acc2_addr = hex::decode(config.initial_accounts[1].addr.clone())
.unwrap()
.try_into()
.unwrap();
2025-07-14 10:43:35 +03:00
2025-08-08 16:53:15 -03:00
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;
2025-07-14 10:43:35 +03:00
2025-08-08 16:53:15 -03:00
assert_eq!(10000, balance_acc_1);
assert_eq!(20000, balance_acc_2);
2025-01-27 13:43:17 +01:00
}
#[test]
2025-07-25 10:00:27 +03:00
fn test_start_different_intial_accounts_balances() {
2025-08-13 03:01:54 -03:00
let acc1_addr = vec![
27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24,
52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143,
];
2025-07-29 14:20:03 +03:00
2025-08-13 03:01:54 -03:00
let acc2_addr = vec![
77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234,
216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102,
];
2025-07-29 14:20:03 +03:00
let initial_acc1 = AccountInitialData {
addr: hex::encode(acc1_addr),
balance: 10000,
};
let initial_acc2 = AccountInitialData {
addr: hex::encode(acc2_addr),
balance: 20000,
};
2025-07-25 10:00:27 +03:00
let initial_accounts = vec![initial_acc1, initial_acc2];
let config = setup_sequencer_config_variable_initial_accounts(initial_accounts);
let sequencer = SequencerCore::start_from_config(config.clone());
2025-07-29 14:20:03 +03:00
let acc1_addr = hex::decode(config.initial_accounts[0].addr.clone())
.unwrap()
.try_into()
.unwrap();
let acc2_addr = hex::decode(config.initial_accounts[1].addr.clone())
.unwrap()
.try_into()
.unwrap();
assert_eq!(
2025-07-29 14:20:03 +03:00
10000,
2025-08-08 16:53:15 -03:00
sequencer
.store
.state
.get_account_by_address(&nssa::Address::new(acc1_addr))
.balance
);
assert_eq!(
2025-07-29 14:20:03 +03:00
20000,
2025-08-08 16:53:15 -03:00
sequencer
.store
.state
.get_account_by_address(&nssa::Address::new(acc2_addr))
.balance
);
2025-01-27 13:43:17 +01:00
}
2025-01-27 13:44:07 +01:00
#[test]
fn test_transaction_pre_check_pass() {
let config = setup_sequencer_config();
let mut sequencer = SequencerCore::start_from_config(config);
common_setup(&mut sequencer);
let tx = common::test_utils::produce_dummy_empty_transaction();
2025-08-07 15:19:06 -03:00
let result = sequencer.transaction_pre_check(tx);
2025-01-27 13:44:07 +01:00
assert!(result.is_ok());
}
2025-07-29 14:20:03 +03:00
#[test]
fn test_transaction_pre_check_native_transfer_valid() {
let config = setup_sequencer_config();
let mut sequencer = SequencerCore::start_from_config(config);
common_setup(&mut sequencer);
let acc1 = hex::decode(sequencer.sequencer_config.initial_accounts[0].addr.clone())
.unwrap()
.try_into()
.unwrap();
let acc2 = hex::decode(sequencer.sequencer_config.initial_accounts[1].addr.clone())
.unwrap()
.try_into()
.unwrap();
let sign_key1 = create_signing_key_for_account1();
2025-08-09 20:25:58 -03:00
let tx = common::test_utils::create_transaction_native_token_transfer(
2025-08-05 14:59:20 +03:00
acc1, 0, acc2, 10, sign_key1,
);
2025-08-07 15:19:06 -03:00
let result = sequencer.transaction_pre_check(tx);
2025-07-29 14:20:03 +03:00
assert!(result.is_ok());
}
#[test]
fn test_transaction_pre_check_native_transfer_other_signature() {
let config = setup_sequencer_config();
let mut sequencer = SequencerCore::start_from_config(config);
common_setup(&mut sequencer);
let acc1 = hex::decode(sequencer.sequencer_config.initial_accounts[0].addr.clone())
.unwrap()
.try_into()
.unwrap();
let acc2 = hex::decode(sequencer.sequencer_config.initial_accounts[1].addr.clone())
.unwrap()
.try_into()
.unwrap();
let sign_key2 = create_signing_key_for_account2();
2025-08-09 20:25:58 -03:00
let tx = common::test_utils::create_transaction_native_token_transfer(
2025-08-05 14:59:20 +03:00
acc1, 0, acc2, 10, sign_key2,
);
2025-08-09 19:05:49 -03:00
// Signature is valid, stateless check pass
2025-08-07 15:19:06 -03:00
let tx = sequencer.transaction_pre_check(tx).unwrap();
2025-07-29 14:20:03 +03:00
2025-08-09 19:05:49 -03:00
// Signature is not from sender. Execution fails
2025-08-08 16:53:15 -03:00
let result = sequencer.execute_check_transaction_on_state(tx);
2025-08-07 15:19:06 -03:00
2025-08-09 20:25:58 -03:00
assert!(matches!(
result,
Err(nssa::error::NssaError::ProgramExecutionFailed(_))
));
2025-07-29 14:20:03 +03:00
}
#[test]
fn test_transaction_pre_check_native_transfer_sent_too_much() {
let config = setup_sequencer_config();
let mut sequencer = SequencerCore::start_from_config(config);
common_setup(&mut sequencer);
let acc1 = hex::decode(sequencer.sequencer_config.initial_accounts[0].addr.clone())
.unwrap()
.try_into()
.unwrap();
let acc2 = hex::decode(sequencer.sequencer_config.initial_accounts[1].addr.clone())
.unwrap()
.try_into()
.unwrap();
let sign_key1 = create_signing_key_for_account1();
2025-08-09 20:25:58 -03:00
let tx = common::test_utils::create_transaction_native_token_transfer(
2025-08-05 14:59:20 +03:00
acc1, 0, acc2, 10000000, sign_key1,
);
2025-08-11 08:55:08 +03:00
2025-08-07 15:19:06 -03:00
let result = sequencer.transaction_pre_check(tx);
2025-07-29 14:20:03 +03:00
2025-07-29 15:04:38 +03:00
//Passed pre-check
assert!(result.is_ok());
2025-08-08 16:53:15 -03:00
let result = sequencer.execute_check_transaction_on_state(result.unwrap());
2025-07-29 14:20:03 +03:00
let is_failed_at_balance_mismatch = matches!(
result.err().unwrap(),
2025-08-09 19:49:07 -03:00
nssa::error::NssaError::ProgramExecutionFailed(_)
2025-07-29 14:20:03 +03:00
);
assert!(is_failed_at_balance_mismatch);
}
#[test]
fn test_transaction_execute_native_transfer() {
let config = setup_sequencer_config();
let mut sequencer = SequencerCore::start_from_config(config);
common_setup(&mut sequencer);
let acc1 = hex::decode(sequencer.sequencer_config.initial_accounts[0].addr.clone())
.unwrap()
.try_into()
.unwrap();
let acc2 = hex::decode(sequencer.sequencer_config.initial_accounts[1].addr.clone())
.unwrap()
.try_into()
.unwrap();
let sign_key1 = create_signing_key_for_account1();
2025-08-09 20:25:58 -03:00
let tx = common::test_utils::create_transaction_native_token_transfer(
2025-08-05 14:59:20 +03:00
acc1, 0, acc2, 100, sign_key1,
);
2025-07-29 14:20:03 +03:00
2025-08-08 16:53:15 -03:00
sequencer.execute_check_transaction_on_state(tx).unwrap();
2025-07-29 14:20:03 +03:00
2025-08-08 16:53:15 -03:00
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;
2025-07-29 14:20:03 +03:00
assert_eq!(bal_from, 9900);
assert_eq!(bal_to, 20100);
}
#[test]
2025-07-16 10:04:23 -03:00
fn test_push_tx_into_mempool_fails_mempool_full() {
let config = SequencerConfig {
max_num_tx_in_block: 1,
..setup_sequencer_config()
};
let mut sequencer = SequencerCore::start_from_config(config);
common_setup(&mut sequencer);
let tx = common::test_utils::produce_dummy_empty_transaction();
// Fill the mempool
2025-08-08 16:53:15 -03:00
sequencer.mempool.push_item(tx.clone());
2025-08-07 15:19:06 -03:00
let result = sequencer.push_tx_into_mempool_pre_check(tx);
assert!(matches!(
result,
2025-08-09 18:40:32 -03:00
Err(TransactionMalformationErrorKind::MempoolFullForRound)
));
}
#[test]
fn test_push_tx_into_mempool_pre_check() {
let config = setup_sequencer_config();
let mut sequencer = SequencerCore::start_from_config(config);
common_setup(&mut sequencer);
let tx = common::test_utils::produce_dummy_empty_transaction();
2025-08-07 15:19:06 -03:00
let result = sequencer.push_tx_into_mempool_pre_check(tx);
assert!(result.is_ok());
assert_eq!(sequencer.mempool.len(), 1);
}
2025-01-27 13:43:17 +01:00
#[test]
fn test_produce_new_block_with_mempool_transactions() {
let config = setup_sequencer_config();
let mut sequencer = SequencerCore::start_from_config(config);
2025-07-28 15:21:35 -03:00
let genesis_height = sequencer.chain_height;
let tx = common::test_utils::produce_dummy_empty_transaction();
2025-08-08 16:53:15 -03:00
sequencer.mempool.push_item(tx);
let block_id = sequencer.produce_new_block_with_mempool_transactions();
assert!(block_id.is_ok());
2025-07-28 15:21:35 -03:00
assert_eq!(block_id.unwrap(), genesis_height + 1);
}
#[test]
2025-07-29 15:41:57 -03:00
fn test_replay_transactions_are_rejected_in_the_same_block() {
let config = setup_sequencer_config();
let mut sequencer = SequencerCore::start_from_config(config);
common_setup(&mut sequencer);
let acc1 = hex::decode(sequencer.sequencer_config.initial_accounts[0].addr.clone())
.unwrap()
.try_into()
.unwrap();
let acc2 = hex::decode(sequencer.sequencer_config.initial_accounts[1].addr.clone())
.unwrap()
.try_into()
.unwrap();
let sign_key1 = create_signing_key_for_account1();
2025-08-09 20:25:58 -03:00
let tx = common::test_utils::create_transaction_native_token_transfer(
2025-08-05 14:59:20 +03:00
acc1, 0, acc2, 100, sign_key1,
);
2025-07-29 15:41:57 -03:00
2025-08-08 16:53:15 -03:00
let tx_original = tx.clone();
let tx_replay = tx.clone();
2025-07-29 15:41:57 -03:00
// Pushing two copies of the same tx to the mempool
2025-08-08 16:53:15 -03:00
sequencer.mempool.push_item(tx_original);
sequencer.mempool.push_item(tx_replay);
2025-07-29 15:41:57 -03:00
// Create block
let current_height = sequencer
.produce_new_block_with_mempool_transactions()
.unwrap();
let block = sequencer
.store
.block_store
.get_block_at_id(current_height)
.unwrap();
// Only one should be included in the block
assert_eq!(block.transactions, vec![tx.clone()]);
}
#[test]
fn test_replay_transactions_are_rejected_in_different_blocks() {
2025-07-28 15:21:35 -03:00
let config = setup_sequencer_config();
let mut sequencer = SequencerCore::start_from_config(config);
common_setup(&mut sequencer);
let acc1 = hex::decode(sequencer.sequencer_config.initial_accounts[0].addr.clone())
.unwrap()
.try_into()
.unwrap();
let acc2 = hex::decode(sequencer.sequencer_config.initial_accounts[1].addr.clone())
.unwrap()
.try_into()
.unwrap();
let sign_key1 = create_signing_key_for_account1();
2025-08-09 20:25:58 -03:00
let tx = common::test_utils::create_transaction_native_token_transfer(
2025-08-05 14:59:20 +03:00
acc1, 0, acc2, 100, sign_key1,
);
2025-07-28 15:21:35 -03:00
// The transaction should be included the first time
2025-08-08 16:53:15 -03:00
sequencer.mempool.push_item(tx.clone());
2025-07-28 15:21:35 -03:00
let current_height = sequencer
.produce_new_block_with_mempool_transactions()
.unwrap();
let block = sequencer
.store
.block_store
.get_block_at_id(current_height)
.unwrap();
assert_eq!(block.transactions, vec![tx.clone()]);
// Add same transaction should fail
2025-08-08 16:53:15 -03:00
sequencer.mempool.push_item(tx);
2025-07-28 15:21:35 -03:00
let current_height = sequencer
.produce_new_block_with_mempool_transactions()
.unwrap();
let block = sequencer
.store
.block_store
.get_block_at_id(current_height)
.unwrap();
assert!(block.transactions.is_empty());
}
2025-01-27 13:51:27 +01:00
}