diff --git a/.github/workflows/godwoken-tests.yml b/.github/workflows/godwoken-tests.yml new file mode 100644 index 000000000..b6ccf21ed --- /dev/null +++ b/.github/workflows/godwoken-tests.yml @@ -0,0 +1,19 @@ +name: Godwoken Tests + +on: + push: + branches: [develop, master, ci, 'v1*'] + tags: ["v*.*.*"] + pull_request: + +jobs: + godwoken-tests: + uses: godwokenrises/godwoken-tests/.github/workflows/reusable-integration-test-v1.yml@develop + with: + extra_github_env: | + MANUAL_BUILD_GODWOKEN=true + GODWOKEN_GIT_URL="https://github.com/${{ github.repository }}" + GODWOKEN_GIT_CHECKOUT=${{ github.ref }} + MANUAL_BUILD_SCRIPTS=true + SCRIPTS_GIT_URL="https://github.com/${{ github.repository }}" + SCRIPTS_GIT_CHECKOUT=${{ github.ref }} diff --git a/crates/block-producer/src/block_producer.rs b/crates/block-producer/src/block_producer.rs index 7858e370d..5e31fe83b 100644 --- a/crates/block-producer/src/block_producer.rs +++ b/crates/block-producer/src/block_producer.rs @@ -22,6 +22,8 @@ use gw_mem_pool::{ }; use gw_rpc_client::{contract::ContractsCellDepManager, rpc_client::RPCClient}; use gw_store::Store; +use gw_types::core::Timepoint; +use gw_types::offchain::{global_state_from_slice, CompatibleFinalizedTimepoint}; use gw_types::{ bytes::Bytes, offchain::{DepositInfo, InputCellInfo}, @@ -59,9 +61,20 @@ fn generate_custodian_cells( deposit_cells: &[DepositInfo], ) -> Vec<(CellOutput, Bytes)> { let block_hash: H256 = block.hash().into(); - let block_number = block.raw().number().unpack(); + let block_timepoint = { + let block_number = block.raw().number().unpack(); + if rollup_context + .fork_config + .use_timestamp_as_timepoint(block_number) + { + let block_timestamp = block.raw().timestamp().unpack(); + Timepoint::from_timestamp(block_timestamp) + } else { + Timepoint::from_block_number(block_number) + } + }; let to_custodian = |deposit| -> _ { - to_custodian_cell(rollup_context, &block_hash, block_number, deposit) + to_custodian_cell(rollup_context, &block_hash, &block_timepoint, deposit) .expect("sanitized deposit") }; @@ -168,6 +181,7 @@ impl BlockProducer { let smt = db.reverted_block_smt()?; smt.root().to_owned() }; + let param = ProduceBlockParam { stake_cell_owner_lock_hash: self.wallet.lock_script().hash().into(), reverted_block_root, @@ -332,16 +346,17 @@ impl BlockProducer { .outputs_mut() .push((generated_stake.output, generated_stake.output_data)); - let last_finalized_block_number = self - .generator - .rollup_context() - .last_finalized_block_number(block.raw().number().unpack() - 1); + let prev_global_state = global_state_from_slice(&rollup_cell.data)?; + let prev_compatible_finalized_timepoint = CompatibleFinalizedTimepoint::from_global_state( + &prev_global_state, + rollup_context.rollup_config.finality_blocks().unpack(), + ); let finalized_custodians = gw_mem_pool::custodian::query_finalized_custodians( rpc_client, &self.store.get_snapshot(), withdrawal_extras.iter().map(|w| w.request()), rollup_context, - last_finalized_block_number, + &prev_compatible_finalized_timepoint, local_cells_manager, ) .await? @@ -350,7 +365,7 @@ impl BlockProducer { local_cells_manager, rpc_client, finalized_custodians, - last_finalized_block_number, + &prev_compatible_finalized_timepoint, ) .await? .expect_any(); diff --git a/crates/block-producer/src/challenger.rs b/crates/block-producer/src/challenger.rs index d0f82588e..c529ea663 100644 --- a/crates/block-producer/src/challenger.rs +++ b/crates/block-producer/src/challenger.rs @@ -449,9 +449,8 @@ impl Challenger { }; let prev_state = rollup_state.get_state().to_owned(); let burn_lock = self.config.challenger_config.burn_lock.clone().into(); - let revert = Revert::new( - &self.rollup_context, + self.rollup_context.clone(), prev_state, &challenge_cell, &stake_cells, diff --git a/crates/block-producer/src/custodian.rs b/crates/block-producer/src/custodian.rs index 0d46793c5..45ca48d98 100644 --- a/crates/block-producer/src/custodian.rs +++ b/crates/block-producer/src/custodian.rs @@ -7,6 +7,8 @@ use gw_rpc_client::{ indexer_types::{Order, SearchKey, SearchKeyFilter}, rpc_client::{QueryResult, RPCClient}, }; +use gw_types::core::Timepoint; +use gw_types::offchain::CompatibleFinalizedTimepoint; use gw_types::{ core::ScriptHashType, offchain::{CellInfo, CollectedCustodianCells}, @@ -20,12 +22,12 @@ use tracing::instrument; pub const MAX_CUSTODIANS: usize = 50; -#[instrument(skip_all, fields(last_finalized_block_number = last_finalized_block_number))] +#[instrument(skip_all, err(Debug), fields(timepoint = ?compatible_finalized_timepoint))] pub async fn query_mergeable_custodians( local_cells_manager: &LocalCellsManager, rpc_client: &RPCClient, collected_custodians: CollectedCustodianCells, - last_finalized_block_number: u64, + compatible_finalized_timepoint: &CompatibleFinalizedTimepoint, ) -> Result> { if collected_custodians.cells_info.len() >= MAX_CUSTODIANS { return Ok(QueryResult::Full(collected_custodians)); @@ -35,7 +37,7 @@ pub async fn query_mergeable_custodians( local_cells_manager, rpc_client, collected_custodians, - last_finalized_block_number, + compatible_finalized_timepoint, MAX_CUSTODIANS, ) .await?; @@ -46,17 +48,17 @@ pub async fn query_mergeable_custodians( query_mergeable_sudt_custodians( rpc_client, query_result.expect_any(), - last_finalized_block_number, + compatible_finalized_timepoint, local_cells_manager, ) .await } -#[instrument(skip_all, fields(last_finalized_block_number = last_finalized_block_number))] +#[instrument(skip_all, err(Debug), fields(timepoint = ?compatible_finalized_timepoint))] async fn query_mergeable_sudt_custodians( rpc_client: &RPCClient, collected: CollectedCustodianCells, - last_finalized_block_number: u64, + compatible_finalized_timepoint: &CompatibleFinalizedTimepoint, local_cells_manager: &LocalCellsManager, ) -> Result> { if collected.cells_info.len() >= MAX_CUSTODIANS { @@ -67,7 +69,7 @@ async fn query_mergeable_sudt_custodians( local_cells_manager, rpc_client, collected, - last_finalized_block_number, + compatible_finalized_timepoint, MAX_CUSTODIANS, ) .await @@ -77,7 +79,7 @@ async fn query_mergeable_ckb_custodians( local_cells_manager: &LocalCellsManager, rpc_client: &RPCClient, mut collected: CollectedCustodianCells, - last_finalized_block_number: u64, + compatible_finalized_timepoint: &CompatibleFinalizedTimepoint, max_cells: usize, ) -> Result> { const MIN_MERGE_CELLS: usize = 5; @@ -136,9 +138,11 @@ async fn query_mergeable_ckb_custodians( Ok(r) => r, Err(_) => continue, }; - if custodian_lock_args_reader.deposit_block_number().unpack() - > last_finalized_block_number - { + if !compatible_finalized_timepoint.is_finalized(&Timepoint::from_full_value( + custodian_lock_args_reader + .deposit_block_timepoint() + .unpack(), + )) { continue; } @@ -172,7 +176,7 @@ pub async fn query_mergeable_sudt_custodians_cells( local_cells_manager: &LocalCellsManager, rpc_client: &RPCClient, mut collected: CollectedCustodianCells, - last_finalized_block_number: u64, + compatible_finalized_timepoint: &CompatibleFinalizedTimepoint, max_cells: usize, ) -> Result> { const MAX_MERGE_SUDTS: usize = 5; @@ -234,7 +238,7 @@ pub async fn query_mergeable_sudt_custodians_cells( }; let sudt_type_scripts = rpc_client - .query_random_sudt_type_script(last_finalized_block_number, MAX_MERGE_SUDTS) + .query_random_sudt_type_script(compatible_finalized_timepoint, MAX_MERGE_SUDTS) .await?; log::info!("merge {} random sudt type scripts", sudt_type_scripts.len()); let mut collected_set: HashSet<_> = { @@ -248,7 +252,7 @@ pub async fn query_mergeable_sudt_custodians_cells( let query_result = rpc_client .query_mergeable_sudt_custodians_cells_by_sudt_type_script( &sudt_type_script, - last_finalized_block_number, + compatible_finalized_timepoint, remain, &collected_set, ) diff --git a/crates/block-producer/src/produce_block.rs b/crates/block-producer/src/produce_block.rs index 7b0bae58f..10a542222 100644 --- a/crates/block-producer/src/produce_block.rs +++ b/crates/block-producer/src/produce_block.rs @@ -19,6 +19,7 @@ use gw_store::{ transaction::StoreTransaction, Store, }; +use gw_types::core::Timepoint; use gw_types::{ core::Status, offchain::{BlockParam, DepositInfo, FinalizedCustodianCapacity}, @@ -157,18 +158,33 @@ pub fn produce_block( .count(block_count.pack()) .build() }; - let last_finalized_block_number = - number.saturating_sub(rollup_context.rollup_config.finality_blocks().unpack()); + + let last_finalized_timepoint = if rollup_context + .fork_config + .use_timestamp_as_timepoint(number) + { + let finality_time_in_ms = rollup_context.rollup_config.finality_time_in_ms(); + Timepoint::from_timestamp( + block + .raw() + .timestamp() + .unpack() + .saturating_sub(finality_time_in_ms), + ) + } else { + let finality_as_blocks = rollup_context.rollup_config.finality_blocks().unpack(); + Timepoint::from_block_number(number.saturating_sub(finality_as_blocks)) + }; let global_state = GlobalState::new_builder() .account(post_merkle_state) .block(post_block) .tip_block_hash(block.hash().pack()) .tip_block_timestamp(block.raw().timestamp()) - .last_finalized_block_number(last_finalized_block_number.pack()) + .last_finalized_block_number(last_finalized_timepoint.full_value().pack()) .reverted_block_root(Into::<[u8; 32]>::into(reverted_block_root).pack()) .rollup_config_hash(rollup_config_hash.pack()) .status((Status::Running as u8).into()) - .version(1u8.into()) + .version(rollup_context.global_state_version(number).into()) .build(); Ok(ProduceBlockResult { block, diff --git a/crates/block-producer/src/runner.rs b/crates/block-producer/src/runner.rs index fe7ca5286..89ae21d8f 100644 --- a/crates/block-producer/src/runner.rs +++ b/crates/block-producer/src/runner.rs @@ -291,16 +291,23 @@ impl BaseInitComponents { ) }; - let opt_block_producer_config = config.block_producer.as_ref(); let mut contracts_dep_manager = None; - if opt_block_producer_config.is_some() { + let opt_block_producer_config = config.block_producer.as_ref(); + if let Some(block_producer_config) = opt_block_producer_config { use gw_rpc_client::contract::check_script; let script_config = config.consensus.contract_type_scripts.clone(); let rollup_type_script = &config.chain.rollup_type_script; + let rollup_config_cell_dep = block_producer_config.rollup_config_cell_dep.clone(); check_script(&script_config, &rollup_config, rollup_type_script)?; - contracts_dep_manager = - Some(ContractsCellDepManager::build(rpc_client.clone(), script_config).await?); + contracts_dep_manager = Some( + ContractsCellDepManager::build( + rpc_client.clone(), + script_config, + rollup_config_cell_dep, + ) + .await?, + ); } if !skip_config_check { @@ -550,7 +557,7 @@ pub async fn run(config: Config, skip_config_check: bool) -> Result<()> { } let chain = Arc::new(Mutex::new( Chain::create( - &rollup_config, + rollup_config.clone(), &config.chain.rollup_type_script.clone().into(), &config.chain, store.clone(), diff --git a/crates/block-producer/src/stake.rs b/crates/block-producer/src/stake.rs index eca24e05e..f2853758c 100644 --- a/crates/block-producer/src/stake.rs +++ b/crates/block-producer/src/stake.rs @@ -9,6 +9,8 @@ use gw_rpc_client::{ indexer_types::{Order, SearchKey, SearchKeyFilter}, rpc_client::RPCClient, }; +use gw_types::core::Timepoint; +use gw_types::offchain::CompatibleFinalizedTimepoint; use gw_types::{ core::ScriptHashType, offchain::{CellInfo, InputCellInfo}, @@ -37,12 +39,23 @@ pub async fn generate( local_cells_manager: &LocalCellsManager, ) -> Result { let owner_lock_hash = lock_script.hash(); + let stake_block_timepoint = { + let block_number: u64 = block.raw().number().unpack(); + if rollup_context + .fork_config + .use_timestamp_as_timepoint(block_number) + { + let block_timestamp: u64 = block.raw().timestamp().unpack(); + Timepoint::from_timestamp(block_timestamp) + } else { + Timepoint::from_block_number(block_number) + } + }; let lock_args: Bytes = { let stake_lock_args = StakeLockArgs::new_builder() .owner_lock_hash(owner_lock_hash.pack()) - .stake_block_number(block.raw().number()) + .stake_block_timepoint(stake_block_timepoint.full_value().pack()) .build(); - let rollup_type_hash = rollup_context.rollup_script_hash.as_slice().iter(); rollup_type_hash .chain(stake_lock_args.as_slice().iter()) @@ -128,14 +141,14 @@ pub async fn generate( /// query stake /// -/// return cell which stake_block_number is less than last_finalized_block_number if the args isn't none -/// otherwise return stake cell randomly +/// Returns a finalized stake_state_cell if `compatible_finalize_timepoint_opt` is some, +/// otherwise returns a random stake_state_cell. pub async fn query_stake( client: &CKBIndexerClient, rollup_context: &RollupContext, owner_lock_hash: [u8; 32], required_staking_capacity: u64, - last_finalized_block_number: Option, + compatible_finalize_timepoint_opt: Option, local_cells_manager: &LocalCellsManager, ) -> Result> { let lock = Script::new_builder() @@ -170,10 +183,11 @@ pub async fn query_stake( Ok(r) => r, Err(_) => return false, }; - match last_finalized_block_number { - Some(last_finalized_block_number) => { - stake_lock_args.stake_block_number().unpack() <= last_finalized_block_number - && stake_lock_args.owner_lock_hash().as_slice() == owner_lock_hash + match &compatible_finalize_timepoint_opt { + Some(compatible_finalized_timepoint) => { + compatible_finalized_timepoint.is_finalized(&Timepoint::from_full_value( + stake_lock_args.stake_block_timepoint().unpack(), + )) && stake_lock_args.owner_lock_hash().as_slice() == owner_lock_hash } None => stake_lock_args.owner_lock_hash().as_slice() == owner_lock_hash, } diff --git a/crates/block-producer/src/withdrawal.rs b/crates/block-producer/src/withdrawal.rs index 6e53075fc..499c9b308 100644 --- a/crates/block-producer/src/withdrawal.rs +++ b/crates/block-producer/src/withdrawal.rs @@ -4,6 +4,8 @@ use anyhow::{anyhow, Result}; use gw_common::H256; use gw_config::ContractsCellDep; use gw_mem_pool::{custodian::sum_withdrawals, withdrawal::Generator}; +use gw_types::offchain::CompatibleFinalizedTimepoint; +use gw_types::packed::RollupConfig; use gw_types::{ bytes::Bytes, core::{DepType, ScriptHashType}, @@ -16,8 +18,6 @@ use gw_types::{ prelude::*, }; use gw_utils::RollupContext; - -use gw_types::packed::RollupConfig; use std::{ collections::HashMap, time::{SystemTime, UNIX_EPOCH}, @@ -224,13 +224,16 @@ pub fn unlock_to_owner( }; let global_state = global_state_from_slice(&rollup_cell.data)?; - let last_finalized_block_number: u64 = global_state.last_finalized_block_number().unpack(); + let compatible_finalized_timepoint = CompatibleFinalizedTimepoint::from_global_state( + &global_state, + rollup_config.finality_blocks().unpack(), + ); let l1_sudt_script_hash = rollup_config.l1_sudt_script_type_hash(); for withdrawal_cell in withdrawal_cells { // Double check if let Err(err) = gw_rpc_client::withdrawal::verify_unlockable_to_owner( &withdrawal_cell, - last_finalized_block_number, + &compatible_finalized_timepoint, &l1_sudt_script_hash, ) { log::error!("[unlock withdrawal] unexpected verify failed {}", err); @@ -275,10 +278,16 @@ pub fn unlock_to_owner( .out_point(rollup_cell.out_point) .dep_type(DepType::Code.into()) .build(); + let rollup_config_dep = contracts_dep.rollup_config.clone(); let withdrawal_lock_dep = contracts_dep.withdrawal_cell_lock.clone(); let sudt_type_dep = contracts_dep.l1_sudt_type.clone(); - let mut cell_deps = vec![rollup_dep, withdrawal_lock_dep.into()]; + let mut cell_deps = vec![ + // rollup_dep and rollup_config_dep will be used by withdrawal_lock_script + rollup_dep, + rollup_config_dep.into(), + withdrawal_lock_dep.into(), + ]; if unlocked_to_owner_outputs .iter() .any(|output| output.0.type_().to_opt().is_some()) @@ -299,22 +308,22 @@ mod test { use std::collections::HashMap; use std::iter::FromIterator; + use crate::withdrawal::generate; use gw_common::{h256_ext::H256Ext, H256}; use gw_config::ContractsCellDep; - use gw_types::core::{DepType, ScriptHashType}; - use gw_types::offchain::{CellInfo, CollectedCustodianCells, InputCellInfo}; - use gw_types::packed::RollupConfig; + use gw_types::core::{DepType, ScriptHashType, Timepoint}; + use gw_types::offchain::{ + CellInfo, CollectedCustodianCells, CompatibleFinalizedTimepoint, InputCellInfo, + }; use gw_types::packed::{ CellDep, CellInput, CellOutput, GlobalState, L2Block, OutPoint, RawL2Block, - RawWithdrawalRequest, Script, UnlockWithdrawalViaFinalize, UnlockWithdrawalWitness, - UnlockWithdrawalWitnessUnion, WithdrawalLockArgs, WithdrawalRequest, - WithdrawalRequestExtra, WitnessArgs, + RawWithdrawalRequest, RollupConfig, Script, UnlockWithdrawalViaFinalize, + UnlockWithdrawalWitness, UnlockWithdrawalWitnessUnion, WithdrawalLockArgs, + WithdrawalRequest, WithdrawalRequestExtra, WitnessArgs, }; use gw_types::prelude::{Builder, Entity, Pack, PackVec, Unpack}; use gw_utils::RollupContext; - use crate::withdrawal::generate; - use super::unlock_to_owner; #[test] @@ -388,16 +397,17 @@ mod test { .unwrap(); let (output, data) = generated.unwrap().outputs.first().unwrap().to_owned(); + let block_timepoint = Timepoint::from_block_number(block.raw().number().unpack()); let (expected_output, expected_data) = gw_generator::utils::build_withdrawal_cell_output( &rollup_context, &withdrawal_extra, &block.hash().into(), - block.raw().number().unpack(), + &block_timepoint, Some(sudt_script.clone()), ) .unwrap(); - assert_eq!(expected_output.as_slice(), output.as_slice()); + assert_eq!(expected_output.to_string(), output.to_string()); assert_eq!(expected_data, data); // Check our generate withdrawal can be queried and unlocked to owner @@ -406,11 +416,14 @@ mod test { data, ..Default::default() }; - let last_finalized_block_number = - block.raw().number().unpack() + rollup_context.rollup_config.finality_blocks().unpack(); + // Make sure the withdrawal is finalized for `global_state` + let compatible_finalized_timepoint = CompatibleFinalizedTimepoint::from_block_number( + block.raw().number().unpack() + rollup_context.rollup_config.finality_blocks().unpack(), + rollup_context.rollup_config.finality_blocks().unpack(), + ); gw_rpc_client::withdrawal::verify_unlockable_to_owner( &info, - last_finalized_block_number, + &compatible_finalized_timepoint, &sudt_script.code_hash(), ) .expect("pass verification"); @@ -419,9 +432,9 @@ mod test { #[test] fn test_unlock_to_owner() { // Output should only change lock to owner lock - let last_finalized_block_number = 100u64; + let last_finalized_timepoint = Timepoint::from_block_number(100); let global_state = GlobalState::new_builder() - .last_finalized_block_number(last_finalized_block_number.pack()) + .last_finalized_block_number(last_finalized_timepoint.full_value().pack()) .build(); let rollup_type = Script::new_builder() @@ -484,7 +497,7 @@ mod test { let withdrawal_without_owner_lock = { let lock_args = WithdrawalLockArgs::new_builder() .owner_lock_hash(owner_lock.hash().pack()) - .withdrawal_block_number((last_finalized_block_number - 1).pack()) + .withdrawal_block_timepoint(last_finalized_timepoint.full_value().pack()) .build(); let mut args = rollup_type.hash().to_vec(); @@ -500,7 +513,7 @@ mod test { let withdrawal_with_owner_lock = { let lock_args = WithdrawalLockArgs::new_builder() .owner_lock_hash(owner_lock.hash().pack()) - .withdrawal_block_number((last_finalized_block_number - 1).pack()) + .withdrawal_block_timepoint(last_finalized_timepoint.full_value().pack()) .build(); let mut args = rollup_type.hash().to_vec(); @@ -580,7 +593,7 @@ mod test { let witness = unlocked.witness_args.first().unwrap().to_owned(); assert_eq!(expected_witness.as_slice(), witness.as_slice()); - assert_eq!(unlocked.deps.len(), 3); + assert_eq!(unlocked.deps.len(), 4); let rollup_dep = CellDep::new_builder() .out_point(rollup_cell.out_point) .dep_type(DepType::Code.into()) @@ -591,10 +604,14 @@ mod test { ); assert_eq!( unlocked.deps.get(1).unwrap().as_slice(), - CellDep::from(contracts_dep.withdrawal_cell_lock).as_slice(), + CellDep::from(contracts_dep.rollup_config).as_slice(), ); assert_eq!( unlocked.deps.get(2).unwrap().as_slice(), + CellDep::from(contracts_dep.withdrawal_cell_lock).as_slice(), + ); + assert_eq!( + unlocked.deps.get(3).unwrap().as_slice(), CellDep::from(contracts_dep.l1_sudt_type).as_slice(), ); } diff --git a/crates/block-producer/src/withdrawal_unlocker.rs b/crates/block-producer/src/withdrawal_unlocker.rs index b5a203ad0..839cb87d4 100644 --- a/crates/block-producer/src/withdrawal_unlocker.rs +++ b/crates/block-producer/src/withdrawal_unlocker.rs @@ -9,9 +9,11 @@ use gw_common::H256; use gw_config::{ContractsCellDep, DebugConfig}; use gw_rpc_client::contract::ContractsCellDepManager; use gw_rpc_client::rpc_client::RPCClient; -use gw_types::offchain::{global_state_from_slice, CellInfo, TxStatus}; +use gw_types::offchain::{ + global_state_from_slice, CellInfo, CompatibleFinalizedTimepoint, TxStatus, +}; use gw_types::packed::{OutPoint, RollupConfig, Transaction}; -use gw_types::prelude::{Pack, Unpack}; +use gw_types::prelude::*; use gw_utils::fee::fill_tx_fee; use gw_utils::genesis_info::CKBGenesisInfo; use gw_utils::transaction_skeleton::TransactionSkeleton; @@ -159,7 +161,7 @@ pub trait BuildUnlockWithdrawalToOwner { async fn query_unlockable_withdrawals( &self, - last_finalized_block_number: u64, + compatible_finalized_timepoint: &CompatibleFinalizedTimepoint, unlocked: &HashSet, ) -> Result>; @@ -178,9 +180,12 @@ pub trait BuildUnlockWithdrawalToOwner { }; let global_state = global_state_from_slice(&rollup_cell.data)?; - let last_finalized_block_number: u64 = global_state.last_finalized_block_number().unpack(); + let compatible_finalized_timepoint = CompatibleFinalizedTimepoint::from_global_state( + &global_state, + self.rollup_config().finality_blocks().unpack(), + ); let unlockable_withdrawals = self - .query_unlockable_withdrawals(last_finalized_block_number, unlocked) + .query_unlockable_withdrawals(&compatible_finalized_timepoint, unlocked) .await?; log::info!( "[unlock withdrawal] find unlockable finalized withdrawals {}", @@ -256,12 +261,12 @@ impl BuildUnlockWithdrawalToOwner for DefaultUnlocker { async fn query_unlockable_withdrawals( &self, - last_finalized_block_number: u64, + compatible_finalized_timepoint: &CompatibleFinalizedTimepoint, unlocked: &HashSet, ) -> Result> { self.rpc_client .query_finalized_owner_lock_withdrawal_cells( - last_finalized_block_number, + compatible_finalized_timepoint, unlocked, Self::MAX_WITHDRAWALS_PER_TX, ) diff --git a/crates/chain/src/chain.rs b/crates/chain/src/chain.rs index e71aff11e..e876c3d2d 100644 --- a/crates/chain/src/chain.rs +++ b/crates/chain/src/chain.rs @@ -30,6 +30,7 @@ use gw_types::{ }, prelude::{Builder as GWBuilder, Entity as GWEntity, Pack as GWPack, Unpack as GWUnpack}, }; +use gw_utils::calc_finalizing_range; use std::{collections::HashSet, convert::TryFrom, sync::Arc, time::Instant}; use tokio::sync::Mutex; use tracing::instrument; @@ -142,7 +143,7 @@ impl LocalState { pub struct Chain { rollup_type_script_hash: [u8; 32], - rollup_config_hash: [u8; 32], + rollup_config: RollupConfig, store: Store, challenge_target: Option, last_sync_event: SyncEvent, @@ -154,7 +155,7 @@ pub struct Chain { impl Chain { pub fn create( - rollup_config: &RollupConfig, + rollup_config: RollupConfig, rollup_type_script: &Script, config: &ChainConfig, store: Store, @@ -163,7 +164,7 @@ impl Chain { ) -> Result { // convert serde types to gw-types assert_eq!( - rollup_config, + &rollup_config, &generator.rollup_context().rollup_config, "check generator rollup config" ); @@ -181,7 +182,6 @@ impl Chain { tip, last_global_state, }; - let rollup_config_hash = rollup_config.hash(); let skipped_invalid_block_list = config .skipped_invalid_block_list .iter() @@ -199,7 +199,7 @@ impl Chain { generator, mem_pool, rollup_type_script_hash, - rollup_config_hash, + rollup_config, skipped_invalid_block_list, }) } @@ -221,8 +221,8 @@ impl Chain { &self.generator } - pub fn rollup_config_hash(&self) -> &[u8; 32] { - &self.rollup_config_hash + pub fn rollup_config_hash(&self) -> [u8; 32] { + self.rollup_config.hash() } pub fn rollup_type_script_hash(&self) -> &[u8; 32] { @@ -574,45 +574,64 @@ impl Chain { Ok(()) } + /// Calculate and store the finalized_custodian_capacity for block block_number. + /// + /// Initialize by the block parent's finalized_custodian_capacity; + /// Update with finalizing deposit requests and block's withdrawals. pub fn calculate_and_store_finalized_custodians( - &mut self, + &self, db: &StoreTransaction, block_number: u64, ) -> Result<(), anyhow::Error> { + if block_number == 0 { + return Ok(()); + } + let block_hash = db .get_block_hash_by_number(block_number)? .context("get block hash")?; - let withdrawals = db - .get_block(&block_hash)? - .context("get block")? - .withdrawals(); - - let mut finalized_custodians = db + 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")? .as_reader() .unpack(); - let last_finalized_block = self - .generator - .rollup_context() - .last_finalized_block_number(block_number - 1); - let deposits = db - .get_block_deposit_info_vec(last_finalized_block) - .context("get last finalized block deposit")?; - for deposit in deposits { - let deposit = deposit.request(); - finalized_custodians.capacity = finalized_custodians - .capacity - .checked_add(deposit.capacity().unpack().into()) - .context("add capacity overflow")?; - finalized_custodians - .checked_add_sudt( - deposit.sudt_script_hash().unpack(), - deposit.amount().unpack(), - deposit.script(), - ) - .context("add sudt overflow")?; + let mut finalized_custodians = parent_finalized_custodians; + + // 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 = 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")?; + for deposit in deposits { + let deposit = deposit.request(); + finalized_custodians.capacity = finalized_custodians + .capacity + .checked_add(deposit.capacity().unpack().into()) + .context("add capacity overflow")?; + finalized_custodians + .checked_add_sudt( + deposit.sudt_script_hash().unpack(), + deposit.amount().unpack(), + deposit.script(), + ) + .context("add sudt overflow")?; + } } + + // Update finalized_custodians with the withdrawals of the current block + let withdrawals = db + .get_block(&block_hash)? + .context("get block")? + .withdrawals(); for w in withdrawals.as_reader().iter() { finalized_custodians.capacity = finalized_custodians .capacity @@ -627,10 +646,12 @@ impl Chain { .context("withdrawal not enough sudt amount")?; } } + db.set_block_post_finalized_custodian_capacity( block_number, &finalized_custodians.pack().as_reader(), )?; + Ok(()) } @@ -1050,6 +1071,7 @@ impl Chain { )?; db.insert_asset_scripts(deposit_asset_scripts)?; db.attach_block(l2block.clone())?; + self.local_state.tip = l2block; self.local_state.last_global_state = global_state; Ok(None) diff --git a/crates/challenge/src/offchain/mock_block.rs b/crates/challenge/src/offchain/mock_block.rs index 3a84aa938..aee4bad7a 100644 --- a/crates/challenge/src/offchain/mock_block.rs +++ b/crates/challenge/src/offchain/mock_block.rs @@ -17,7 +17,7 @@ use gw_store::state::traits::JournalDB; use gw_store::state::MemStateDB; use gw_store::transaction::StoreTransaction; use gw_traits::CodeStore; -use gw_types::core::{ChallengeTargetType, Status}; +use gw_types::core::{ChallengeTargetType, Status, Timepoint}; use gw_types::offchain::RunResult; use gw_types::packed::{ AccountMerkleState, BlockMerkleState, Byte32, CCTransactionSignatureWitness, @@ -320,13 +320,20 @@ impl MockBlockParam { .build() }; - let last_finalized_block_number = self.number.saturating_sub(self.finality_blocks); - + let last_finalized_timepoint = if self + .rollup_context + .fork_config + .use_timestamp_as_timepoint(self.number) + { + unimplemented!() + } else { + Timepoint::from_block_number(self.number.saturating_sub(self.finality_blocks)) + }; let global_state = GlobalState::new_builder() .account(post_account) .block(post_block) .tip_block_hash(raw_block.hash().pack()) - .last_finalized_block_number(last_finalized_block_number.pack()) + .last_finalized_block_number(last_finalized_timepoint.full_value().pack()) .reverted_block_root(self.reverted_block_root.clone()) .rollup_config_hash(self.rollup_config_hash.clone()) .status((Status::Halting as u8).into()) diff --git a/crates/challenge/src/revert.rs b/crates/challenge/src/revert.rs index 1d2bd75b2..c12dba4c3 100644 --- a/crates/challenge/src/revert.rs +++ b/crates/challenge/src/revert.rs @@ -5,7 +5,7 @@ use ckb_types::prelude::Reader; use ckb_types::prelude::{Builder, Entity}; use gw_common::smt::Blake2bHasher; use gw_common::H256; -use gw_types::core::Status; +use gw_types::core::{Status, Timepoint}; use gw_types::offchain::CellInfo; use gw_types::packed::BlockMerkleState; use gw_types::packed::ChallengeLockArgsReader; @@ -15,11 +15,14 @@ use gw_types::packed::{ CellOutput, ChallengeLockArgs, GlobalState, RollupAction, RollupActionUnion, Script, WitnessArgs, }; -use gw_types::prelude::Unpack; -use gw_types::{bytes::Bytes, prelude::Pack}; +use gw_types::{ + bytes::Bytes, + prelude::{Pack, Unpack}, +}; use gw_utils::RollupContext; pub struct Revert<'a> { + rollup_context: RollupContext, finality_blocks: u64, reward_burn_rate: u8, prev_global_state: GlobalState, @@ -39,7 +42,7 @@ pub struct RevertOutput { impl<'a> Revert<'a> { pub fn new( - rollup_context: &RollupContext, + rollup_context: RollupContext, prev_global_state: GlobalState, challenge_cell: &'a CellInfo, stake_cells: &'a [CellInfo], @@ -50,6 +53,7 @@ impl<'a> Revert<'a> { let finality_blocks = rollup_context.rollup_config.finality_blocks().unpack(); Revert { + rollup_context, finality_blocks, prev_global_state, challenge_cell, @@ -95,11 +99,25 @@ impl<'a> Revert<'a> { .count(block_count) .build() }; - let last_finalized_block_number = { - let number = first_reverted_block.number().unpack(); - number - .saturating_sub(1) - .saturating_sub(self.finality_blocks) + let last_finalized_timepoint = if self + .rollup_context + .fork_config + .use_timestamp_as_timepoint(first_reverted_block.number().unpack()) + { + Timepoint::Timestamp( + first_reverted_block + .timestamp() + .unpack() + .saturating_sub(self.rollup_context.rollup_config.finality_time_in_ms()), + ) + } else { + Timepoint::from_block_number( + first_reverted_block + .number() + .unpack() + .saturating_sub(1) + .saturating_sub(self.finality_blocks), + ) }; let running_status: u8 = Status::Running.into(); @@ -110,7 +128,7 @@ impl<'a> Revert<'a> { .block(block_merkle_state) .tip_block_hash(first_reverted_block.parent_block_hash()) .tip_block_timestamp(self.revert_witness.new_tip_block.timestamp()) - .last_finalized_block_number(last_finalized_block_number.pack()) + .last_finalized_block_number(last_finalized_timepoint.full_value().pack()) .reverted_block_root(self.post_reverted_block_root.pack()) .status(running_status.into()) .build(); diff --git a/crates/config/src/config.rs b/crates/config/src/config.rs index 6f9b782bc..084894fca 100644 --- a/crates/config/src/config.rs +++ b/crates/config/src/config.rs @@ -151,6 +151,7 @@ pub struct ContractTypeScriptConfig { #[derive(Clone, Debug, Default)] pub struct ContractsCellDep { + pub rollup_config: CellDep, pub rollup_cell_type: CellDep, pub deposit_cell_lock: CellDep, pub stake_cell_lock: CellDep, diff --git a/crates/config/src/fork_config.rs b/crates/config/src/fork_config.rs index 59cd055c6..4c0742339 100644 --- a/crates/config/src/fork_config.rs +++ b/crates/config/src/fork_config.rs @@ -66,6 +66,11 @@ impl ForkConfig { } } + /// Returns if use timestamp as timepoint + pub fn use_timestamp_as_timepoint(&self, block_number: u64) -> bool { + self.global_state_version(block_number) >= 2 + } + /// Return l2 tx cycles limit by block height pub fn max_l2_tx_cycles(&self, block_number: u64) -> u64 { match self.increase_max_l2_tx_cycles_to_500m { diff --git a/crates/generator/src/utils.rs b/crates/generator/src/utils.rs index 1cd6cb8f6..2d0ec888b 100644 --- a/crates/generator/src/utils.rs +++ b/crates/generator/src/utils.rs @@ -4,6 +4,7 @@ use anyhow::{Context, Result}; use gw_common::{builtins::CKB_SUDT_ACCOUNT_ID, state::State, H256}; use gw_config::BackendType; use gw_traits::CodeStore; +use gw_types::core::Timepoint; use gw_types::{ bytes::Bytes, core::{AllowedContractType, ScriptHashType}, @@ -48,7 +49,7 @@ pub fn build_withdrawal_cell_output( rollup_context: &RollupContext, req: &WithdrawalRequestExtra, block_hash: &H256, - block_number: u64, + block_timepoint: &Timepoint, opt_asset_script: Option