From 20348314cfef0b0d61431bd953c1a1ec1928cd88 Mon Sep 17 00:00:00 2001 From: keroro Date: Wed, 9 Nov 2022 22:54:13 +0800 Subject: [PATCH] feat: use since to provide l1 time --- crates/block-producer/src/block_producer.rs | 62 +------ crates/block-producer/src/challenger.rs | 16 +- crates/block-producer/src/produce_block.rs | 8 +- crates/challenge/src/revert.rs | 17 +- crates/tests/src/testing_tool/chain.rs | 2 - crates/tests/src/tests/mem_block_repackage.rs | 2 - crates/utils/src/lib.rs | 2 - .../utils/src/query_l1_header_by_timestamp.rs | 155 ------------------ crates/utils/src/transaction_skeleton.rs | 10 +- 9 files changed, 17 insertions(+), 257 deletions(-) delete mode 100644 crates/utils/src/query_l1_header_by_timestamp.rs diff --git a/crates/block-producer/src/block_producer.rs b/crates/block-producer/src/block_producer.rs index d5005c416..723daf558 100644 --- a/crates/block-producer/src/block_producer.rs +++ b/crates/block-producer/src/block_producer.rs @@ -21,7 +21,6 @@ use gw_mem_pool::{ pool::{MemPool, OutputParam}, }; use gw_rpc_client::{contract::ContractsCellDepManager, rpc_client::RPCClient}; -use gw_store::traits::chain_store::ChainStore; use gw_store::Store; use gw_types::core::Timepoint; use gw_types::offchain::{global_state_from_slice, CompatibleFinalizedTimepoint}; @@ -35,9 +34,8 @@ use gw_types::{ prelude::*, }; use gw_utils::{ - fee::fill_tx_fee_with_local, genesis_info::CKBGenesisInfo, get_l1_confirmed_header, - local_cells::LocalCellsManager, query_l1_header_by_timestamp, query_rollup_cell, since::Since, - transaction_skeleton::TransactionSkeleton, wallet::Wallet, + fee::fill_tx_fee_with_local, genesis_info::CKBGenesisInfo, local_cells::LocalCellsManager, + query_rollup_cell, since::Since, transaction_skeleton::TransactionSkeleton, wallet::Wallet, }; use std::{collections::HashSet, sync::Arc, time::Instant}; use tokio::sync::Mutex; @@ -180,41 +178,10 @@ impl BlockProducer { smt.root().to_owned() }; - // By applying finality mechanism based on L1 timestamp, we assign the L1 timestamp, - // obtained from a L1 header_dep, to GlobalState.last_finalized_timepoint. - // - // But L1 chain rollback makes things difficult. It must be a relation between - // GlobalState or l2block and L1 header_dep, while L1 rollback, the relation should - // be updated in time. - // - // Currently, we choose a L1 confirmed block header to be L1 header_dep, to avoid - // L1 reorg issue. - // - // For deep reorg, we have to recover Godwoken by manually reverting L2. - let prev_global_state = self - .store - .get_block_post_global_state(&block_param.parent_block.hash().into())? - .expect("get parent post global state"); - let prev_timestamp = match Timepoint::from_full_value( - prev_global_state.last_finalized_block_number().unpack(), - ) { - Timepoint::BlockNumber(_) => 0, - Timepoint::Timestamp(ts) => { - ts + self - .generator - .rollup_context() - .rollup_config - .finality_as_duration() - } - }; - let l1_confirmed_header = get_l1_confirmed_header(&self.rpc_client, prev_timestamp).await?; - let l1_confirmed_header_timestamp = l1_confirmed_header.timestamp(); - let param = ProduceBlockParam { stake_cell_owner_lock_hash: self.wallet.lock_script().hash().into(), reverted_block_root, rollup_config_hash: self.rollup_config_hash, - l1_confirmed_header_timestamp, block_param, }; let db = self.store.begin_transaction(); @@ -274,31 +241,6 @@ impl BlockProducer { tx_skeleton .cell_deps_mut() .push(contracts_dep.omni_lock.clone().into()); - // header_deps, used for obtaining l1 time in the state-validator-script - let block_number = block.raw().number().unpack(); - if 2 <= rollup_context.determine_global_state_version(block_number) { - // Recover the l1_confirmed_header based on l1_confimed_timestamp. - // The l1_confirmed_header is referenced when producing l2block and GlobalState. - // - // The block producer would reference confirmed headers to avoid L1 reorg issue. - let finality_as_duration = rollup_context.rollup_config.finality_as_duration(); - let last_finalized_timestamp = match Timepoint::from_full_value( - global_state.last_finalized_block_number().unpack(), - ) { - Timepoint::Timestamp(last_finalized_timestamp) => last_finalized_timestamp, - Timepoint::BlockNumber(_) => { - unreachable!("2 <= global_state_version, last_finalized_timepoint should be timestamp-based"); - } - }; - let l1_confirmed_timestamp = - last_finalized_timestamp.saturating_add(finality_as_duration); - let l1_confirmed_header = - query_l1_header_by_timestamp(&self.rpc_client, l1_confirmed_timestamp).await?; - - tx_skeleton - .header_deps_mut() - .push(ckb_types::prelude::Unpack::unpack(&l1_confirmed_header.hash()).pack()); - } // Package pending revert withdrawals and custodians let db = { self.chain.lock().await.store().begin_transaction() }; diff --git a/crates/block-producer/src/challenger.rs b/crates/block-producer/src/challenger.rs index 128b7286f..910479d14 100644 --- a/crates/block-producer/src/challenger.rs +++ b/crates/block-producer/src/challenger.rs @@ -23,7 +23,7 @@ use gw_jsonrpc_types::test_mode::TestModePayload; use gw_rpc_client::contract::ContractsCellDepManager; use gw_rpc_client::rpc_client::RPCClient; use gw_types::bytes::Bytes; -use gw_types::core::{ChallengeTargetType, Status, Timepoint}; +use gw_types::core::{ChallengeTargetType, Status}; use gw_types::offchain::{ global_state_from_slice, CellInfo, InputCellInfo, RollupContext, TxStatus, }; @@ -39,7 +39,6 @@ use gw_utils::wallet::Wallet; use tokio::sync::Mutex; use tracing::instrument; -use gw_utils::get_l1_confirmed_header; use std::collections::{HashMap, HashSet}; use std::convert::TryFrom; use std::sync::Arc; @@ -451,15 +450,6 @@ impl Challenger { }; let prev_state = rollup_state.get_state().to_owned(); let burn_lock = self.config.challenger_config.burn_lock.clone().into(); - - let prev_timestamp = - match Timepoint::from_full_value(prev_state.last_finalized_block_number().unpack()) { - Timepoint::BlockNumber(_) => 0, - Timepoint::Timestamp(ts) => { - ts + self.rollup_context.rollup_config.finality_as_duration() - } - }; - let l1_confirmed_header = get_l1_confirmed_header(&self.rpc_client, prev_timestamp).await?; let revert = Revert::new( self.rollup_context.clone(), prev_state, @@ -467,7 +457,6 @@ impl Challenger { &stake_cells, burn_lock, context, - l1_confirmed_header, ); let revert_output = revert.build_output()?; @@ -492,9 +481,6 @@ impl Challenger { tx_skeleton.inputs_mut().push(rollup_state.rollup_input()); tx_skeleton.outputs_mut().push(rollup_output); tx_skeleton.witnesses_mut().push(rollup_witness); - tx_skeleton - .header_deps_mut() - .extend(revert_output.header_deps); // Challenge let challenge_input = to_input_cell_info_with_since(challenge_cell, since); diff --git a/crates/block-producer/src/produce_block.rs b/crates/block-producer/src/produce_block.rs index f68465a36..5908026eb 100644 --- a/crates/block-producer/src/produce_block.rs +++ b/crates/block-producer/src/produce_block.rs @@ -44,7 +44,6 @@ pub struct ProduceBlockParam { pub stake_cell_owner_lock_hash: H256, pub reverted_block_root: H256, pub rollup_config_hash: H256, - pub l1_confirmed_header_timestamp: u64, pub block_param: BlockParam, } @@ -62,7 +61,6 @@ pub fn produce_block( stake_cell_owner_lock_hash, reverted_block_root, rollup_config_hash, - l1_confirmed_header_timestamp, block_param: BlockParam { number, @@ -167,7 +165,11 @@ pub fn produce_block( } else { let finality_as_duration = rollup_context.rollup_config.finality_as_duration(); Timepoint::from_timestamp( - l1_confirmed_header_timestamp.saturating_sub(finality_as_duration), + block + .raw() + .timestamp() + .unpack() + .saturating_sub(finality_as_duration), ) }; let global_state = GlobalState::new_builder() diff --git a/crates/challenge/src/revert.rs b/crates/challenge/src/revert.rs index f719938ba..26d42c0ab 100644 --- a/crates/challenge/src/revert.rs +++ b/crates/challenge/src/revert.rs @@ -12,7 +12,7 @@ use gw_types::packed::ChallengeLockArgsReader; use gw_types::packed::RawL2Block; use gw_types::packed::RollupRevert; use gw_types::packed::{ - Byte32, CellOutput, ChallengeLockArgs, GlobalState, RollupAction, RollupActionUnion, Script, + CellOutput, ChallengeLockArgs, GlobalState, RollupAction, RollupActionUnion, Script, WitnessArgs, }; use gw_types::{ @@ -30,7 +30,6 @@ pub struct Revert<'a> { burn_lock: Script, post_reverted_block_root: [u8; 32], revert_witness: RevertWitness, - l1_confirmed_header: ckb_types::core::HeaderView, } pub struct RevertOutput { @@ -38,7 +37,6 @@ pub struct RevertOutput { pub reward_cells: Vec<(CellOutput, Bytes)>, pub burn_cells: Vec<(CellOutput, Bytes)>, pub rollup_witness: WitnessArgs, - pub header_deps: Vec, } impl<'a> Revert<'a> { @@ -49,7 +47,6 @@ impl<'a> Revert<'a> { stake_cells: &'a [CellInfo], burn_lock: Script, revert_context: RevertContext, - l1_confirmed_header: ckb_types::core::HeaderView, ) -> Self { let reward_burn_rate = rollup_context.rollup_config.reward_burn_rate().into(); let finality_blocks = rollup_context.rollup_config.finality_blocks().unpack(); @@ -64,7 +61,6 @@ impl<'a> Revert<'a> { reward_burn_rate, post_reverted_block_root: revert_context.post_reverted_block_root.into(), revert_witness: revert_context.revert_witness, - l1_confirmed_header, } } @@ -115,7 +111,12 @@ impl<'a> Revert<'a> { .saturating_sub(self.finality_blocks), ) } else { - Timepoint::from_timestamp(self.l1_confirmed_header.timestamp()) + Timepoint::Timestamp( + first_reverted_block + .timestamp() + .unpack() + .saturating_sub(self.rollup_context.rollup_config.finality_as_duration()), + ) }; let running_status: u8 = Status::Running.into(); @@ -147,15 +148,11 @@ impl<'a> Revert<'a> { .output_type(Some(rollup_action.as_bytes()).pack()) .build(); - let header_deps = - vec![ckb_types::prelude::Unpack::unpack(&self.l1_confirmed_header.hash()).pack()]; - Ok(RevertOutput { post_global_state, reward_cells: rewards_output.reward_cells, burn_cells: rewards_output.burn_cells, rollup_witness, - header_deps, }) } } diff --git a/crates/tests/src/testing_tool/chain.rs b/crates/tests/src/testing_tool/chain.rs index 508aca1a3..0996c4490 100644 --- a/crates/tests/src/testing_tool/chain.rs +++ b/crates/tests/src/testing_tool/chain.rs @@ -638,12 +638,10 @@ pub async fn construct_block_with_timestamp( let remaining_capacity = mem_block.take_finalized_custodians_capacity(); let block_param = generate_produce_block_param(chain.store(), mem_block, post_merkle_state)?; let reverted_block_root = db.get_reverted_block_smt_root().unwrap(); - let l1_confirmed_header_timestamp = 0; let param = ProduceBlockParam { stake_cell_owner_lock_hash, rollup_config_hash, reverted_block_root, - l1_confirmed_header_timestamp, block_param, }; produce_block(db, generator, param).map(|mut r| { diff --git a/crates/tests/src/tests/mem_block_repackage.rs b/crates/tests/src/tests/mem_block_repackage.rs index b3a174cdd..e81f0b799 100644 --- a/crates/tests/src/tests/mem_block_repackage.rs +++ b/crates/tests/src/tests/mem_block_repackage.rs @@ -78,14 +78,12 @@ async fn test_repackage_mem_block() { let smt = db.reverted_block_smt().unwrap(); smt.root().to_owned() }; - let l1_confirmed_header_timestamp = 0; let param = ProduceBlockParam { stake_cell_owner_lock_hash: random_always_success_script(&rollup_script_hash) .hash() .into(), reverted_block_root, rollup_config_hash: rollup_context.rollup_config.hash().into(), - l1_confirmed_header_timestamp, block_param, }; let store = chain.store(); diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index 56dd4e71a..82295219c 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -8,7 +8,6 @@ pub mod genesis_info; pub mod liveness; pub mod local_cells; pub mod polyjuice_parser; -mod query_l1_header_by_timestamp; mod query_rollup_cell; pub mod script_log; pub mod since; @@ -17,5 +16,4 @@ pub mod wallet; pub mod withdrawal; pub use calc_finalizing_range::calc_finalizing_range; -pub use query_l1_header_by_timestamp::{get_l1_confirmed_header, query_l1_header_by_timestamp}; pub use query_rollup_cell::query_rollup_cell; diff --git a/crates/utils/src/query_l1_header_by_timestamp.rs b/crates/utils/src/query_l1_header_by_timestamp.rs deleted file mode 100644 index d650be394..000000000 --- a/crates/utils/src/query_l1_header_by_timestamp.rs +++ /dev/null @@ -1,155 +0,0 @@ -use anyhow::Result; -use gw_rpc_client::rpc_client::RPCClient; -use gw_types::prelude::Unpack; -use lazy_static::lazy_static; -use std::sync::{Arc, Mutex}; -use std::time::{Duration, Instant}; - -lazy_static! { - // https://github.com/nervosnetwork/ckb/blob/d2f0ebaab33171b3c8b65b0d521afeef1dba160d/spec/src/consensus.rs#L54 - pub static ref L1_MEDIAN_TIME_BLOCK_COUNT: u64 = 37; - pub static ref L1_CONFIRMATIONS: u64 = 100 + *L1_MEDIAN_TIME_BLOCK_COUNT; - - // L1_HEADER_CURSOR is initialized and updated by `query_l1_header_by_timestamp` which should - // only be used by the block producer to search for a matching L1 header by timestamp. - pub static ref L1_HEADER_CURSOR: Arc> = Arc::new(Mutex::new(0)); -} - -/// Returns a l1 confirmed block whose timestamp is greater than or equal to min_timestamp. -/// -/// It should only be called by the block producer when **constructs newly GlobalStates**, -/// specifically when they construct newly GlobalStates' last_finalized_timepoint. -/// -/// Block producers must ensure that a newly created GlobalState's last_finalized_timepoint is -/// higher or equal to that of the earlier GlobalState, so they expect that the returned block -/// header has a higher timestamp than `min_timestamp`. -pub async fn get_l1_confirmed_header( - rpc_client: &RPCClient, - min_timestamp: u64, -) -> Result { - let tip_number: u64 = rpc_client.get_tip().await?.number().unpack(); - let confirmed_number = tip_number.saturating_sub(*L1_CONFIRMATIONS); - for number in confirmed_number..=confirmed_number + *L1_MEDIAN_TIME_BLOCK_COUNT { - let header = rpc_client - .get_header_by_number(number) - .await? - .expect("get l1 confirmed block header"); - let header_timestamp = header.inner.timestamp.value(); - if header_timestamp >= min_timestamp { - // In practice, the `min_timestamp` is equal to - // `prev_global_state.last_finalized_timepoint + rollup_config.finality_as_duration()`. - // - // Then we can infer that: - // 1. prev_global_state's last_finalized_timepoint <= post_global_state's - // 2. prev_global_state's corresponding l1 header_dep's number <= post_global_state's - debug_assert!( - query_l1_header_by_timestamp(rpc_client, min_timestamp) - .await - .expect("debug_assert") - .number() - <= header.inner.number.value() - ); - - return Ok(header.into()); - } - } - unreachable!( - "[get_l1_confirmed_header] cannot find the target block, min_timestamp: {}, tip_number: {}", - min_timestamp, tip_number, - ); -} - -/// Query l1 header by block timestamp. -/// -/// It should only be used by the block producer, to search for a matching L1 header by -/// timestamp when **submits l1tx**. As block producer is single-threaded, we can ensure that this -/// function won't be called concurrently. -pub async fn query_l1_header_by_timestamp( - rpc_client: &RPCClient, - target_timestamp: u64, -) -> Result { - let start_time = Instant::now(); - let mut last_warning_time = Instant::now(); - let mut cursor = get_cursor(); - - // Initialize cursor if it has not uninitialized. - if cursor == 0 { - // Search from tip, along the lower blocks. - cursor = rpc_client.get_tip().await?.number().unpack(); - while cursor > 0 { - let header_opt = rpc_client.get_header_by_number(cursor).await?; - let is_matched = header_opt - .as_ref() - .map(|header| header.inner.timestamp.value() == target_timestamp) - .unwrap_or(false); - if is_matched { - set_cursor(cursor); - return Ok(header_opt.expect("checked").into()); - } - - tick_logging( - &start_time, - &mut last_warning_time, - target_timestamp, - cursor, - ); - cursor -= 1; - } - unreachable!(); - } - - // Search from stored cursor, along the higher blocks. - loop { - let header = match rpc_client.get_header_by_number(cursor).await? { - Some(header) => header, - None => { - unreachable!( - "[query_l1_header_by_timestamp] get_header_by_number({}) responses None, target_timestamp: {}", - cursor, target_timestamp - ); - } - }; - let is_matched = header.inner.timestamp.value() == target_timestamp; - if is_matched { - set_cursor(cursor); - return Ok(header.into()); - } - - tick_logging( - &start_time, - &mut last_warning_time, - target_timestamp, - cursor, - ); - cursor += 1; - } -} - -fn get_cursor() -> u64 { - *L1_HEADER_CURSOR - .lock() - .expect("acquire lock of L1_HEADER_CURSOR") -} - -fn set_cursor(cursor: u64) { - *L1_HEADER_CURSOR - .lock() - .expect("acquire lock of L1_HEADER_CURSOR") = cursor; -} - -fn tick_logging( - start_time: &Instant, - last_warning_time: &mut Instant, - target_timestamp: u64, - cursor: u64, -) { - if start_time.elapsed() > Duration::from_secs(60) - && last_warning_time.elapsed() > Duration::from_secs(5) - { - *last_warning_time = Instant::now(); - log::warn!( - "[query_l1_header_by_timestamp] has been a long time, target_timestamp: {}, cursor_number: {}, elapsed: {:?}", - target_timestamp, cursor, start_time.elapsed() - ); - } -} diff --git a/crates/utils/src/transaction_skeleton.rs b/crates/utils/src/transaction_skeleton.rs index 6c3c5efa8..f3f317e9f 100644 --- a/crates/utils/src/transaction_skeleton.rs +++ b/crates/utils/src/transaction_skeleton.rs @@ -5,8 +5,8 @@ use gw_types::{ bytes::Bytes, offchain::{CellInfo, InputCellInfo}, packed::{ - Byte32, CellDep, CellInput, CellOutput, OmniLockWitnessLock, OutPoint, RawTransaction, - Transaction, WitnessArgs, + CellDep, CellInput, CellOutput, OmniLockWitnessLock, OutPoint, RawTransaction, Transaction, + WitnessArgs, }, prelude::*, }; @@ -87,7 +87,6 @@ pub struct TransactionSkeleton { witnesses: Vec, cell_outputs: Vec<(CellOutput, Bytes)>, omni_lock_code_hash: Option<[u8; 32]>, - header_deps: Vec, } impl TransactionSkeleton { @@ -126,10 +125,6 @@ impl TransactionSkeleton { &mut self.witnesses } - pub fn header_deps_mut(&mut self) -> &mut Vec { - &mut self.header_deps - } - pub fn omni_lock_code_hash(&self) -> Option<&[u8; 32]> { self.omni_lock_code_hash.as_ref() } @@ -202,7 +197,6 @@ impl TransactionSkeleton { .outputs(outputs.pack()) .outputs_data(outputs_data.pack()) .cell_deps(self.cell_deps.clone().pack()) - .header_deps(self.header_deps.clone().pack()) .build(); // build witnesses