lssa/sequencer/service/src/service.rs
2026-03-20 00:41:05 +03:00

204 lines
6.5 KiB
Rust

use std::{collections::BTreeMap, sync::Arc};
use common::{
HashType,
block::{Block, BlockId},
transaction::NSSATransaction,
};
use jsonrpsee::{
core::async_trait,
types::{ErrorCode, ErrorObjectOwned},
};
use log::warn;
use mempool::MemPoolHandle;
use nssa::{self, program::Program};
use nssa_core::{
Commitment, MembershipProof,
account::{Account, AccountId, Nonce},
program::ProgramId,
};
use sequencer_core::{
DbError, SequencerCore, block_settlement_client::BlockSettlementClientTrait,
indexer_client::IndexerClientTrait,
};
use tokio::sync::Mutex;
pub struct SequencerService<BC: BlockSettlementClientTrait, IC: IndexerClientTrait> {
sequencer: Arc<Mutex<SequencerCore<BC, IC>>>,
mempool_handle: MemPoolHandle<NSSATransaction>,
max_block_size: u64,
}
impl<BC: BlockSettlementClientTrait, IC: IndexerClientTrait> SequencerService<BC, IC> {
pub fn new(
sequencer: Arc<Mutex<SequencerCore<BC, IC>>>,
mempool_handle: MemPoolHandle<NSSATransaction>,
max_block_size: u64,
) -> Self {
Self {
sequencer,
mempool_handle,
max_block_size,
}
}
}
#[async_trait]
impl<BC: BlockSettlementClientTrait + Send + 'static, IC: IndexerClientTrait + Send + 'static>
sequencer_service_rpc::RpcServer for SequencerService<BC, IC>
{
async fn send_transaction(&self, tx: NSSATransaction) -> Result<HashType, ErrorObjectOwned> {
// Reserve ~200 bytes for block header overhead
const BLOCK_HEADER_OVERHEAD: u64 = 200;
let tx_hash = tx.hash();
let tx_size = u64::try_from(
borsh::to_vec(&tx)
.expect("NSSATransaction BorshSerialize should never fail")
.len(),
)
.expect("Transaction size should fit in u64");
let max_tx_size = self.max_block_size.saturating_sub(BLOCK_HEADER_OVERHEAD);
if tx_size > max_tx_size {
return Err(ErrorObjectOwned::owned(
ErrorCode::InvalidParams.code(),
format!("Transaction too large: size {tx_size}, max {max_tx_size}"),
None::<()>,
));
}
let authenticated_tx = tx
.transaction_stateless_check()
.inspect_err(|err| warn!("Error at pre_check {err:#?}"))
.map_err(|err| {
ErrorObjectOwned::owned(
ErrorCode::InvalidParams.code(),
format!("{err:?}"),
None::<()>,
)
})?;
self.mempool_handle
.push(authenticated_tx)
.await
.expect("Mempool is closed, this is a bug");
Ok(tx_hash)
}
async fn check_health(&self) -> Result<(), ErrorObjectOwned> {
Ok(())
}
async fn get_block(&self, block_id: BlockId) -> Result<Option<Block>, ErrorObjectOwned> {
let sequencer = self.sequencer.lock().await;
sequencer
.block_store()
.get_block_at_id(block_id)
.map_err(|err| internal_error(&err))
}
async fn get_block_range(
&self,
start_block_id: BlockId,
end_block_id: BlockId,
) -> Result<Vec<Block>, ErrorObjectOwned> {
let sequencer = self.sequencer.lock().await;
(start_block_id..=end_block_id)
.map(|block_id| {
sequencer
.block_store()
.get_block_at_id(block_id)
.map_err(|err| internal_error(&err))
.and_then(|opt| {
opt.ok_or_else(|| {
ErrorObjectOwned::owned(
NOT_FOUND_ERROR_CODE,
format!("Block with id {block_id} not found"),
None::<()>,
)
})
})
})
.collect::<Result<Vec<_>, _>>()
}
async fn get_last_block_id(&self) -> Result<BlockId, ErrorObjectOwned> {
let sequencer = self.sequencer.lock().await;
Ok(sequencer.chain_height())
}
async fn get_account_balance(&self, account_id: AccountId) -> Result<u128, ErrorObjectOwned> {
let sequencer = self.sequencer.lock().await;
let account = sequencer.state().get_account_by_id(account_id);
Ok(account.balance)
}
async fn get_transaction(
&self,
hash: HashType,
) -> Result<Option<NSSATransaction>, ErrorObjectOwned> {
let sequencer = self.sequencer.lock().await;
Ok(sequencer.block_store().get_transaction_by_hash(hash))
}
async fn get_accounts_nonces(
&self,
account_ids: Vec<AccountId>,
) -> Result<Vec<Nonce>, ErrorObjectOwned> {
let sequencer = self.sequencer.lock().await;
let nonces = account_ids
.into_iter()
.map(|account_id| sequencer.state().get_account_by_id(account_id).nonce)
.collect();
Ok(nonces)
}
async fn get_proof_for_commitment(
&self,
commitment: Commitment,
) -> Result<MembershipProof, ErrorObjectOwned> {
let sequencer = self.sequencer.lock().await;
sequencer
.state()
.get_proof_for_commitment(&commitment)
.ok_or_else(|| {
ErrorObjectOwned::owned(
NOT_FOUND_ERROR_CODE,
"Proof for commitment not found",
None::<()>,
)
})
}
async fn get_account(&self, account_id: AccountId) -> Result<Account, ErrorObjectOwned> {
let sequencer = self.sequencer.lock().await;
Ok(sequencer.state().get_account_by_id(account_id))
}
async fn get_program_ids(&self) -> Result<BTreeMap<String, ProgramId>, ErrorObjectOwned> {
let mut program_ids = BTreeMap::new();
program_ids.insert(
"authenticated_transfer".to_owned(),
Program::authenticated_transfer_program().id(),
);
program_ids.insert("token".to_owned(), Program::token().id());
program_ids.insert("pinata".to_owned(), Program::pinata().id());
program_ids.insert("amm".to_owned(), Program::amm().id());
program_ids.insert(
"privacy_preserving_circuit".to_owned(),
nssa::PRIVACY_PRESERVING_CIRCUIT_ID,
);
Ok(program_ids)
}
}
const NOT_FOUND_ERROR_CODE: i32 = -31999;
fn internal_error(err: &DbError) -> ErrorObjectOwned {
ErrorObjectOwned::owned(ErrorCode::InternalError.code(), err.to_string(), None::<()>)
}