Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix chain revert bad block #468

Merged
merged 7 commits into from
Dec 8, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -509,7 +508,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 @@ -526,6 +525,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 @@ -543,6 +547,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 @@ -558,7 +581,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
89 changes: 50 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,38 @@ impl Chain {
return Ok(SyncEvent::BadBlock { context });
}

if let Some(_challenge_target) = self.process_block(
println!("deposit_requests {}", deposit_requests.len());
zeroqn marked this conversation as resolved.
Show resolved Hide resolved
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 +469,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 +570,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 +719,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 +738,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 +835,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 +890,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