Skip to content

Commit

Permalink
Closes #143, eth relay reorg support
Browse files Browse the repository at this point in the history
  • Loading branch information
hackfisher committed Feb 28, 2020
1 parent ae37d2f commit 0d7e6e8
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 74 deletions.
166 changes: 94 additions & 72 deletions frame/chainrelay/eth/relay/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,13 @@ pub trait Trait: system::Trait {

/// Familial details concerning a block
#[derive(Default, Clone, Copy, Eq, PartialEq, Encode, Decode)]
pub struct BlockDetails {
/// Block number
pub height: EthBlockNumber,
pub hash: H256,
pub struct HeaderInfo {
/// Total difficulty of the block and all its parents
pub total_difficulty: U256,
// /// Parent block hash
// pub parent: H256,
// /// List of children block hashes
// pub children: Vec<H256>,
// /// Whether the block is considered finalized
// pub is_finalized: bool,
/// Parent hash of the header
pub parent_hash: H256,
/// Block number
pub number: EthBlockNumber,
}

#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)]
Expand All @@ -54,24 +49,19 @@ pub struct EthReceiptProof {
decl_storage! {
trait Store for Module<T: Trait> as EthRelay {
/// Anchor block that works as genesis block
pub BeginHeader get(fn begin_header): Option<EthHeader>;
pub GenesisHeader get(fn begin_header): Option<EthHeader>;

/// Info of the best block header for now
/// Hash of best block header
pub BestHeaderHash get(fn best_header_hash): H256;

pub HeaderOf get(header_of): map H256 => Option<EthHeader>;

pub HeaderDetailsOf get(header_details_of): map H256 => Option<BlockDetails>;

/// Block delay for verify transaction
pub FinalizeNumber get(finalize_number): Option<u64>;
pub CanonicalHeaderHashOf get(canonical_header_hash_of): map u64 => H256;

// pub BestHashOf get(best_hash_of): map u64 => Option<H256>;
pub HeaderOf get(header_of): map H256 => Option<EthHeader>;

// pub HashsOf get(hashs_of): map u64 => Vec<H256>;
pub HeaderInfoOf get(header_info_of): map H256 => Option<HeaderInfo>;

// pub HeaderForIndex get(header_for_index): map H256 => Vec<(u64, T::Hash)>;
// pub UnverifiedHeader get(unverified_header): map PrevHash => Vec<Header>;
/// Number of blocks finality
pub NumberOfBlocksFinality get(number_of_blocks_finality) config(): u64 = 30;

pub CheckAuthorities get(fn check_authorities) config(): bool = true;
pub Authorities get(fn authorities) config(): Vec<T::AccountId>;
Expand All @@ -85,8 +75,6 @@ decl_storage! {

// Discard the result even it fail.
let _ = <Module<T>>::init_genesis_header(&header,config.genesis_difficulty);

// TODO: initialize other parameters.
}
});
}
Expand All @@ -99,29 +87,36 @@ decl_module! {
{
fn deposit_event() = default;

// TODO: Just for easy testing.
pub fn reset_genesis_header(origin, header: EthHeader, genesis_difficulty: u64) {
let relayer = ensure_signed(origin)?;
if Self::check_authorities() {
ensure!(Self::authorities().contains(&relayer), "Account - NO PRIVILEGES");
}

// TODO: Just for easy testing.
Self::init_genesis_header(&header, genesis_difficulty)?;

<Module<T>>::deposit_event(RawEvent::SetGenesisHeader(relayer, header, genesis_difficulty));
}

pub fn relay_header(origin, header: EthHeader) {

let relayer = ensure_signed(origin)?;
if Self::check_authorities() {
ensure!(Self::authorities().contains(&relayer), "Account - NO PRIVILEGES");
}
// 1. There must be a corresponding parent hash
// 2. Update best hash if the current block number is larger than current best block's number (Chain reorg)

Self::verify_header(&header)?;
let header_hash = header.hash();

Self::store_header(&header)?;
ensure!(! HeaderInfoOf::get(&header_hash).is_some(), "The header is already known.");

// let best_header_hash = Self::best_header_hash();
// if self.best_header_hash == Default::default() {
// Self::maybe_store_header(&header)?;
// }

Self::verify_header(&header)?;
Self::maybe_store_header(&header)?;

<Module<T>>::deposit_event(RawEvent::RelayHeader(relayer, header));
}
Expand All @@ -137,14 +132,6 @@ decl_module! {
<Module<T>>::deposit_event(RawEvent::VerifyProof(relayer, verified_receipt, proof_record));
}

// Assuming that there are at least one honest worker submiting headers
// This method may be merged together with relay_header
pub fn challenge_header(origin, _header: EthHeader) {
let _relayer = ensure_signed(origin)?;
// if header confirmed then return
// if header in unverified header then challenge
}

pub fn add_authority(origin, who: T::AccountId) {
let _me = ensure_root(origin)?;

Expand Down Expand Up @@ -179,9 +166,6 @@ decl_event! {
SetGenesisHeader(AccountId, EthHeader, u64),
RelayHeader(AccountId, EthHeader),
VerifyProof(AccountId, Receipt, EthReceiptProof),

// Develop
// Print(u64),
}
}

Expand All @@ -191,7 +175,6 @@ pub trait VerifyEthReceipts {
}

impl<T: Trait> Module<T> {
// TOOD: what is the total difficulty for genesis/begin header
pub fn init_genesis_header(header: &EthHeader, genesis_difficulty: u64) -> Result<(), &'static str> {
let header_hash = header.hash();

Expand All @@ -201,13 +184,13 @@ impl<T: Trait> Module<T> {

HeaderOf::insert(&header_hash, header);

// initialize the header details, including total difficulty.
HeaderDetailsOf::insert(
// initialize header info, including total difficulty.
HeaderInfoOf::insert(
&header_hash,
BlockDetails {
height: block_number,
hash: header_hash,
HeaderInfo {
parent_hash: *header.parent_hash(),
total_difficulty: genesis_difficulty.into(),
number: block_number,
},
);

Expand All @@ -216,8 +199,7 @@ impl<T: Trait> Module<T> {
*hash = header_hash;
});

// Initialize the header.
BeginHeader::put(header.clone());
GenesisHeader::put(header.clone());

Ok(())
}
Expand All @@ -229,15 +211,14 @@ impl<T: Trait> Module<T> {
ensure!(header.hash() == header.re_compute_hash(), "Header Hash - MISMATCHED");

let parent_hash = header.parent_hash();

let number = header.number();

ensure!(
number >= Self::begin_header().ok_or("Begin Header - NOT EXISTED")?.number(),
"Block Number - TOO SMALL",
);

// TODO: check parent hash is the last header, ignore or reorg
// There must be a corresponding parent hash
let prev_header = Self::header_of(parent_hash).ok_or("Previous Header - NOT EXISTED")?;
ensure!((prev_header.number() + 1) == number, "Block Number - MISMATCHED");

Expand Down Expand Up @@ -274,45 +255,89 @@ impl<T: Trait> Module<T> {
Ok(())
}

fn store_header(header: &EthHeader) -> Result<(), &'static str> {
fn maybe_store_header(header: &EthHeader) -> Result<(), &'static str> {
let best_header_info =
Self::header_info_of(Self::best_header_hash()).ok_or("Best Header Detail - NOT EXISTED")?;

if best_header_info.number > header.number + Self::number_of_blocks_finality() {
return Err("Header Too Old: It's too late to add this block header.");
}

let header_hash = header.hash();
let block_number = header.number();
HeaderOf::insert(header_hash, header);

let prev_total_difficulty = Self::header_details_of(header.parent_hash())
let parent_total_difficulty = Self::header_info_of(header.parent_hash())
.ok_or("Previous Header Detail - NOT EXISTED")?
.total_difficulty;
let best_header_hash = Self::best_header_hash();
// let best_header = Self::header_of(best_header_hash).ok_or("Can not find best header.");
let best_header_details =
Self::header_details_of(best_header_hash).ok_or("Best Header Detail - NOT EXISTED")?;

HeaderOf::insert(header_hash, header);

HeaderDetailsOf::insert(
header_hash,
BlockDetails {
height: block_number,
hash: header_hash,
total_difficulty: prev_total_difficulty + header.difficulty(),
},
);
let block_number = header.number();
let header_info = HeaderInfo {
number: block_number,
parent_hash: *header.parent_hash(),
total_difficulty: parent_total_difficulty + header.difficulty(),
};

// TODO: Check total difficulty and reorg if necessary.
if prev_total_difficulty + header.difficulty() > best_header_details.total_difficulty {
HeaderInfoOf::insert(&header_hash, header_info);

// Check total difficulty and re-org if necessary.
if header_info.total_difficulty > best_header_info.total_difficulty
|| (header_info.total_difficulty == best_header_info.total_difficulty
&& header.difficulty % 2 == U256::default())
{
// The new header is the tip of the new canonical chain.
// We need to update hashes of the canonical chain to match the new header.

// If the new header has a lower number than the previous header, we need to cleaning
// it going forward.
if best_header_info.number > header_info.number {
for number in header_info.number + 1..=best_header_info.number {
CanonicalHeaderHashOf::remove(&number);
}
}
// Replacing the global best header hash.
BestHeaderHash::mutate(|hash| {
*hash = header_hash;
});

CanonicalHeaderHashOf::insert(&header_info.number, &header_hash);

// Replacing past hashes until we converge into the same parent.
// Starting from the parent hash.
let mut number = header.number - 1;
let mut current_hash = header_info.parent_hash;
loop {
let prev_value = CanonicalHeaderHashOf::get(&number);
// If the current block hash is 0 (unlikely), or the previous hash matches the
// current hash, then we chains converged and can stop now.
if number == 0 || prev_value == current_hash {
break;
}

CanonicalHeaderHashOf::insert(&number, &current_hash);

// Check if there is an info to get the parent hash
if let Some(info) = HeaderInfoOf::get(&current_hash) {
current_hash = info.parent_hash;
} else {
break;
}
number -= 1;
}
}

Ok(())
}

// TODO: Economic model design required for the relay
fn _punish(_who: &T::AccountId) -> Result<(), &'static str> {
unimplemented!()
}
}

impl<T: Trait> VerifyEthReceipts for Module<T> {
/// confirm that the block hash is right
/// get the receipt MPT trie root from the block header
/// Using receipt MPT trie root to verify the proof and index etc.
fn verify_receipt(proof_record: &EthReceiptProof) -> Result<Receipt, &'static str> {
let header = Self::header_of(&proof_record.header_hash).ok_or("Header - NOT EXISTED")?;
let proof: Proof = rlp::decode(&proof_record.proof).map_err(|_| "Rlp Decode - FAILED")?;
Expand All @@ -323,8 +348,5 @@ impl<T: Trait> VerifyEthReceipts for Module<T> {
let receipt = rlp::decode(&value).map_err(|_| "Deserialize Receipt - FAILED")?;

Ok(receipt)
// confirm that the block hash is right
// get the receipt MPT trie root from the block header
// Using receipt MPT trie root to verify the proof and index etc.
}
}
4 changes: 2 additions & 2 deletions frame/chainrelay/eth/relay/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ fn relay_header() {

assert_ok!(EthRelay::verify_header(&header2));

assert_ok!(EthRelay::store_header(&header2));
assert_ok!(EthRelay::maybe_store_header(&header2));


// 6760581
Expand All @@ -168,6 +168,6 @@ fn relay_header() {

assert_ok!(EthRelay::verify_header(&header3));

assert_ok!(EthRelay::store_header(&header3));
assert_ok!(EthRelay::maybe_store_header(&header3));
});
}

0 comments on commit 0d7e6e8

Please sign in to comment.