Skip to content

Commit

Permalink
Merge pull request #468 from zeroqn/fix-chain-revert-bad-block
Browse files Browse the repository at this point in the history
Fix chain revert bad block
  • Loading branch information
jjyr authored Dec 8, 2021
2 parents cb4975e + 9885eef commit cbf4bd7
Show file tree
Hide file tree
Showing 6 changed files with 503 additions and 50 deletions.
1 change: 0 additions & 1 deletion crates/benches/benches/benchmarks/smt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,6 @@ impl BenchExecutionEnvironment {
)
.unwrap();

let rollup_config: gw_types::packed::RollupConfig = config.rollup_config.to_owned().into();
db.attach_block(genesis).unwrap();
db.commit().unwrap();
}
Expand Down
1 change: 1 addition & 0 deletions crates/block-producer/src/produce_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use gw_types::{
prelude::*,
};

#[derive(Clone)]
pub struct ProduceBlockResult {
pub block: L2Block,
pub global_state: GlobalState,
Expand Down
38 changes: 33 additions & 5 deletions crates/block-producer/src/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ pub fn run(config: Config, skip_config_check: bool) -> Result<()> {
);

let base = BaseInitComponents::init(&config, skip_config_check)?;
let (mem_pool, wallet, poa, _offchain_mock_context, pg_pool) = match config
let (mem_pool, wallet, poa, offchain_mock_context, pg_pool) = match config
.block_producer
.clone()
{
Expand All @@ -409,7 +409,6 @@ pub fn run(config: Config, skip_config_check: bool) -> Result<()> {
base.init_offchain_mock_context(&poa, &block_producer_config)
.await
})?;

let mem_pool_provider = DefaultMemPoolProvider::new(
base.rpc_client.clone(),
Arc::clone(&poa),
Expand Down Expand Up @@ -461,7 +460,7 @@ pub fn run(config: Config, skip_config_check: bool) -> Result<()> {
rollup_config_hash,
rollup_context,
rollup_type_script,
builtin_load_data: _,
builtin_load_data,
ckb_genesis_info,
rpc_client,
store,
Expand Down Expand Up @@ -518,7 +517,7 @@ pub fn run(config: Config, skip_config_check: bool) -> Result<()> {
let chain_updater = ChainUpdater::new(
Arc::clone(&chain),
rpc_client.clone(),
rollup_context,
rollup_context.clone(),
rollup_type_script.clone(),
web3_indexer,
);
Expand All @@ -535,6 +534,11 @@ pub fn run(config: Config, skip_config_check: bool) -> Result<()> {
.ok_or_else(|| anyhow!("mem-pool must be enabled in mode: {:?}", mode))?;
let wallet =
wallet.ok_or_else(|| anyhow!("wallet must be enabled in mode: {:?}", mode))?;
let offchain_mock_context = {
let ctx = offchain_mock_context;
let msg = "offchain mock require block producer config, wallet and poa in mode: ";
ctx.ok_or_else(|| anyhow!("{} {:?}", msg, mode))?
};
let poa = poa.ok_or_else(|| anyhow!("poa must be enabled in mode: {:?}", mode))?;
let tests_control = if let NodeMode::Test = config.node_mode {
Some(TestModeControl::new(
Expand All @@ -552,6 +556,25 @@ pub fn run(config: Config, skip_config_check: bool) -> Result<()> {
wallet,
));

let wallet = Wallet::from_config(&block_producer_config.wallet_config)
.with_context(|| "init wallet")?;

// Challenger
let challenger = Challenger::new(
rollup_context,
rpc_client.clone(),
wallet,
block_producer_config.clone(),
config.debug.clone(),
builtin_load_data,
ckb_genesis_info.clone(),
Arc::clone(&chain),
Arc::clone(&poa),
tests_control.clone(),
Arc::clone(&cleaner),
offchain_mock_context,
);

// Block Producer
let block_producer = BlockProducer::create(
rollup_config_hash,
Expand All @@ -567,7 +590,12 @@ pub fn run(config: Config, skip_config_check: bool) -> Result<()> {
)
.with_context(|| "init block producer")?;

(Some(block_producer), None, tests_control, Some(cleaner))
(
Some(block_producer),
Some(challenger),
tests_control,
Some(cleaner),
)
}
};

Expand Down
88 changes: 49 additions & 39 deletions crates/chain/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use gw_common::{sparse_merkle_tree, state::State, H256};
use gw_config::ChainConfig;
use gw_generator::{
generator::{ApplyBlockArgs, ApplyBlockResult},
traits::StateExt,
ChallengeContext, Generator,
};
use gw_jsonrpc_types::debugger::ReprMockTransaction;
Expand Down Expand Up @@ -361,16 +362,37 @@ impl Chain {
return Ok(SyncEvent::BadBlock { context });
}

if let Some(_challenge_target) = self.process_block(
if let Some(challenge_target) = self.process_block(
db,
l2block.clone(),
l2block_committed_info.clone(),
global_state.clone(),
deposit_requests,
deposit_asset_scripts,
)? {
db.rollback()?;

let block_number = l2block.raw().number().unpack();
Err(anyhow!("Bad block found! #{}", block_number))
log::warn!("bad block #{} found, rollback db", block_number,);

db.insert_bad_block(&l2block, &l2block_committed_info, &global_state)?;
log::info!("insert bad block 0x{}", hex::encode(l2block.hash()));

let global_block_root: H256 = global_state.block().merkle_root().unpack();
let local_block_root = db.get_block_smt_root()?;
assert_eq!(local_block_root, global_block_root, "block root fork");

assert!(self.challenge_target.is_none());
db.set_bad_block_challenge_target(
&l2block.hash().into(),
&challenge_target,
)?;
self.challenge_target = Some(challenge_target.clone());
self.local_state.tip = l2block;

let context =
gw_challenge::context::build_challenge_context(db, challenge_target)?;
Ok(SyncEvent::BadBlock { context })
} else {
let block_number = l2block.raw().number().unpack();
log::info!("sync new block #{} success", block_number);
Expand Down Expand Up @@ -446,17 +468,11 @@ impl Chain {
match self.challenge_target {
// Previous challenge miss right target, we should challenge it
Some(ref target) => {
// let context = gw_challenge::context::build_challenge_context(
// db,
// target.to_owned(),
// )?;
panic!(
"found bad block after challenge cancelled: {}, index: {}, type: {:?}",
target.block_hash(),
target.target_index(),
target.target_type()
);
// Ok(SyncEvent::BadBlock { context })
let context = gw_challenge::context::build_challenge_context(
db,
target.to_owned(),
)?;
Ok(SyncEvent::BadBlock { context })
}
None => Ok(SyncEvent::Success),
}
Expand Down Expand Up @@ -553,17 +569,11 @@ impl Chain {
// If our bad block isn't reverted, just challenge it
match self.challenge_target {
Some(ref target) => {
// let context = gw_challenge::context::build_challenge_context(
// db,
// target.to_owned(),
// )?;
panic!(
"found bad block: {}, index: {}, type: {:?}",
target.block_hash(),
target.target_index(),
target.target_type()
);
// Ok(SyncEvent::BadBlock { context })
let context = gw_challenge::context::build_challenge_context(
db,
target.to_owned(),
)?;
Ok(SyncEvent::BadBlock { context })
}
None => Ok(SyncEvent::Success),
}
Expand Down Expand Up @@ -708,9 +718,14 @@ impl Chain {
.get_block_post_global_state(&last_valid_tip_block_hash)?
.expect("last valid tip global state should exists");

let local_reverted_block_root: H256 = db.get_reverted_block_smt_root()?;
let last_valid_tip_reverted_block_root: H256 =
last_valid_tip_global_state.reverted_block_root().unpack();

if local_state_tip_hash == last_valid_tip_block_hash
&& local_state_global_state.as_slice()
== last_valid_tip_global_state.as_slice()
&& local_reverted_block_root == last_valid_tip_reverted_block_root
{
// No need to rewind
return Ok(());
Expand All @@ -722,10 +737,7 @@ impl Chain {
// after sync complete.

// Rewind reverted block smt to last valid tip in db
let mut current_reverted_block_root: H256 =
local_state_global_state.reverted_block_root().unpack();
let last_valid_tip_reverted_block_root: H256 =
last_valid_tip_global_state.reverted_block_root().unpack();
let mut current_reverted_block_root = local_reverted_block_root;
let genesis_hash = db.get_block_hash_by_number(0)?.expect("genesis hash");
let genesis_reverted_block_root: H256 = {
let genesis_global_state = db
Expand Down Expand Up @@ -822,22 +834,20 @@ impl Chain {
}

// check consistency of account SMT
let expected_account_root: H256 = {
let raw_block = self.local_state.tip.raw();
raw_block.post_account().merkle_root().unpack()
};
let expected_account = self.local_state.tip.raw().post_account();

assert_eq!(
db.account_smt().unwrap().root(),
&expected_account_root,
&expected_account.merkle_root().unpack(),
"account root consistent in DB"
);

let tree = db.state_tree(StateContext::ReadOnly)?;
let current_account_root = tree.calculate_root().unwrap();
let current_account = tree.merkle_state()?;

assert_eq!(
current_account_root, expected_account_root,
current_account.as_slice(),
expected_account.as_slice(),
"check account tree"
);

Expand Down Expand Up @@ -879,11 +889,11 @@ impl Chain {
{
let tree = db.state_tree(StateContext::ReadOnly)?;

let prev_merkle_root: H256 = l2block.raw().prev_account().merkle_root().unpack();
let prev_merkle_state = l2block.raw().prev_account();
assert_eq!(
tree.calculate_root()?,
prev_merkle_root,
"prev account merkle root must be consistent"
tree.merkle_state()?.as_slice(),
prev_merkle_state.as_slice(),
"prev account merkle state must be consistent"
);
}

Expand Down
20 changes: 19 additions & 1 deletion crates/tests/src/testing_tool/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,23 @@ pub fn setup_chain(rollup_type_script: Script) -> Chain {
rollup_type_script,
rollup_config,
account_lock_manage,
None,
);
chain.complete_initial_syncing().unwrap();
chain
}

// Simulate process restart
pub fn restart_chain(chain: &Chain, rollup_type_script: Script) -> Chain {
let mut account_lock_manage = AccountLockManage::default();
let rollup_config = chain.generator().rollup_context().rollup_config.to_owned();
account_lock_manage
.register_lock_algorithm((*ALWAYS_SUCCESS_CODE_HASH).into(), Box::new(AlwaysSuccess));
let mut chain = setup_chain_with_account_lock_manage(
rollup_type_script,
rollup_config,
account_lock_manage,
Some(chain.store().to_owned()),
);
chain.complete_initial_syncing().unwrap();
chain
Expand All @@ -105,8 +122,9 @@ pub fn setup_chain_with_account_lock_manage(
rollup_type_script: Script,
rollup_config: RollupConfig,
account_lock_manage: AccountLockManage,
opt_store: Option<Store>,
) -> Chain {
let store = Store::open_tmp().unwrap();
let store = opt_store.unwrap_or_else(|| Store::open_tmp().unwrap());
let rollup_script_hash = rollup_type_script.hash();
let genesis_config = GenesisConfig {
timestamp: 0,
Expand Down
Loading

0 comments on commit cbf4bd7

Please sign in to comment.