Skip to content

Commit

Permalink
Merge pull request #163 from yangby-cryptape/bugfix/activation-epochs
Browse files Browse the repository at this point in the history
fix: no chain_root in 1st block of the mmr activated epoch
  • Loading branch information
quake authored Dec 4, 2023
2 parents d25eabb + 4f047ff commit 0978253
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1077,7 +1077,7 @@ pub(crate) fn verify_mmr_proof<'a, T: Iterator<Item = &'a HeaderView>>(
raw_proof: packed::HeaderDigestVecReader,
headers: T,
) -> Result<(), Status> {
if last_header.is_valid(mmr_activated_epoch) {
if last_header.patched_is_valid(mmr_activated_epoch) {
trace!(
"passed: verify extra hash for block-{} ({:#x})",
last_header.header().number(),
Expand Down
8 changes: 4 additions & 4 deletions src/protocols/light_client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ impl LightClientProtocol {
) -> Result<(), Status> {
let mmr_activated_epoch = self.mmr_activated_epoch();
for header in headers {
if !header.is_valid(mmr_activated_epoch) {
if !header.patched_is_valid(mmr_activated_epoch) {
let header = header.header();
let errmsg = format!(
"failed to verify chain root for block#{}, hash: {:#x}",
Expand All @@ -285,7 +285,7 @@ impl LightClientProtocol {
return Err(StatusCode::InvalidNonce.with_context(errmsg));
}
// Check Chain Root
if !verifiable_header.is_valid(self.mmr_activated_epoch()) {
if !verifiable_header.patched_is_valid(self.mmr_activated_epoch()) {
let errmsg = format!(
"failed to verify chain root for block#{}, hash: {:#x}",
header.number(),
Expand Down Expand Up @@ -414,8 +414,8 @@ impl LightClientProtocol {
pub(crate) fn new(storage: Storage, peers: Arc<Peers>, consensus: Consensus) -> Self {
// Ref: https://github.com/nervosnetwork/rfcs/blob/01f3bc64ef8f54c94c7b0dcf9d30c84b6c8418b0/rfcs/0044-ckb-light-client/0044-ckb-light-client.md#deployment
let mmr_activated_epoch = match consensus.id.as_str() {
mainnet::CHAIN_SPEC_NAME => 8648,
testnet::CHAIN_SPEC_NAME => 5676,
mainnet::CHAIN_SPEC_NAME => 8651,
testnet::CHAIN_SPEC_NAME => 5711,
_ => 0,
};
Self {
Expand Down
48 changes: 47 additions & 1 deletion src/protocols/light_client/prelude.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
use ckb_network::{CKBProtocolContext, PeerIndex};
use ckb_types::{core::HeaderView, packed::LightClientMessage, prelude::*};
use ckb_types::{
core::{EpochNumber, EpochNumberWithFraction, ExtraHashView, HeaderView},
packed::LightClientMessage,
prelude::*,
utilities::merkle_mountain_range::VerifiableHeader,
};

use super::{Status, StatusCode};

Expand Down Expand Up @@ -38,3 +43,44 @@ impl HeaderUtils for HeaderView {
&& self.hash() == child.parent_hash()
}
}

// TODO Remove patch after the upstream fixed.
//
// Ref: https://github.com/nervosnetwork/ckb/blob/v0.112.1/util/types/src/utilities/merkle_mountain_range.rs#L212-L241
pub(crate) trait VerifiableHeaderPatch {
fn patched_is_valid(&self, mmr_activated_epoch_number: EpochNumber) -> bool;
}

impl VerifiableHeaderPatch for VerifiableHeader {
fn patched_is_valid(&self, mmr_activated_epoch_number: EpochNumber) -> bool {
let mmr_activated_epoch = EpochNumberWithFraction::new(mmr_activated_epoch_number, 0, 1);
let has_chain_root = self.header().epoch() > mmr_activated_epoch;
if has_chain_root {
if self.header().is_genesis() {
if !self.parent_chain_root().is_default() {
return false;
}
} else {
let is_extension_beginning_with_chain_root_hash = self
.extension()
.map(|extension| {
let actual_extension_data = extension.raw_data();
let parent_chain_root_hash = self.parent_chain_root().calc_mmr_hash();
actual_extension_data.starts_with(parent_chain_root_hash.as_slice())
})
.unwrap_or(false);
if !is_extension_beginning_with_chain_root_hash {
return false;
}
}
}

let expected_extension_hash = self
.extension()
.map(|extension| extension.calc_raw_data_hash());
let extra_hash_view = ExtraHashView::new(self.uncles_hash(), expected_extension_hash);
let expected_extra_hash = extra_hash_view.extra_hash();
let actual_extra_hash = self.header().extra_hash();
expected_extra_hash == actual_extra_hash
}
}
2 changes: 1 addition & 1 deletion src/tests/protocols/light_client/send_last_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ async fn invalid_chain_root() {

let data = {
let header = HeaderBuilder::default()
.epoch(EpochNumberWithFraction::new(1, 0, 10).pack())
.epoch(EpochNumberWithFraction::new(1, 1, 10).pack())
.number(11u64.pack())
.build();
let last_header = packed::VerifiableHeader::new_builder()
Expand Down
11 changes: 7 additions & 4 deletions src/tests/protocols/light_client/send_last_state_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -920,6 +920,7 @@ async fn invalid_parent_chain_root_for_the_genesis_block() {
}

async fn test_parent_chain_root_for_the_genesis_block(should_passed: bool) {
setup();
let chain = MockChain::new_with_dummy_pow("test-light-client").start();
let nc = MockNetworkContext::new(SupportProtocols::LightClient);

Expand All @@ -934,8 +935,8 @@ async fn test_parent_chain_root_for_the_genesis_block(should_passed: bool) {
protocol.set_mmr_activated_epoch(0);
protocol.set_last_n_blocks(3);

let num = 1;
chain.mine_to(1);
let num = 10;
chain.mine_to(num + 1);

let snapshot = chain.shared().snapshot();

Expand Down Expand Up @@ -971,9 +972,11 @@ async fn test_parent_chain_root_for_the_genesis_block(should_passed: bool) {
let headers = (0..num)
.into_iter()
.map(|n| {
if !should_passed && n == 0 {
if !should_passed && n == num / 2 {
// Set a wrong parent chain root:
// - Use n's chain root as n's parent chain root
let parent_chain_root = snapshot
.chain_root_mmr(1)
.chain_root_mmr(n)
.get_root()
.expect("has chain root");
snapshot
Expand Down

0 comments on commit 0978253

Please sign in to comment.