2026-02-03 11:36:07 +02:00
|
|
|
use std::{path::Path, sync::Arc};
|
|
|
|
|
|
2026-02-13 13:37:10 +02:00
|
|
|
use common::block::{BedrockStatus, Block, BlockMeta, MantleMsgId};
|
2026-03-20 10:39:04 -04:00
|
|
|
use nssa::V03State;
|
2026-02-03 11:36:07 +02:00
|
|
|
use rocksdb::{
|
|
|
|
|
BoundColumnFamily, ColumnFamilyDescriptor, DBWithThreadMode, MultiThreaded, Options, WriteBatch,
|
|
|
|
|
};
|
|
|
|
|
|
2026-03-25 16:14:33 +02:00
|
|
|
use crate::{
|
2026-04-01 08:53:45 +03:00
|
|
|
CF_BLOCK_NAME, CF_META_NAME, DB_META_FIRST_BLOCK_IN_DB_KEY, DBIO, DbResult,
|
2026-04-02 17:45:49 +03:00
|
|
|
cells::shared_cells::{BlockCell, FirstBlockCell, FirstBlockSetCell, LastBlockCell},
|
2026-03-25 16:14:33 +02:00
|
|
|
error::DbError,
|
2026-03-31 09:08:40 +03:00
|
|
|
sequencer::sequencer_cells::{
|
|
|
|
|
LastFinalizedBlockIdCell, LatestBlockMetaCellOwned, LatestBlockMetaCellRef,
|
2026-04-29 14:05:23 +02:00
|
|
|
NSSAStateCellOwned, NSSAStateCellRef, ZoneSdkCheckpointCellOwned, ZoneSdkCheckpointCellRef,
|
2026-03-31 09:08:40 +03:00
|
|
|
},
|
2026-03-25 16:14:33 +02:00
|
|
|
};
|
2026-01-23 14:09:10 +02:00
|
|
|
|
2026-03-31 09:08:40 +03:00
|
|
|
pub mod sequencer_cells;
|
|
|
|
|
|
|
|
|
|
/// Key base for storing metainformation about the last finalized block on Bedrock.
|
|
|
|
|
pub const DB_META_LAST_FINALIZED_BLOCK_ID: &str = "last_finalized_block_id";
|
|
|
|
|
/// Key base for storing metainformation about the latest block meta.
|
|
|
|
|
pub const DB_META_LATEST_BLOCK_META_KEY: &str = "latest_block_meta";
|
2026-04-29 14:05:23 +02:00
|
|
|
/// Key base for storing the zone-sdk sequencer checkpoint (opaque bytes).
|
|
|
|
|
pub const DB_META_ZONE_SDK_CHECKPOINT_KEY: &str = "zone_sdk_checkpoint";
|
2026-03-31 09:08:40 +03:00
|
|
|
|
|
|
|
|
/// Key base for storing the NSSA state.
|
|
|
|
|
pub const DB_NSSA_STATE_KEY: &str = "nssa_state";
|
|
|
|
|
|
|
|
|
|
/// Name of state column family.
|
|
|
|
|
pub const CF_NSSA_STATE_NAME: &str = "cf_nssa_state";
|
2026-01-23 14:09:10 +02:00
|
|
|
|
|
|
|
|
pub struct RocksDBIO {
|
|
|
|
|
pub db: DBWithThreadMode<MultiThreaded>,
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-01 08:53:45 +03:00
|
|
|
impl DBIO for RocksDBIO {
|
|
|
|
|
fn db(&self) -> &DBWithThreadMode<MultiThreaded> {
|
|
|
|
|
&self.db
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-23 14:09:10 +02:00
|
|
|
impl RocksDBIO {
|
2026-04-10 20:23:25 +03:00
|
|
|
pub fn open(path: &Path) -> DbResult<Self> {
|
|
|
|
|
let db_opts = Options::default();
|
|
|
|
|
Self::open_inner(path, &db_opts)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn create(
|
2026-02-13 13:37:10 +02:00
|
|
|
path: &Path,
|
2026-03-04 18:42:33 +03:00
|
|
|
genesis_block: &Block,
|
|
|
|
|
genesis_msg_id: MantleMsgId,
|
2026-04-10 20:23:25 +03:00
|
|
|
genesis_state: &V03State,
|
2026-02-13 13:37:10 +02:00
|
|
|
) -> DbResult<Self> {
|
2026-01-23 14:09:10 +02:00
|
|
|
let mut db_opts = Options::default();
|
|
|
|
|
db_opts.create_missing_column_families(true);
|
|
|
|
|
db_opts.create_if_missing(true);
|
2026-04-10 20:23:25 +03:00
|
|
|
let dbio = Self::open_inner(path, &db_opts)?;
|
2026-01-23 14:09:10 +02:00
|
|
|
|
|
|
|
|
let is_start_set = dbio.get_meta_is_first_block_set()?;
|
2026-03-09 18:27:56 +03:00
|
|
|
if !is_start_set {
|
2026-03-04 18:42:33 +03:00
|
|
|
let block_id = genesis_block.header.block_id;
|
2026-04-10 20:23:25 +03:00
|
|
|
// TODO: Shouldn't this be atomic (batched)?
|
2026-03-04 18:42:33 +03:00
|
|
|
dbio.put_meta_first_block_in_db(genesis_block, genesis_msg_id)?;
|
2026-01-23 14:09:10 +02:00
|
|
|
dbio.put_meta_is_first_block_set()?;
|
|
|
|
|
dbio.put_meta_last_block_in_db(block_id)?;
|
2026-01-27 10:02:15 +02:00
|
|
|
dbio.put_meta_last_finalized_block_id(None)?;
|
2026-02-13 13:37:10 +02:00
|
|
|
dbio.put_meta_latest_block_meta(&BlockMeta {
|
2026-03-04 18:42:33 +03:00
|
|
|
id: genesis_block.header.block_id,
|
|
|
|
|
hash: genesis_block.header.hash,
|
|
|
|
|
msg_id: genesis_msg_id,
|
2026-02-13 13:37:10 +02:00
|
|
|
})?;
|
2026-04-10 20:23:25 +03:00
|
|
|
dbio.put_nssa_state_in_db(genesis_state)?;
|
2026-01-23 14:09:10 +02:00
|
|
|
}
|
2026-03-09 18:27:56 +03:00
|
|
|
|
|
|
|
|
Ok(dbio)
|
2026-01-23 14:09:10 +02:00
|
|
|
}
|
|
|
|
|
|
2026-04-10 20:23:25 +03:00
|
|
|
fn open_inner(path: &Path, db_opts: &Options) -> DbResult<Self> {
|
|
|
|
|
let mut cf_opts = Options::default();
|
|
|
|
|
cf_opts.set_max_write_buffer_number(16);
|
|
|
|
|
|
|
|
|
|
// ToDo: Add more column families for different data
|
|
|
|
|
let cfb = ColumnFamilyDescriptor::new(CF_BLOCK_NAME, cf_opts.clone());
|
|
|
|
|
let cfmeta = ColumnFamilyDescriptor::new(CF_META_NAME, cf_opts.clone());
|
|
|
|
|
let cfstate = ColumnFamilyDescriptor::new(CF_NSSA_STATE_NAME, cf_opts.clone());
|
|
|
|
|
|
|
|
|
|
let db = DBWithThreadMode::<MultiThreaded>::open_cf_descriptors(
|
|
|
|
|
db_opts,
|
|
|
|
|
path,
|
|
|
|
|
vec![cfb, cfmeta, cfstate],
|
|
|
|
|
)
|
|
|
|
|
.map_err(|err| DbError::RocksDbError {
|
|
|
|
|
error: err,
|
|
|
|
|
additional_info: Some("Failed to open or create DB".to_owned()),
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
let dbio = Self { db };
|
|
|
|
|
Ok(dbio)
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-23 14:09:10 +02:00
|
|
|
pub fn destroy(path: &Path) -> DbResult<()> {
|
|
|
|
|
let mut cf_opts = Options::default();
|
|
|
|
|
cf_opts.set_max_write_buffer_number(16);
|
|
|
|
|
// ToDo: Add more column families for different data
|
|
|
|
|
let _cfb = ColumnFamilyDescriptor::new(CF_BLOCK_NAME, cf_opts.clone());
|
|
|
|
|
let _cfmeta = ColumnFamilyDescriptor::new(CF_META_NAME, cf_opts.clone());
|
2026-01-30 08:13:55 +02:00
|
|
|
let _cfstate = ColumnFamilyDescriptor::new(CF_NSSA_STATE_NAME, cf_opts.clone());
|
2026-01-23 14:09:10 +02:00
|
|
|
|
|
|
|
|
let mut db_opts = Options::default();
|
|
|
|
|
db_opts.create_missing_column_families(true);
|
|
|
|
|
db_opts.create_if_missing(true);
|
|
|
|
|
DBWithThreadMode::<MultiThreaded>::destroy(&db_opts, path)
|
|
|
|
|
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 13:10:31 +02:00
|
|
|
// Columns
|
|
|
|
|
|
2026-01-23 14:09:10 +02:00
|
|
|
pub fn meta_column(&self) -> Arc<BoundColumnFamily<'_>> {
|
2026-03-26 13:10:31 +02:00
|
|
|
self.db
|
|
|
|
|
.cf_handle(CF_META_NAME)
|
|
|
|
|
.expect("Meta column should exist")
|
2026-01-23 14:09:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn block_column(&self) -> Arc<BoundColumnFamily<'_>> {
|
2026-03-26 13:10:31 +02:00
|
|
|
self.db
|
|
|
|
|
.cf_handle(CF_BLOCK_NAME)
|
|
|
|
|
.expect("Block column should exist")
|
2026-01-23 14:09:10 +02:00
|
|
|
}
|
|
|
|
|
|
2026-01-30 08:13:55 +02:00
|
|
|
pub fn nssa_state_column(&self) -> Arc<BoundColumnFamily<'_>> {
|
2026-03-26 13:10:31 +02:00
|
|
|
self.db
|
|
|
|
|
.cf_handle(CF_NSSA_STATE_NAME)
|
|
|
|
|
.expect("State should exist")
|
2026-01-23 14:09:10 +02:00
|
|
|
}
|
|
|
|
|
|
2026-03-26 13:10:31 +02:00
|
|
|
// Meta
|
2026-01-23 14:09:10 +02:00
|
|
|
|
2026-03-26 13:10:31 +02:00
|
|
|
pub fn get_meta_first_block_in_db(&self) -> DbResult<u64> {
|
|
|
|
|
self.get::<FirstBlockCell>(()).map(|cell| cell.0)
|
2026-01-23 14:09:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_meta_last_block_in_db(&self) -> DbResult<u64> {
|
2026-03-26 13:10:31 +02:00
|
|
|
self.get::<LastBlockCell>(()).map(|cell| cell.0)
|
2026-01-23 14:09:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_meta_is_first_block_set(&self) -> DbResult<bool> {
|
2026-03-26 13:10:31 +02:00
|
|
|
Ok(self.get_opt::<FirstBlockSetCell>(())?.is_some())
|
2026-01-23 14:09:10 +02:00
|
|
|
}
|
|
|
|
|
|
2026-04-10 20:23:25 +03:00
|
|
|
pub fn put_nssa_state_in_db(&self, state: &V03State) -> DbResult<()> {
|
|
|
|
|
self.put(&NSSAStateCellRef(state), ())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn put_nssa_state_in_db_batch(
|
|
|
|
|
&self,
|
|
|
|
|
state: &V03State,
|
|
|
|
|
batch: &mut WriteBatch,
|
|
|
|
|
) -> DbResult<()> {
|
2026-03-26 13:10:31 +02:00
|
|
|
self.put_batch(&NSSAStateCellRef(state), (), batch)
|
2026-01-30 08:13:55 +02:00
|
|
|
}
|
|
|
|
|
|
2026-02-13 13:37:10 +02:00
|
|
|
pub fn put_meta_first_block_in_db(&self, block: &Block, msg_id: MantleMsgId) -> DbResult<()> {
|
2026-01-23 14:09:10 +02:00
|
|
|
let cf_meta = self.meta_column();
|
|
|
|
|
self.db
|
|
|
|
|
.put_cf(
|
|
|
|
|
&cf_meta,
|
|
|
|
|
borsh::to_vec(&DB_META_FIRST_BLOCK_IN_DB_KEY).map_err(|err| {
|
|
|
|
|
DbError::borsh_cast_message(
|
|
|
|
|
err,
|
2026-03-04 18:42:33 +03:00
|
|
|
Some("Failed to serialize DB_META_FIRST_BLOCK_IN_DB_KEY".to_owned()),
|
2026-01-23 14:09:10 +02:00
|
|
|
)
|
|
|
|
|
})?,
|
|
|
|
|
borsh::to_vec(&block.header.block_id).map_err(|err| {
|
|
|
|
|
DbError::borsh_cast_message(
|
|
|
|
|
err,
|
2026-03-04 18:42:33 +03:00
|
|
|
Some("Failed to serialize first block id".to_owned()),
|
2026-01-23 14:09:10 +02:00
|
|
|
)
|
|
|
|
|
})?,
|
|
|
|
|
)
|
|
|
|
|
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
|
|
|
|
|
|
2026-01-30 08:13:55 +02:00
|
|
|
let mut batch = WriteBatch::default();
|
2026-02-13 13:37:10 +02:00
|
|
|
self.put_block(block, msg_id, true, &mut batch)?;
|
2026-01-30 08:13:55 +02:00
|
|
|
self.db.write(batch).map_err(|rerr| {
|
|
|
|
|
DbError::rocksdb_cast_message(
|
|
|
|
|
rerr,
|
2026-03-04 18:42:33 +03:00
|
|
|
Some("Failed to write first block in db".to_owned()),
|
2026-01-30 08:13:55 +02:00
|
|
|
)
|
|
|
|
|
})?;
|
|
|
|
|
|
2026-01-23 14:09:10 +02:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn put_meta_last_block_in_db(&self, block_id: u64) -> DbResult<()> {
|
2026-03-26 13:10:31 +02:00
|
|
|
self.put(&LastBlockCell(block_id), ())
|
2026-01-23 14:09:10 +02:00
|
|
|
}
|
|
|
|
|
|
2026-02-13 13:37:10 +02:00
|
|
|
fn put_meta_last_block_in_db_batch(
|
|
|
|
|
&self,
|
|
|
|
|
block_id: u64,
|
|
|
|
|
batch: &mut WriteBatch,
|
|
|
|
|
) -> DbResult<()> {
|
2026-03-26 13:10:31 +02:00
|
|
|
self.put_batch(&LastBlockCell(block_id), (), batch)
|
2026-02-13 13:37:10 +02:00
|
|
|
}
|
|
|
|
|
|
2026-01-27 10:02:15 +02:00
|
|
|
pub fn put_meta_last_finalized_block_id(&self, block_id: Option<u64>) -> DbResult<()> {
|
2026-03-26 13:10:31 +02:00
|
|
|
self.put(&LastFinalizedBlockIdCell(block_id), ())
|
2026-01-27 10:02:15 +02:00
|
|
|
}
|
|
|
|
|
|
2026-01-23 14:09:10 +02:00
|
|
|
pub fn put_meta_is_first_block_set(&self) -> DbResult<()> {
|
2026-03-26 13:10:31 +02:00
|
|
|
self.put(&FirstBlockSetCell(true), ())
|
2026-01-23 14:09:10 +02:00
|
|
|
}
|
|
|
|
|
|
2026-02-13 13:37:10 +02:00
|
|
|
fn put_meta_latest_block_meta(&self, block_meta: &BlockMeta) -> DbResult<()> {
|
2026-03-26 13:10:31 +02:00
|
|
|
self.put(&LatestBlockMetaCellRef(block_meta), ())
|
2026-02-13 13:37:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn put_meta_latest_block_meta_batch(
|
|
|
|
|
&self,
|
|
|
|
|
block_meta: &BlockMeta,
|
|
|
|
|
batch: &mut WriteBatch,
|
|
|
|
|
) -> DbResult<()> {
|
2026-03-26 13:10:31 +02:00
|
|
|
self.put_batch(&LatestBlockMetaCellRef(block_meta), (), batch)
|
2026-02-13 13:37:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn latest_block_meta(&self) -> DbResult<BlockMeta> {
|
2026-03-26 13:10:31 +02:00
|
|
|
self.get::<LatestBlockMetaCellOwned>(()).map(|val| val.0)
|
2026-02-13 13:37:10 +02:00
|
|
|
}
|
|
|
|
|
|
2026-04-29 14:05:23 +02:00
|
|
|
pub fn get_zone_sdk_checkpoint_bytes(&self) -> DbResult<Option<Vec<u8>>> {
|
|
|
|
|
Ok(self
|
|
|
|
|
.get_opt::<ZoneSdkCheckpointCellOwned>(())?
|
|
|
|
|
.map(|cell| cell.0))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn put_zone_sdk_checkpoint_bytes(&self, bytes: &[u8]) -> DbResult<()> {
|
|
|
|
|
self.put(&ZoneSdkCheckpointCellRef(bytes), ())
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-13 13:37:10 +02:00
|
|
|
pub fn put_block(
|
|
|
|
|
&self,
|
|
|
|
|
block: &Block,
|
|
|
|
|
msg_id: MantleMsgId,
|
|
|
|
|
first: bool,
|
|
|
|
|
batch: &mut WriteBatch,
|
|
|
|
|
) -> DbResult<()> {
|
2026-01-23 14:09:10 +02:00
|
|
|
let cf_block = self.block_column();
|
|
|
|
|
|
|
|
|
|
if !first {
|
|
|
|
|
let last_curr_block = self.get_meta_last_block_in_db()?;
|
|
|
|
|
|
|
|
|
|
if block.header.block_id > last_curr_block {
|
2026-02-13 13:37:10 +02:00
|
|
|
self.put_meta_last_block_in_db_batch(block.header.block_id, batch)?;
|
|
|
|
|
self.put_meta_latest_block_meta_batch(
|
|
|
|
|
&BlockMeta {
|
|
|
|
|
id: block.header.block_id,
|
|
|
|
|
hash: block.header.hash,
|
|
|
|
|
msg_id,
|
|
|
|
|
},
|
|
|
|
|
batch,
|
|
|
|
|
)?;
|
2026-01-23 14:09:10 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-30 08:13:55 +02:00
|
|
|
batch.put_cf(
|
|
|
|
|
&cf_block,
|
|
|
|
|
borsh::to_vec(&block.header.block_id).map_err(|err| {
|
2026-03-04 18:42:33 +03:00
|
|
|
DbError::borsh_cast_message(err, Some("Failed to serialize block id".to_owned()))
|
2026-01-30 08:13:55 +02:00
|
|
|
})?,
|
2026-02-03 11:11:19 +02:00
|
|
|
borsh::to_vec(block).map_err(|err| {
|
2026-03-04 18:42:33 +03:00
|
|
|
DbError::borsh_cast_message(err, Some("Failed to serialize block data".to_owned()))
|
2026-01-30 08:13:55 +02:00
|
|
|
})?,
|
|
|
|
|
);
|
2026-01-23 14:09:10 +02:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-13 22:38:23 +03:00
|
|
|
pub fn get_block(&self, block_id: u64) -> DbResult<Option<Block>> {
|
2026-03-26 13:10:31 +02:00
|
|
|
self.get_opt::<BlockCell>(block_id)
|
|
|
|
|
.map(|opt| opt.map(|val| val.0))
|
2026-01-23 14:09:10 +02:00
|
|
|
}
|
|
|
|
|
|
2026-03-20 10:39:04 -04:00
|
|
|
pub fn get_nssa_state(&self) -> DbResult<V03State> {
|
2026-03-26 13:10:31 +02:00
|
|
|
self.get::<NSSAStateCellOwned>(()).map(|val| val.0)
|
2026-01-23 14:09:10 +02:00
|
|
|
}
|
2026-01-30 08:13:55 +02:00
|
|
|
|
|
|
|
|
pub fn delete_block(&self, block_id: u64) -> DbResult<()> {
|
|
|
|
|
let cf_block = self.block_column();
|
|
|
|
|
let key = borsh::to_vec(&block_id).map_err(|err| {
|
2026-03-04 18:42:33 +03:00
|
|
|
DbError::borsh_cast_message(err, Some("Failed to serialize block id".to_owned()))
|
2026-01-30 08:13:55 +02:00
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
if self
|
|
|
|
|
.db
|
|
|
|
|
.get_cf(&cf_block, &key)
|
|
|
|
|
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?
|
|
|
|
|
.is_none()
|
|
|
|
|
{
|
2026-03-13 22:38:23 +03:00
|
|
|
return Err(DbError::db_interaction_error(format!(
|
|
|
|
|
"Block with id {block_id} not found"
|
|
|
|
|
)));
|
2026-01-30 08:13:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.db
|
|
|
|
|
.delete_cf(&cf_block, key)
|
|
|
|
|
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-30 11:13:50 +02:00
|
|
|
/// Mark every pending block with `block_id <= last_finalized` as finalized.
|
|
|
|
|
/// Idempotent — already-finalized blocks are skipped.
|
|
|
|
|
pub fn clean_pending_blocks_up_to(&self, last_finalized: u64) -> DbResult<()> {
|
|
|
|
|
let pending_ids: Vec<u64> = self
|
|
|
|
|
.get_all_blocks()
|
|
|
|
|
.filter_map(Result::ok)
|
|
|
|
|
.filter(|b| matches!(b.bedrock_status, BedrockStatus::Pending))
|
|
|
|
|
.map(|b| b.header.block_id)
|
|
|
|
|
.filter(|id| *id <= last_finalized)
|
|
|
|
|
.collect();
|
|
|
|
|
for id in pending_ids {
|
|
|
|
|
self.mark_block_as_finalized(id)?;
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-13 13:37:10 +02:00
|
|
|
pub fn mark_block_as_finalized(&self, block_id: u64) -> DbResult<()> {
|
2026-03-13 22:38:23 +03:00
|
|
|
let mut block = self.get_block(block_id)?.ok_or_else(|| {
|
|
|
|
|
DbError::db_interaction_error(format!("Block with id {block_id} not found"))
|
|
|
|
|
})?;
|
2026-02-13 13:37:10 +02:00
|
|
|
block.bedrock_status = BedrockStatus::Finalized;
|
|
|
|
|
|
|
|
|
|
let cf_block = self.block_column();
|
|
|
|
|
self.db
|
|
|
|
|
.put_cf(
|
|
|
|
|
&cf_block,
|
|
|
|
|
borsh::to_vec(&block_id).map_err(|err| {
|
|
|
|
|
DbError::borsh_cast_message(
|
|
|
|
|
err,
|
2026-03-04 18:42:33 +03:00
|
|
|
Some("Failed to serialize block id".to_owned()),
|
2026-02-13 13:37:10 +02:00
|
|
|
)
|
|
|
|
|
})?,
|
|
|
|
|
borsh::to_vec(&block).map_err(|err| {
|
|
|
|
|
DbError::borsh_cast_message(
|
|
|
|
|
err,
|
2026-03-04 18:42:33 +03:00
|
|
|
Some("Failed to serialize block data".to_owned()),
|
2026-02-13 13:37:10 +02:00
|
|
|
)
|
|
|
|
|
})?,
|
|
|
|
|
)
|
|
|
|
|
.map_err(|rerr| {
|
|
|
|
|
DbError::rocksdb_cast_message(
|
|
|
|
|
rerr,
|
|
|
|
|
Some(format!("Failed to mark block {block_id} as finalized")),
|
|
|
|
|
)
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-30 08:13:55 +02:00
|
|
|
pub fn get_all_blocks(&self) -> impl Iterator<Item = DbResult<Block>> {
|
|
|
|
|
let cf_block = self.block_column();
|
|
|
|
|
self.db
|
|
|
|
|
.iterator_cf(&cf_block, rocksdb::IteratorMode::Start)
|
|
|
|
|
.map(|res| {
|
|
|
|
|
let (_key, value) = res.map_err(|rerr| {
|
|
|
|
|
DbError::rocksdb_cast_message(
|
|
|
|
|
rerr,
|
2026-03-04 18:42:33 +03:00
|
|
|
Some("Failed to get key value pair".to_owned()),
|
2026-01-30 08:13:55 +02:00
|
|
|
)
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
borsh::from_slice::<Block>(&value).map_err(|err| {
|
|
|
|
|
DbError::borsh_cast_message(
|
|
|
|
|
err,
|
2026-03-04 18:42:33 +03:00
|
|
|
Some("Failed to deserialize block data".to_owned()),
|
2026-01-30 08:13:55 +02:00
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-13 13:37:10 +02:00
|
|
|
pub fn atomic_update(
|
|
|
|
|
&self,
|
|
|
|
|
block: &Block,
|
|
|
|
|
msg_id: MantleMsgId,
|
2026-03-20 10:39:04 -04:00
|
|
|
state: &V03State,
|
2026-02-13 13:37:10 +02:00
|
|
|
) -> DbResult<()> {
|
2026-01-30 08:13:55 +02:00
|
|
|
let block_id = block.header.block_id;
|
|
|
|
|
let mut batch = WriteBatch::default();
|
2026-02-13 13:37:10 +02:00
|
|
|
self.put_block(block, msg_id, false, &mut batch)?;
|
2026-04-10 20:23:25 +03:00
|
|
|
self.put_nssa_state_in_db_batch(state, &mut batch)?;
|
2026-01-30 08:13:55 +02:00
|
|
|
self.db.write(batch).map_err(|rerr| {
|
|
|
|
|
DbError::rocksdb_cast_message(
|
|
|
|
|
rerr,
|
|
|
|
|
Some(format!("Failed to udpate db with block {block_id}")),
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
}
|
2026-02-04 14:57:38 +02:00
|
|
|
}
|