2026-05-14 21:19:25 -04:00
|
|
|
use std::{collections::HashMap, path::Path, sync::Arc};
|
2024-11-25 07:26:16 +02:00
|
|
|
|
2026-05-14 21:19:25 -04:00
|
|
|
use anyhow::{Context as _, Result};
|
2026-02-12 02:39:22 +03:00
|
|
|
use common::{
|
|
|
|
|
HashType,
|
|
|
|
|
block::{Block, BlockMeta, MantleMsgId},
|
|
|
|
|
transaction::NSSATransaction,
|
|
|
|
|
};
|
2026-05-14 21:19:25 -04:00
|
|
|
use log::info;
|
|
|
|
|
use logos_blockchain_zone_sdk::sequencer::SequencerCheckpoint;
|
2026-03-20 10:39:04 -04:00
|
|
|
use nssa::V03State;
|
2026-05-14 21:19:25 -04:00
|
|
|
pub use storage::DbResult;
|
|
|
|
|
use storage::sequencer::RocksDBIO;
|
2024-11-25 07:26:16 +02:00
|
|
|
|
2026-01-27 10:09:34 -03:00
|
|
|
pub struct SequencerStore {
|
2026-05-14 21:19:25 -04:00
|
|
|
dbio: Arc<RocksDBIO>,
|
2025-07-24 08:21:20 -03:00
|
|
|
// TODO: Consider adding the hashmap to the database for faster recovery.
|
2025-11-18 19:31:03 +03:00
|
|
|
tx_hash_to_block_map: HashMap<HashType, u64>,
|
|
|
|
|
genesis_id: u64,
|
|
|
|
|
signing_key: nssa::PrivateKey,
|
2024-11-25 07:26:16 +02:00
|
|
|
}
|
|
|
|
|
|
2026-01-27 10:09:34 -03:00
|
|
|
impl SequencerStore {
|
2026-05-14 21:19:25 -04:00
|
|
|
/// Open existing database at the given location. Fails if no database is found.
|
|
|
|
|
pub fn open_db(location: &Path, signing_key: nssa::PrivateKey) -> DbResult<Self> {
|
|
|
|
|
let dbio = Arc::new(RocksDBIO::open(location)?);
|
|
|
|
|
let genesis_id = dbio.get_meta_first_block_in_db()?;
|
|
|
|
|
let last_id = dbio.latest_block_meta()?.id;
|
|
|
|
|
|
|
|
|
|
info!("Preparing block cache");
|
|
|
|
|
let mut tx_hash_to_block_map = HashMap::new();
|
|
|
|
|
for i in genesis_id..=last_id {
|
|
|
|
|
let block = dbio
|
|
|
|
|
.get_block(i)?
|
|
|
|
|
.expect("Block should be present in the database");
|
|
|
|
|
|
|
|
|
|
tx_hash_to_block_map.extend(block_to_transactions_map(&block));
|
|
|
|
|
}
|
|
|
|
|
info!(
|
|
|
|
|
"Block cache prepared. Total blocks in cache: {}",
|
|
|
|
|
tx_hash_to_block_map.len()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
Ok(Self {
|
|
|
|
|
dbio,
|
|
|
|
|
tx_hash_to_block_map,
|
|
|
|
|
genesis_id,
|
|
|
|
|
signing_key,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-26 00:27:20 +03:00
|
|
|
/// Starting database at the start of new chain.
|
2024-11-25 07:26:16 +02:00
|
|
|
/// Creates files if necessary.
|
|
|
|
|
///
|
|
|
|
|
/// ATTENTION: Will overwrite genesis block.
|
2026-05-14 21:19:25 -04:00
|
|
|
pub fn create_db_with_genesis(
|
2025-09-03 10:29:51 +03:00
|
|
|
location: &Path,
|
2026-03-04 18:42:33 +03:00
|
|
|
genesis_block: &Block,
|
|
|
|
|
genesis_msg_id: MantleMsgId,
|
2026-05-14 21:19:25 -04:00
|
|
|
genesis_state: &V03State,
|
2025-09-03 10:29:51 +03:00
|
|
|
signing_key: nssa::PrivateKey,
|
2026-05-14 21:19:25 -04:00
|
|
|
) -> DbResult<Self> {
|
|
|
|
|
let dbio = Arc::new(RocksDBIO::create(
|
|
|
|
|
location,
|
|
|
|
|
genesis_block,
|
|
|
|
|
genesis_msg_id,
|
|
|
|
|
genesis_state,
|
|
|
|
|
)?);
|
2024-12-05 13:05:58 +02:00
|
|
|
let genesis_id = dbio.get_meta_first_block_in_db()?;
|
2026-05-14 21:19:25 -04:00
|
|
|
let tx_hash_to_block_map = block_to_transactions_map(genesis_block);
|
2024-12-05 13:05:58 +02:00
|
|
|
|
2025-07-22 10:23:52 -03:00
|
|
|
Ok(Self {
|
|
|
|
|
dbio,
|
|
|
|
|
tx_hash_to_block_map,
|
2026-03-03 23:21:08 +03:00
|
|
|
genesis_id,
|
2025-09-03 10:29:51 +03:00
|
|
|
signing_key,
|
2025-07-22 10:23:52 -03:00
|
|
|
})
|
2024-11-25 07:26:16 +02:00
|
|
|
}
|
|
|
|
|
|
2026-05-14 21:19:25 -04:00
|
|
|
/// Shared handle to the underlying rocksdb. Used to persist the zone-sdk
|
|
|
|
|
/// checkpoint from the sequencer's drive task without needing &mut to the
|
|
|
|
|
/// store.
|
|
|
|
|
#[must_use]
|
|
|
|
|
pub fn dbio(&self) -> Arc<RocksDBIO> {
|
|
|
|
|
Arc::clone(&self.dbio)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_block_at_id(&self, id: u64) -> DbResult<Option<Block>> {
|
2026-03-12 15:43:18 +03:00
|
|
|
self.dbio.get_block(id)
|
2024-11-25 07:26:16 +02:00
|
|
|
}
|
|
|
|
|
|
2026-05-14 21:19:25 -04:00
|
|
|
pub fn delete_block_at_id(&mut self, block_id: u64) -> DbResult<()> {
|
|
|
|
|
self.dbio.delete_block(block_id)
|
2025-07-22 10:23:52 -03:00
|
|
|
}
|
|
|
|
|
|
2026-05-14 21:19:25 -04:00
|
|
|
pub fn mark_block_as_finalized(&mut self, block_id: u64) -> DbResult<()> {
|
|
|
|
|
self.dbio.mark_block_as_finalized(block_id)
|
2026-02-12 18:20:25 +03:00
|
|
|
}
|
|
|
|
|
|
2025-07-22 13:21:33 -03:00
|
|
|
/// Returns the transaction corresponding to the given hash, if it exists in the blockchain.
|
2026-05-14 21:19:25 -04:00
|
|
|
#[must_use]
|
2026-01-29 22:20:42 +03:00
|
|
|
pub fn get_transaction_by_hash(&self, hash: HashType) -> Option<NSSATransaction> {
|
2026-03-13 22:38:23 +03:00
|
|
|
let block_id = *self.tx_hash_to_block_map.get(&hash)?;
|
|
|
|
|
let block = self
|
|
|
|
|
.get_block_at_id(block_id)
|
|
|
|
|
.ok()
|
|
|
|
|
.flatten()
|
|
|
|
|
.expect("Block should be present since the hash is in the map");
|
|
|
|
|
for transaction in block.body.transactions {
|
|
|
|
|
if transaction.hash() == hash {
|
|
|
|
|
return Some(transaction);
|
2025-07-22 10:23:52 -03:00
|
|
|
}
|
|
|
|
|
}
|
2026-03-13 22:38:23 +03:00
|
|
|
panic!(
|
|
|
|
|
"Transaction hash was in the map but transaction was not found in the block. This should never happen."
|
|
|
|
|
);
|
2024-11-25 07:26:16 +02:00
|
|
|
}
|
2025-11-18 19:31:03 +03:00
|
|
|
|
2026-05-14 21:19:25 -04:00
|
|
|
pub fn latest_block_meta(&self) -> DbResult<BlockMeta> {
|
|
|
|
|
self.dbio.latest_block_meta()
|
2025-11-18 19:31:03 +03:00
|
|
|
}
|
|
|
|
|
|
2026-05-14 21:19:25 -04:00
|
|
|
#[must_use]
|
2026-03-09 18:27:56 +03:00
|
|
|
pub const fn genesis_id(&self) -> u64 {
|
2025-11-18 19:31:03 +03:00
|
|
|
self.genesis_id
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-14 21:19:25 -04:00
|
|
|
#[must_use]
|
2026-03-09 18:27:56 +03:00
|
|
|
pub const fn signing_key(&self) -> &nssa::PrivateKey {
|
2025-11-18 19:31:03 +03:00
|
|
|
&self.signing_key
|
|
|
|
|
}
|
2026-01-22 02:01:02 -03:00
|
|
|
|
2026-05-14 21:19:25 -04:00
|
|
|
pub fn get_all_blocks(&self) -> impl Iterator<Item = DbResult<Block>> {
|
|
|
|
|
self.dbio.get_all_blocks()
|
2026-01-22 02:01:02 -03:00
|
|
|
}
|
2026-01-27 01:20:17 -03:00
|
|
|
|
2026-02-12 02:39:22 +03:00
|
|
|
pub(crate) fn update(
|
|
|
|
|
&mut self,
|
|
|
|
|
block: &Block,
|
|
|
|
|
msg_id: MantleMsgId,
|
2026-03-20 10:39:04 -04:00
|
|
|
state: &V03State,
|
2026-05-14 21:19:25 -04:00
|
|
|
) -> DbResult<()> {
|
2026-01-31 18:12:59 -03:00
|
|
|
let new_transactions_map = block_to_transactions_map(block);
|
2026-02-12 02:39:22 +03:00
|
|
|
self.dbio.atomic_update(block, msg_id, state)?;
|
2026-01-27 10:09:34 -03:00
|
|
|
self.tx_hash_to_block_map.extend(new_transactions_map);
|
|
|
|
|
Ok(())
|
2026-01-27 01:20:17 -03:00
|
|
|
}
|
2026-01-27 16:03:21 -03:00
|
|
|
|
2026-05-14 21:19:25 -04:00
|
|
|
pub fn get_nssa_state(&self) -> DbResult<V03State> {
|
|
|
|
|
self.dbio.get_nssa_state()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_zone_checkpoint(&self) -> Result<Option<SequencerCheckpoint>> {
|
|
|
|
|
let Some(bytes) = self.dbio.get_zone_sdk_checkpoint_bytes()? else {
|
|
|
|
|
return Ok(None);
|
|
|
|
|
};
|
|
|
|
|
let checkpoint: SequencerCheckpoint = serde_json::from_slice(&bytes)
|
|
|
|
|
.context("Failed to deserialize stored zone-sdk checkpoint")?;
|
|
|
|
|
Ok(Some(checkpoint))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn set_zone_checkpoint(&self, checkpoint: &SequencerCheckpoint) -> Result<()> {
|
|
|
|
|
let bytes =
|
|
|
|
|
serde_json::to_vec(checkpoint).context("Failed to serialize zone-sdk checkpoint")?;
|
|
|
|
|
self.dbio.put_zone_sdk_checkpoint_bytes(&bytes)?;
|
|
|
|
|
Ok(())
|
2026-01-27 16:03:21 -03:00
|
|
|
}
|
2024-11-25 07:26:16 +02:00
|
|
|
}
|
2025-07-22 10:23:52 -03:00
|
|
|
|
2025-10-25 00:30:04 -03:00
|
|
|
pub(crate) fn block_to_transactions_map(block: &Block) -> HashMap<HashType, u64> {
|
2025-07-22 10:23:52 -03:00
|
|
|
block
|
2025-09-03 10:29:51 +03:00
|
|
|
.body
|
2025-07-22 10:23:52 -03:00
|
|
|
.transactions
|
|
|
|
|
.iter()
|
2025-09-03 10:29:51 +03:00
|
|
|
.map(|transaction| (transaction.hash(), block.header.block_id))
|
2025-07-22 10:23:52 -03:00
|
|
|
.collect()
|
|
|
|
|
}
|
2025-07-22 12:27:42 -03:00
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
2026-03-04 18:42:33 +03:00
|
|
|
#![expect(clippy::shadow_unrelated, reason = "We don't care about it in tests")]
|
|
|
|
|
|
2025-09-03 10:29:51 +03:00
|
|
|
use common::{block::HashableBlockData, test_utils::sequencer_sign_key_for_testing};
|
2025-07-22 12:27:42 -03:00
|
|
|
use tempfile::tempdir;
|
2025-08-10 00:53:53 -03:00
|
|
|
|
2025-11-26 00:27:20 +03:00
|
|
|
use super::*;
|
|
|
|
|
|
2025-07-22 12:27:42 -03:00
|
|
|
#[test]
|
2026-03-04 18:42:33 +03:00
|
|
|
fn get_transaction_by_hash() {
|
2025-07-22 12:27:42 -03:00
|
|
|
let temp_dir = tempdir().unwrap();
|
|
|
|
|
let path = temp_dir.path();
|
2025-08-28 12:00:04 +03:00
|
|
|
|
2025-09-03 10:29:51 +03:00
|
|
|
let signing_key = sequencer_sign_key_for_testing();
|
|
|
|
|
|
|
|
|
|
let genesis_block_hashable_data = HashableBlockData {
|
|
|
|
|
block_id: 0,
|
2026-01-29 22:20:42 +03:00
|
|
|
prev_block_hash: HashType([0; 32]),
|
2025-09-03 10:29:51 +03:00
|
|
|
timestamp: 0,
|
|
|
|
|
transactions: vec![],
|
2025-07-22 13:21:33 -03:00
|
|
|
};
|
2025-09-03 10:29:51 +03:00
|
|
|
|
2026-01-29 15:21:15 -03:00
|
|
|
let genesis_block = genesis_block_hashable_data.into_pending_block(&signing_key, [0; 32]);
|
2025-07-22 13:21:33 -03:00
|
|
|
// Start an empty node store
|
2026-05-14 21:19:25 -04:00
|
|
|
let mut node_store = SequencerStore::create_db_with_genesis(
|
|
|
|
|
path,
|
|
|
|
|
&genesis_block,
|
|
|
|
|
[0; 32],
|
|
|
|
|
&testnet_initial_state::initial_state(),
|
|
|
|
|
signing_key,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
2025-08-05 14:59:20 +03:00
|
|
|
|
|
|
|
|
let tx = common::test_utils::produce_dummy_empty_transaction();
|
2025-08-12 12:18:13 -03:00
|
|
|
let block = common::test_utils::produce_dummy_block(1, None, vec![tx.clone()]);
|
2025-08-05 14:59:20 +03:00
|
|
|
|
2025-07-22 13:21:33 -03:00
|
|
|
// Try retrieve a tx that's not in the chain yet.
|
2025-08-07 15:19:06 -03:00
|
|
|
let retrieved_tx = node_store.get_transaction_by_hash(tx.hash());
|
2025-07-22 12:27:42 -03:00
|
|
|
assert_eq!(None, retrieved_tx);
|
2025-07-22 13:21:33 -03:00
|
|
|
// Add the block with the transaction
|
2026-04-07 15:55:45 -03:00
|
|
|
let dummy_state = V03State::new_with_genesis_accounts(&[], vec![], 0);
|
2026-02-12 02:39:22 +03:00
|
|
|
node_store.update(&block, [1; 32], &dummy_state).unwrap();
|
2025-07-22 13:21:33 -03:00
|
|
|
// Try again
|
2025-08-07 15:19:06 -03:00
|
|
|
let retrieved_tx = node_store.get_transaction_by_hash(tx.hash());
|
2025-07-22 13:21:33 -03:00
|
|
|
assert_eq!(Some(tx), retrieved_tx);
|
2025-07-22 12:27:42 -03:00
|
|
|
}
|
2026-02-11 23:47:36 +03:00
|
|
|
|
|
|
|
|
#[test]
|
2026-03-04 18:42:33 +03:00
|
|
|
fn latest_block_meta_returns_genesis_meta_initially() {
|
2026-02-11 23:47:36 +03:00
|
|
|
let temp_dir = tempdir().unwrap();
|
|
|
|
|
let path = temp_dir.path();
|
|
|
|
|
|
|
|
|
|
let signing_key = sequencer_sign_key_for_testing();
|
|
|
|
|
|
|
|
|
|
let genesis_block_hashable_data = HashableBlockData {
|
|
|
|
|
block_id: 0,
|
|
|
|
|
prev_block_hash: HashType([0; 32]),
|
|
|
|
|
timestamp: 0,
|
|
|
|
|
transactions: vec![],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let genesis_block = genesis_block_hashable_data.into_pending_block(&signing_key, [0; 32]);
|
|
|
|
|
let genesis_hash = genesis_block.header.hash;
|
|
|
|
|
|
2026-05-14 21:19:25 -04:00
|
|
|
let node_store = SequencerStore::create_db_with_genesis(
|
|
|
|
|
path,
|
|
|
|
|
&genesis_block,
|
|
|
|
|
[0; 32],
|
|
|
|
|
&testnet_initial_state::initial_state(),
|
|
|
|
|
signing_key,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
2026-02-11 23:47:36 +03:00
|
|
|
|
|
|
|
|
// Verify that initially the latest block hash equals genesis hash
|
2026-02-12 02:39:22 +03:00
|
|
|
let latest_meta = node_store.latest_block_meta().unwrap();
|
|
|
|
|
assert_eq!(latest_meta.hash, genesis_hash);
|
|
|
|
|
assert_eq!(latest_meta.msg_id, [0; 32]);
|
2026-02-11 23:47:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2026-03-04 18:42:33 +03:00
|
|
|
fn latest_block_meta_updates_after_new_block() {
|
2026-02-11 23:47:36 +03:00
|
|
|
let temp_dir = tempdir().unwrap();
|
|
|
|
|
let path = temp_dir.path();
|
|
|
|
|
|
|
|
|
|
let signing_key = sequencer_sign_key_for_testing();
|
|
|
|
|
|
|
|
|
|
let genesis_block_hashable_data = HashableBlockData {
|
|
|
|
|
block_id: 0,
|
|
|
|
|
prev_block_hash: HashType([0; 32]),
|
|
|
|
|
timestamp: 0,
|
|
|
|
|
transactions: vec![],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let genesis_block = genesis_block_hashable_data.into_pending_block(&signing_key, [0; 32]);
|
2026-05-14 21:19:25 -04:00
|
|
|
let mut node_store = SequencerStore::create_db_with_genesis(
|
|
|
|
|
path,
|
|
|
|
|
&genesis_block,
|
|
|
|
|
[0; 32],
|
|
|
|
|
&testnet_initial_state::initial_state(),
|
|
|
|
|
signing_key,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
2026-02-11 23:47:36 +03:00
|
|
|
|
|
|
|
|
// Add a new block
|
|
|
|
|
let tx = common::test_utils::produce_dummy_empty_transaction();
|
2026-03-09 18:27:56 +03:00
|
|
|
let block = common::test_utils::produce_dummy_block(1, None, vec![tx]);
|
2026-02-11 23:47:36 +03:00
|
|
|
let block_hash = block.header.hash;
|
2026-02-12 02:39:22 +03:00
|
|
|
let block_msg_id = [1; 32];
|
2026-02-11 23:47:36 +03:00
|
|
|
|
2026-04-07 15:55:45 -03:00
|
|
|
let dummy_state = V03State::new_with_genesis_accounts(&[], vec![], 0);
|
2026-02-12 02:39:22 +03:00
|
|
|
node_store
|
|
|
|
|
.update(&block, block_msg_id, &dummy_state)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
// Verify that the latest block meta now equals the new block's hash and msg_id
|
|
|
|
|
let latest_meta = node_store.latest_block_meta().unwrap();
|
|
|
|
|
assert_eq!(latest_meta.hash, block_hash);
|
|
|
|
|
assert_eq!(latest_meta.msg_id, block_msg_id);
|
2026-02-11 23:47:36 +03:00
|
|
|
}
|
2026-02-12 18:20:25 +03:00
|
|
|
|
|
|
|
|
#[test]
|
2026-03-04 18:42:33 +03:00
|
|
|
fn mark_block_finalized() {
|
2026-02-12 18:20:25 +03:00
|
|
|
let temp_dir = tempdir().unwrap();
|
|
|
|
|
let path = temp_dir.path();
|
|
|
|
|
|
|
|
|
|
let signing_key = sequencer_sign_key_for_testing();
|
|
|
|
|
|
|
|
|
|
let genesis_block_hashable_data = HashableBlockData {
|
|
|
|
|
block_id: 0,
|
|
|
|
|
prev_block_hash: HashType([0; 32]),
|
|
|
|
|
timestamp: 0,
|
|
|
|
|
transactions: vec![],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let genesis_block = genesis_block_hashable_data.into_pending_block(&signing_key, [0; 32]);
|
2026-05-14 21:19:25 -04:00
|
|
|
let mut node_store = SequencerStore::create_db_with_genesis(
|
|
|
|
|
path,
|
|
|
|
|
&genesis_block,
|
|
|
|
|
[0; 32],
|
|
|
|
|
&testnet_initial_state::initial_state(),
|
|
|
|
|
signing_key,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
2026-02-12 18:20:25 +03:00
|
|
|
|
|
|
|
|
// Add a new block with Pending status
|
|
|
|
|
let tx = common::test_utils::produce_dummy_empty_transaction();
|
2026-03-09 18:27:56 +03:00
|
|
|
let block = common::test_utils::produce_dummy_block(1, None, vec![tx]);
|
2026-02-12 18:20:25 +03:00
|
|
|
let block_id = block.header.block_id;
|
|
|
|
|
|
2026-04-07 15:55:45 -03:00
|
|
|
let dummy_state = V03State::new_with_genesis_accounts(&[], vec![], 0);
|
2026-02-12 18:20:25 +03:00
|
|
|
node_store.update(&block, [1; 32], &dummy_state).unwrap();
|
|
|
|
|
|
|
|
|
|
// Verify initial status is Pending
|
2026-03-13 22:38:23 +03:00
|
|
|
let retrieved_block = node_store.get_block_at_id(block_id).unwrap().unwrap();
|
2026-02-12 18:20:25 +03:00
|
|
|
assert!(matches!(
|
|
|
|
|
retrieved_block.bedrock_status,
|
|
|
|
|
common::block::BedrockStatus::Pending
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
// Mark block as finalized
|
|
|
|
|
node_store.mark_block_as_finalized(block_id).unwrap();
|
|
|
|
|
|
|
|
|
|
// Verify status is now Finalized
|
2026-03-13 22:38:23 +03:00
|
|
|
let finalized_block = node_store.get_block_at_id(block_id).unwrap().unwrap();
|
2026-02-12 18:20:25 +03:00
|
|
|
assert!(matches!(
|
|
|
|
|
finalized_block.bedrock_status,
|
|
|
|
|
common::block::BedrockStatus::Finalized
|
|
|
|
|
));
|
|
|
|
|
}
|
2026-05-14 21:19:25 -04:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn open_existing_db_caches_transactions() {
|
|
|
|
|
let temp_dir = tempdir().unwrap();
|
|
|
|
|
let path = temp_dir.path();
|
|
|
|
|
|
|
|
|
|
let signing_key = sequencer_sign_key_for_testing();
|
|
|
|
|
|
|
|
|
|
let genesis_block_hashable_data = HashableBlockData {
|
|
|
|
|
block_id: 0,
|
|
|
|
|
prev_block_hash: HashType([0; 32]),
|
|
|
|
|
timestamp: 0,
|
|
|
|
|
transactions: vec![],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let genesis_block = genesis_block_hashable_data.into_pending_block(&signing_key, [0; 32]);
|
|
|
|
|
let tx = common::test_utils::produce_dummy_empty_transaction();
|
|
|
|
|
{
|
|
|
|
|
// Create a scope to drop the first store after creating the db
|
|
|
|
|
let mut node_store = SequencerStore::create_db_with_genesis(
|
|
|
|
|
path,
|
|
|
|
|
&genesis_block,
|
|
|
|
|
[0; 32],
|
|
|
|
|
&testnet_initial_state::initial_state(),
|
|
|
|
|
signing_key.clone(),
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
// Add a new block
|
|
|
|
|
let block = common::test_utils::produce_dummy_block(1, None, vec![tx.clone()]);
|
|
|
|
|
node_store
|
|
|
|
|
.update(
|
|
|
|
|
&block,
|
|
|
|
|
[1; 32],
|
|
|
|
|
&V03State::new_with_genesis_accounts(&[], vec![], 0),
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Re-open the store and verify that the transaction is still retrievable (which means it
|
|
|
|
|
// was cached correctly)
|
|
|
|
|
let node_store = SequencerStore::open_db(path, signing_key).unwrap();
|
|
|
|
|
let retrieved_tx = node_store.get_transaction_by_hash(tx.hash());
|
|
|
|
|
assert_eq!(Some(tx), retrieved_tx);
|
|
|
|
|
}
|
2025-07-22 12:27:42 -03:00
|
|
|
}
|