diff --git a/crates/chain/src/chain.rs b/crates/chain/src/chain.rs index 768c701c2..e876c3d2d 100644 --- a/crates/chain/src/chain.rs +++ b/crates/chain/src/chain.rs @@ -590,6 +590,7 @@ impl Chain { let block_hash = db .get_block_hash_by_number(block_number)? .context("get block hash")?; + let block = db.get_block(&block_hash)?.context("get block")?; let parent_finalized_custodians = db .get_block_post_finalized_custodian_capacity(block_number - 1) .context("get parent block remaining finalized custodians")? @@ -600,10 +601,13 @@ impl Chain { // Update finalized_custodians with the finalizing deposit requests for the current block // // The finalizing range is represents in the form of `(from, to]`. - let finalizing_range = db - .get_block_finalizing_range(&block_hash) - .context("get block finalizing range")?; - for finalizing_number in finalizing_range.range() { + let finalizing_range = calc_finalizing_range( + &self.rollup_config, + self.generator.fork_config(), + &db, + &block, + )?; + for finalizing_number in finalizing_range { let deposits = db .get_block_deposit_info_vec(finalizing_number) .context("get finalizing block deposit info vec")?; @@ -1067,16 +1071,6 @@ impl Chain { )?; db.insert_asset_scripts(deposit_asset_scripts)?; db.attach_block(l2block.clone())?; - db.set_block_finalizing_range( - &l2block.hash().into(), - &calc_finalizing_range( - &self.rollup_config, - self.generator.fork_config(), - db, - &l2block, - )? - .as_reader(), - )?; self.local_state.tip = l2block; self.local_state.last_global_state = global_state; diff --git a/crates/db/src/schema.rs b/crates/db/src/schema.rs index 17192c0a5..f12b9421d 100644 --- a/crates/db/src/schema.rs +++ b/crates/db/src/schema.rs @@ -3,7 +3,7 @@ /// Column families alias type pub type Col = u8; /// Total column number -pub const COLUMNS: u32 = 38; +pub const COLUMNS: u32 = 37; /// Column store meta data pub const COLUMN_META: Col = 0; /// Column store chain index @@ -79,8 +79,6 @@ pub const COLUMN_BLOCK_SUBMIT_TX_HASH: Col = 7; pub const COLUMN_BLOCK_DEPOSIT_INFO_VEC: Col = 16; /// block number (in big endian) -> FinalizedCustodianCapacity. pub const COLUMN_BLOCK_POST_FINALIZED_CUSTODIAN_CAPACITY: Col = 36; -/// block hash -> finalizing block number range -pub const COLUMN_BLOCK_FINALIZING_RANGE: Col = 37; /// chain id pub const META_CHAIN_ID_KEY: &[u8] = b"CHAIN_ID"; diff --git a/crates/mem-pool/src/pool.rs b/crates/mem-pool/src/pool.rs index 3038d2599..7c0ee0bba 100644 --- a/crates/mem-pool/src/pool.rs +++ b/crates/mem-pool/src/pool.rs @@ -40,6 +40,7 @@ use gw_types::{ }, prelude::{Builder, Entity, Pack, PackVec, Unpack}, }; +use gw_utils::calc_finalizing_range; use gw_utils::local_cells::LocalCellsManager; use std::{ cmp::{max, min}, @@ -388,16 +389,23 @@ impl MemPool { return Ok(Default::default()); } let snap = self.store.get_snapshot(); + let block: L2Block = snap + .get_block(&self.current_tip.0) + .context("get block")? + .ok_or_else(|| anyhow!("failed to get last block"))?; let mut c: FinalizedCustodianCapacity = snap .get_block_post_finalized_custodian_capacity(self.current_tip.1) .ok_or_else(|| anyhow!("failed to get last block post finalized custodian capacity"))? .as_reader() .unpack(); - let finalizing_range = snap - .get_block_finalizing_range(&self.current_tip.0) - .context("get tip block finalizing range")?; - for finalizing_number in finalizing_range.range() { + let finalizing_range = calc_finalizing_range( + &self.generator.rollup_context().rollup_config, + &self.generator.rollup_context().fork_config, + &snap, + &block, + )?; + for finalizing_number in finalizing_range { let finalizing_deposits = snap .get_block_deposit_info_vec(finalizing_number) .context("get last finalized block deposit")?; diff --git a/crates/store/src/traits/chain_store.rs b/crates/store/src/traits/chain_store.rs index 8e89af3b3..9112611ad 100644 --- a/crates/store/src/traits/chain_store.rs +++ b/crates/store/src/traits/chain_store.rs @@ -6,7 +6,7 @@ use gw_common::H256; use gw_db::error::Error; use gw_db::schema::{ COLUMN_ASSET_SCRIPT, COLUMN_BAD_BLOCK, COLUMN_BAD_BLOCK_CHALLENGE_TARGET, COLUMN_BLOCK, - COLUMN_BLOCK_DEPOSIT_INFO_VEC, COLUMN_BLOCK_FINALIZING_RANGE, COLUMN_BLOCK_GLOBAL_STATE, + COLUMN_BLOCK_DEPOSIT_INFO_VEC, COLUMN_BLOCK_GLOBAL_STATE, COLUMN_BLOCK_POST_FINALIZED_CUSTODIAN_CAPACITY, COLUMN_BLOCK_SUBMIT_TX, COLUMN_BLOCK_SUBMIT_TX_HASH, COLUMN_INDEX, COLUMN_MEM_POOL_TRANSACTION, COLUMN_MEM_POOL_TRANSACTION_RECEIPT, COLUMN_MEM_POOL_WITHDRAWAL, COLUMN_META, @@ -153,11 +153,6 @@ pub trait ChainStore: KVStoreRead { )) } - fn get_block_finalizing_range(&self, block_hash: &H256) -> Option { - let data = self.get(COLUMN_BLOCK_FINALIZING_RANGE, block_hash.as_slice())?; - Some(from_box_should_be_ok!(packed::FinalizingRangeReader, data)) - } - /// Get tip block hash. It may be a bad block. fn get_tip_block_hash(&self) -> Result { let slice = self diff --git a/crates/store/src/transaction/store_transaction.rs b/crates/store/src/transaction/store_transaction.rs index a87d994e0..5c753dd30 100644 --- a/crates/store/src/transaction/store_transaction.rs +++ b/crates/store/src/transaction/store_transaction.rs @@ -8,7 +8,7 @@ use gw_common::h256_ext::H256Ext; use gw_common::{merkle_utils::calculate_state_checkpoint, smt::SMT, H256}; use gw_db::schema::{ Col, COLUMN_ASSET_SCRIPT, COLUMN_BAD_BLOCK, COLUMN_BAD_BLOCK_CHALLENGE_TARGET, COLUMN_BLOCK, - COLUMN_BLOCK_DEPOSIT_INFO_VEC, COLUMN_BLOCK_FINALIZING_RANGE, COLUMN_BLOCK_GLOBAL_STATE, + COLUMN_BLOCK_DEPOSIT_INFO_VEC, COLUMN_BLOCK_GLOBAL_STATE, COLUMN_BLOCK_POST_FINALIZED_CUSTODIAN_CAPACITY, COLUMN_BLOCK_SUBMIT_TX, COLUMN_BLOCK_SUBMIT_TX_HASH, COLUMN_INDEX, COLUMN_MEM_POOL_TRANSACTION, COLUMN_MEM_POOL_TRANSACTION_RECEIPT, COLUMN_MEM_POOL_WITHDRAWAL, COLUMN_META, @@ -357,19 +357,6 @@ impl StoreTransaction { ) } - pub fn set_block_finalizing_range( - &self, - block_hash: &H256, - finalizing_range: &packed::FinalizingRangeReader, - ) -> Result<(), Error> { - self.insert_raw( - COLUMN_BLOCK_FINALIZING_RANGE, - block_hash.as_slice(), - finalizing_range.as_slice(), - )?; - Ok(()) - } - pub fn set_reverted_block_smt_root(&self, root: H256) -> Result<(), Error> { self.insert_raw( COLUMN_META, diff --git a/crates/tests/src/tests/calc_finalizing_range.rs b/crates/tests/src/tests/calc_finalizing_range.rs index 8ad3d716c..286f0f753 100644 --- a/crates/tests/src/tests/calc_finalizing_range.rs +++ b/crates/tests/src/tests/calc_finalizing_range.rs @@ -1,7 +1,6 @@ use crate::testing_tool::chain::setup_chain; use gw_config::ForkConfig; -use gw_db::schema::{COLUMN_BLOCK_GLOBAL_STATE, COLUMN_INDEX}; -use gw_store::traits::chain_store::ChainStore; +use gw_db::schema::{COLUMN_BLOCK, COLUMN_BLOCK_GLOBAL_STATE, COLUMN_INDEX}; use gw_store::traits::kv_store::KVStoreWrite; use gw_types::core::Timepoint; use gw_types::packed::{BlockMerkleState, L2Block, RawL2Block}; @@ -102,15 +101,13 @@ async fn test_calc_finalizing_range() { .unwrap(); db.insert_raw(COLUMN_INDEX, raw.number().as_slice(), &block.hash()) .unwrap(); - - let finalizing_range = - calc_finalizing_range(&rollup_config, &fork_config, db, block).unwrap(); - db.set_block_finalizing_range(&block.hash().into(), &finalizing_range.as_reader()) + db.insert_raw(COLUMN_BLOCK, &block.hash(), block.as_slice()) .unwrap(); db.commit().unwrap(); } // ## Assert + let mut last_range_end = None; let fork_height = fork_config.upgrade_global_state_version_to_v2.unwrap(); let finality_as_blocks = rollup_config.finality_blocks().unpack(); let finality_time_in_mss = rollup_config.finality_time_in_ms(); @@ -118,18 +115,13 @@ async fn test_calc_finalizing_range() { let block = &blocks[i]; let block_number = block.raw().number().unpack(); let block_timestamp = block.raw().timestamp().unpack(); - let range = chain - .store() - .get_block_finalizing_range(&block.hash().into()) - .unwrap(); - let from_block_number = range.from_block_number().unpack(); - let to_block_number = range.to_block_number().unpack(); + let range = + calc_finalizing_range(&rollup_config, &fork_config, chain.store(), block).unwrap(); - if block_number <= finality_as_blocks { - assert_eq!(0, from_block_number); - assert_eq!(from_block_number, to_block_number); + if block_number < finality_as_blocks { + assert!(range.is_empty()); } else { - for nb in range.range() { + for nb in range.clone() { if nb < fork_height { assert_eq!(nb, block_number.saturating_sub(finality_as_blocks)); } else { @@ -138,5 +130,29 @@ async fn test_calc_finalizing_range() { } } } + + if !range.is_empty() { + assert!(range.start < range.end); + if last_range_end.is_none() { + assert_eq!( + range, + (1..2), + "block_number: {}, range: {:?}, last_range_end: {:?}", + block_number, + range, + last_range_end + ); + } else { + assert_eq!( + last_range_end.unwrap(), + range.start, + "block_number: {}, range: {:?}, last_range_end: {:?}", + block_number, + range, + last_range_end + ); + } + last_range_end = Some(range.end); + } } } diff --git a/crates/types/schemas/mem_block.mol b/crates/types/schemas/mem_block.mol index b1dcbacee..49afead5a 100644 --- a/crates/types/schemas/mem_block.mol +++ b/crates/types/schemas/mem_block.mol @@ -32,16 +32,6 @@ table FinalizedCustodianCapacity { sudt: SudtCustodianVec, } -// Ensure that from_block_number <= to_block_number -// -// This represents a block number range `(from_block_number, to_block_number]`: -// - when from_block_number < to_block_number, blocks {from_block_number+1, from_block_number+2, ..., to_block_number} are finalizing; -// - when from_block_number = to_block_number, no any blocks are finalizing -struct FinalizingRange { - from_block_number: Uint64, - to_block_number: Uint64, -} - vector AccountMerkleStateVec ; table CompactMemBlock { diff --git a/crates/types/src/generated/mem_block.rs b/crates/types/src/generated/mem_block.rs index 79e9d5493..677e15b02 100644 --- a/crates/types/src/generated/mem_block.rs +++ b/crates/types/src/generated/mem_block.rs @@ -2459,169 +2459,6 @@ impl molecule::prelude::Builder for FinalizedCustodianCapacityBuilder { } } #[derive(Clone)] -pub struct FinalizingRange(molecule::bytes::Bytes); -impl ::core::fmt::LowerHex for FinalizingRange { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - use molecule::hex_string; - if f.alternate() { - write!(f, "0x")?; - } - write!(f, "{}", hex_string(self.as_slice())) - } -} -impl ::core::fmt::Debug for FinalizingRange { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({:#x})", Self::NAME, self) - } -} -impl ::core::fmt::Display for FinalizingRange { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{} {{ ", Self::NAME)?; - write!(f, "{}: {}", "from_block_number", self.from_block_number())?; - write!(f, ", {}: {}", "to_block_number", self.to_block_number())?; - write!(f, " }}") - } -} -impl ::core::default::Default for FinalizingRange { - fn default() -> Self { - let v: Vec = vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - FinalizingRange::new_unchecked(v.into()) - } -} -impl FinalizingRange { - pub const TOTAL_SIZE: usize = 16; - pub const FIELD_SIZES: [usize; 2] = [8, 8]; - pub const FIELD_COUNT: usize = 2; - pub fn from_block_number(&self) -> Uint64 { - Uint64::new_unchecked(self.0.slice(0..8)) - } - pub fn to_block_number(&self) -> Uint64 { - Uint64::new_unchecked(self.0.slice(8..16)) - } - pub fn as_reader<'r>(&'r self) -> FinalizingRangeReader<'r> { - FinalizingRangeReader::new_unchecked(self.as_slice()) - } -} -impl molecule::prelude::Entity for FinalizingRange { - type Builder = FinalizingRangeBuilder; - const NAME: &'static str = "FinalizingRange"; - fn new_unchecked(data: molecule::bytes::Bytes) -> Self { - FinalizingRange(data) - } - fn as_bytes(&self) -> molecule::bytes::Bytes { - self.0.clone() - } - fn as_slice(&self) -> &[u8] { - &self.0[..] - } - fn from_slice(slice: &[u8]) -> molecule::error::VerificationResult { - FinalizingRangeReader::from_slice(slice).map(|reader| reader.to_entity()) - } - fn from_compatible_slice(slice: &[u8]) -> molecule::error::VerificationResult { - FinalizingRangeReader::from_compatible_slice(slice).map(|reader| reader.to_entity()) - } - fn new_builder() -> Self::Builder { - ::core::default::Default::default() - } - fn as_builder(self) -> Self::Builder { - Self::new_builder() - .from_block_number(self.from_block_number()) - .to_block_number(self.to_block_number()) - } -} -#[derive(Clone, Copy)] -pub struct FinalizingRangeReader<'r>(&'r [u8]); -impl<'r> ::core::fmt::LowerHex for FinalizingRangeReader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - use molecule::hex_string; - if f.alternate() { - write!(f, "0x")?; - } - write!(f, "{}", hex_string(self.as_slice())) - } -} -impl<'r> ::core::fmt::Debug for FinalizingRangeReader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({:#x})", Self::NAME, self) - } -} -impl<'r> ::core::fmt::Display for FinalizingRangeReader<'r> { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{} {{ ", Self::NAME)?; - write!(f, "{}: {}", "from_block_number", self.from_block_number())?; - write!(f, ", {}: {}", "to_block_number", self.to_block_number())?; - write!(f, " }}") - } -} -impl<'r> FinalizingRangeReader<'r> { - pub const TOTAL_SIZE: usize = 16; - pub const FIELD_SIZES: [usize; 2] = [8, 8]; - pub const FIELD_COUNT: usize = 2; - pub fn from_block_number(&self) -> Uint64Reader<'r> { - Uint64Reader::new_unchecked(&self.as_slice()[0..8]) - } - pub fn to_block_number(&self) -> Uint64Reader<'r> { - Uint64Reader::new_unchecked(&self.as_slice()[8..16]) - } -} -impl<'r> molecule::prelude::Reader<'r> for FinalizingRangeReader<'r> { - type Entity = FinalizingRange; - const NAME: &'static str = "FinalizingRangeReader"; - fn to_entity(&self) -> Self::Entity { - Self::Entity::new_unchecked(self.as_slice().to_owned().into()) - } - fn new_unchecked(slice: &'r [u8]) -> Self { - FinalizingRangeReader(slice) - } - fn as_slice(&self) -> &'r [u8] { - self.0 - } - fn verify(slice: &[u8], _compatible: bool) -> molecule::error::VerificationResult<()> { - use molecule::verification_error as ve; - let slice_len = slice.len(); - if slice_len != Self::TOTAL_SIZE { - return ve!(Self, TotalSizeNotMatch, Self::TOTAL_SIZE, slice_len); - } - Ok(()) - } -} -#[derive(Debug, Default)] -pub struct FinalizingRangeBuilder { - pub(crate) from_block_number: Uint64, - pub(crate) to_block_number: Uint64, -} -impl FinalizingRangeBuilder { - pub const TOTAL_SIZE: usize = 16; - pub const FIELD_SIZES: [usize; 2] = [8, 8]; - pub const FIELD_COUNT: usize = 2; - pub fn from_block_number(mut self, v: Uint64) -> Self { - self.from_block_number = v; - self - } - pub fn to_block_number(mut self, v: Uint64) -> Self { - self.to_block_number = v; - self - } -} -impl molecule::prelude::Builder for FinalizingRangeBuilder { - type Entity = FinalizingRange; - const NAME: &'static str = "FinalizingRangeBuilder"; - fn expected_length(&self) -> usize { - Self::TOTAL_SIZE - } - fn write(&self, writer: &mut W) -> molecule::io::Result<()> { - writer.write_all(self.from_block_number.as_slice())?; - writer.write_all(self.to_block_number.as_slice())?; - Ok(()) - } - fn build(&self) -> Self::Entity { - let mut inner = Vec::with_capacity(self.expected_length()); - self.write(&mut inner) - .unwrap_or_else(|_| panic!("{} build should be ok", Self::NAME)); - FinalizingRange::new_unchecked(inner.into()) - } -} -#[derive(Clone)] pub struct AccountMerkleStateVec(molecule::bytes::Bytes); impl ::core::fmt::LowerHex for AccountMerkleStateVec { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { diff --git a/crates/types/src/offchain/mem_block.rs b/crates/types/src/offchain/mem_block.rs index 56b07dce1..f1a244251 100644 --- a/crates/types/src/offchain/mem_block.rs +++ b/crates/types/src/offchain/mem_block.rs @@ -1,20 +1,9 @@ use sparse_merkle_tree::H256; -use std::ops::RangeInclusive; -use crate::packed::{FinalizingRange, Script}; -use crate::prelude::Unpack; +use crate::packed::Script; pub struct SudtCustodian { pub script_hash: H256, pub amount: u128, pub script: Script, } - -impl FinalizingRange { - // Returns `(from_block_number, to_block_number]` - pub fn range(&self) -> RangeInclusive { - let from_block_number = self.from_block_number().unpack(); - let to_block_number = self.to_block_number().unpack(); - from_block_number + 1..=to_block_number - } -} diff --git a/crates/utils/src/calc_finalizing_range.rs b/crates/utils/src/calc_finalizing_range.rs index bf1608984..a598b2d70 100644 --- a/crates/utils/src/calc_finalizing_range.rs +++ b/crates/utils/src/calc_finalizing_range.rs @@ -1,84 +1,107 @@ -use anyhow::Result; +use anyhow::{Context, Result}; use gw_config::ForkConfig; -use gw_store::{traits::chain_store::ChainStore, transaction::StoreTransaction}; +use gw_store::traits::chain_store::ChainStore; use gw_types::{ core::Timepoint, offchain::CompatibleFinalizedTimepoint, - packed::{FinalizingRange, L2Block, RollupConfig}, + packed::{L2Block, RollupConfig}, prelude::*, }; +use std::ops::Range; -/// Calculates FinalizingRange for a block. +// Returns true is the block of `older_block_number` is finalized for `compatible_finalized_timepoint` +fn is_older_block_finalized( + fork_config: &ForkConfig, + db: &impl ChainStore, + compatible_finalized_timepoint: &CompatibleFinalizedTimepoint, + older_block_number: u64, +) -> Result { + let older_block_hash = db + .get_block_hash_by_number(older_block_number)? + .context("get older block hash")?; + let older_block = db + .get_block(&older_block_hash)? + .context("get older block")?; + let older_timepoint = if fork_config.use_timestamp_as_timepoint(older_block_number) { + Timepoint::from_timestamp(older_block.raw().timestamp().unpack()) + } else { + Timepoint::from_block_number(older_block_number) + }; + Ok(compatible_finalized_timepoint.is_finalized(&older_timepoint)) +} + +// Returns the highest block that is finalized for `block`. +fn find_finalized_upper_bound( + rollup_config: &RollupConfig, + fork_config: &ForkConfig, + db: &impl ChainStore, + block: &L2Block, +) -> Result { + let block_number = block.raw().number().unpack(); + let finality_blocks = rollup_config.finality_blocks().unpack(); + + // When using block number as timepoint, `block_number - finality_blocks` is the only finalizing + // block. + // + // NOTE: For simplicity, we return `block_number - finality_blocks` as an upper bound, even if + // some timestamp-as-timepoint blocks are also finalized for `block`. + // This off-chain trick is safe. + if !fork_config.use_timestamp_as_timepoint(block_number.saturating_sub(finality_blocks)) { + return Ok(block_number.saturating_sub(finality_blocks)); + } + + // When using timestamp as timepoint, binary search by block timestamps + let global_state = db + .get_block_post_global_state(&block.hash().into())? + .context("get current block global state")?; + let compatible_finalized_timepoint = + CompatibleFinalizedTimepoint::from_global_state(&global_state, finality_blocks); + let mut l = fork_config + .upgrade_global_state_version_to_v2 + .context("upgrade_global_state_version_to_v2 configuration required")?; + let mut r = block.raw().number().unpack().saturating_sub(1); + while l < r { + let mid = (r + l + 1) >> 1; + if is_older_block_finalized(fork_config, db, &compatible_finalized_timepoint, mid)? { + l = mid; + } else { + r = mid.saturating_sub(1); + } + } + + // Double check to cover edge case + if is_older_block_finalized(fork_config, db, &compatible_finalized_timepoint, l)? { + Ok(l) + } else { + assert_eq!(l, fork_config.upgrade_global_state_version_to_v2.unwrap()); + Ok(l.saturating_sub(1)) + } +} + +/// Calculates finalizing range for a block. /// /// "Block _X_ is finalizing for block _Y_" means that they meet the following criteria: /// - block _X_ is not finalized for block _Y-1_ /// - block _X_ is finalized for block _Y_ -/// -/// FinalizingRange represents a range of finalizing block numbers, in the form of (from_block_number, to_block_number]: -/// - when from_block_number < to_block_number, blocks {from_block_number+1, from_block_number+2, ..., to_block_number} are finalizing; -/// - when from_block_number = to_block_number, no any blocks are finalizing pub fn calc_finalizing_range( rollup_config: &RollupConfig, fork_config: &ForkConfig, - db: &StoreTransaction, + db: &impl ChainStore, current_block: &L2Block, -) -> Result { +) -> Result> { if current_block.raw().number().unpack() == 0 { - return Ok(Default::default()); + return Ok(0..0); } - // Construct CompatibleFinalizedTimepoint of `current_block`, used to check finality of past - // blocks, or says, filter the finalizing blocks - let current_block_hash = current_block.hash(); - let current_block_number: u64 = current_block.raw().number().unpack(); - let current_global_state = db - .get_block_post_global_state(¤t_block_hash.into())? - .expect("get current block global state"); - let compatible_finalized_timepoint = CompatibleFinalizedTimepoint::from_global_state( - ¤t_global_state, - rollup_config.finality_blocks().unpack(), - ); - - // Initialize finalizing range, `(from, to] = (parent_to, parent_to]`; - let parent_finalizing_range = if current_block_number <= 1 { - Default::default() - } else { - let parent_hash = current_block.raw().parent_block_hash(); - db.get_block_finalizing_range(&parent_hash.unpack()) - .expect("get parent block finalizing range") - }; - let from_number: u64 = parent_finalizing_range.to_block_number().unpack(); - let mut to_number = from_number; - - // Iterate to determine the finalizing range for the current block - while to_number + 1 < current_block_number { - let older_block_number = to_number + 1; - let older_block_hash = db - .get_block_hash_by_number(older_block_number)? - .expect("get finalizing block hash"); - let older_global_state = db - .get_block_post_global_state(&older_block_hash)? - .expect("get finalizing block global state"); - - // NOTE: It is determined which finality rule to apply by older block, but not the the - // current block. - let older_timepoint = if fork_config.use_timestamp_as_timepoint(older_block_number) { - // We know global_state.tip_block_timestamp is equal to l2block.timestamp - Timepoint::from_timestamp(older_global_state.tip_block_timestamp().unpack()) - } else { - Timepoint::from_block_number(older_block_number) - }; - if !compatible_finalized_timepoint.is_finalized(&older_timepoint) { - break; - } - - // This `order_block` just went from unfinalized to finalized for `current_block`. - // Iterate the next order block. - to_number += 1; - } + let parent_hash = current_block.raw().parent_block_hash().unpack(); + let parent_block = db.get_block(&parent_hash)?.context("get parent block")?; + let parent_finalized_upper_bound = + find_finalized_upper_bound(rollup_config, fork_config, db, &parent_block)?; + let current_finalized_upper_bound = + find_finalized_upper_bound(rollup_config, fork_config, db, current_block)?; - Ok(FinalizingRange::new_builder() - .from_block_number(from_number.pack()) - .to_block_number(to_number.pack()) - .build()) + // `0..=parent_finalized_upper_bound` blocks is finalized for `parent_block`, + // `0..=current_finalized_upper_bound` blocks is finalized for `current_block`, + // then `parent_finalized_upper_bound+1..=current_finalized_upper_bound` is finalizing for `current_block` + Ok(parent_finalized_upper_bound + 1..current_finalized_upper_bound + 1) }