Skip to content

Commit

Permalink
fix: add barebones for BTC-relay.
Browse files Browse the repository at this point in the history
  • Loading branch information
nakul1010 committed Sep 20, 2023
1 parent 719192e commit ea09b0e
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 7 deletions.
92 changes: 85 additions & 7 deletions crates/btc-relay/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
//! - **Parachain Confirmations:** The minimum number of Parachain confirmations a Bitcoin block header must have to be
//! usable in transaction inclusion verification.

#![deny(warnings)]
// #![deny(warnings)]
#![cfg_attr(test, feature(proc_macro_hygiene))]
#![cfg_attr(not(feature = "std"), no_std)]

Expand Down Expand Up @@ -65,6 +65,7 @@ use frame_support::{
transactional,
};
use frame_system::{ensure_signed, pallet_prelude::BlockNumberFor};
use sp_arithmetic::traits::Saturating;
use sp_core::{H256, U256};
use sp_runtime::traits::{CheckedAdd, CheckedDiv, CheckedSub, One};
use sp_std::{
Expand Down Expand Up @@ -320,6 +321,11 @@ pub mod pallet {
InvalidCoinbasePosition,
}

/// Store ChainWork
/// mapping block hash -> chain work
#[pallet::storage]
pub(super) type ChainWork<T: Config> = StorageMap<_, Blake2_128Concat, H256Le, u32, OptionQuery>;

/// Store Bitcoin block headers
#[pallet::storage]
pub(super) type BlockHeaders<T: Config> =
Expand Down Expand Up @@ -398,6 +404,8 @@ pub mod pallet {
StableParachainConfirmations::<T>::put(self.parachain_confirmations);
DisableDifficultyCheck::<T>::put(self.disable_difficulty_check);
DisableInclusionCheck::<T>::put(self.disable_inclusion_check);
// ToDo: insert genesis block hash and block work as zero
ChainWork::<T>::insert(H256Le::zero(), 0);
}
}
}
Expand Down Expand Up @@ -469,18 +477,14 @@ impl<T: Config> Pallet<T> {

// Update the blockchain
// check if we create a new blockchain or extend the existing one
runtime_print!("Prev max height: {:?}", prev_blockchain.max_height);
runtime_print!("Prev block height: {:?}", prev_block_height);
let is_new_fork = prev_blockchain.max_height != prev_block_height;
runtime_print!("Fork detected: {:?}", is_new_fork);

let chain_id = if is_new_fork {
// create new blockchain element
Self::create_and_store_blockchain(current_block_height, &basic_block_header)?
} else {
// extend the current chain
let blockchain = Self::extend_blockchain(current_block_height, &basic_block_header, prev_blockchain)?;

if blockchain.chain_id != MAIN_CHAIN_ID {
// if we added a block to a fork, we may need to reorder the chains
Self::reorganize_chains(&blockchain)?;
Expand Down Expand Up @@ -767,6 +771,14 @@ impl<T: Config> Pallet<T> {
ChainCounter::<T>::get()
}

fn get_block_chainwork(block_hash: H256Le) -> Option<u32> {
ChainWork::<T>::get(block_hash)
}

fn set_block_chainwork(block_hash: H256Le, chain_work: u32) {
ChainWork::<T>::insert(block_hash, chain_work)
}

/// Get a block hash from a blockchain
///
/// # Arguments
Expand Down Expand Up @@ -1157,7 +1169,6 @@ impl<T: Config> Pallet<T> {
// check if the previous element in Chains has a lower block_height
let mut current_position = fork_position;
let mut current_height = fork.max_height;

// swap elements as long as previous block height is smaller
while current_position > 0 {
// get the previous position
Expand All @@ -1181,7 +1192,14 @@ impl<T: Config> Pallet<T> {
// and the current height is more than the
// STABLE_TRANSACTION_CONFIRMATIONS ahead
// we are swapping the main chain
if prev_height.saturating_add(Self::get_stable_transaction_confirmations()) <= current_height {
let current_fork_work = Self::get_chainwork(Self::get_block_chain_from_id(current_position)?)?;
let prev_fork_work = Self::get_chainwork(Self::get_block_chain_from_id(prev_position)?)?;
// update: ensure_no_ongoing_fork
if prev_height.saturating_add(Self::get_stable_transaction_confirmations()) <= current_height
&&
// We should only reorg when the fork has more work than the current main chain
current_fork_work > prev_fork_work
{
// Swap the mainchain. As an optimization, this function returns the
// new best block hash and its height
let (new_chain_tip_hash, new_chain_tip_height) = Self::swap_main_blockchain(&fork)?;
Expand Down Expand Up @@ -1218,6 +1236,66 @@ impl<T: Config> Pallet<T> {
Ok(())
}

fn get_target(chain_id: u32, height: u32) -> Result<u32, DispatchError> {
let block_hash = Self::get_block_hash(chain_id, height)?;
let block_header = Self::get_block_header_from_hash(block_hash)?;
Ok(block_header.block_header.target.low_u32())
}

// Reference: https://github.com/spesmilo/electrum/blob/cee22abcb5544c5a6fa7f8a8108ccda9c32c2e29/electrum/blockchain.py#L582-L588
fn chainwork_of_header_at_height(chain_id: u32, height: u32) -> Result<u32, DispatchError> {
let target = Self::get_target(chain_id, height)?;
let work = (2_u32.saturating_pow(256).saturating_sub(target).saturating_less_one())
.saturating_div(target.saturating_plus_one())
.saturating_plus_one();
Ok(work)
}

// Reference: https://github.com/spesmilo/electrum/blob/cee22abcb5544c5a6fa7f8a8108ccda9c32c2e29/electrum/blockchain.py#L589-L614
fn get_chainwork(chain: BlockChain) -> Result<u32, DispatchError> {
println!("--- chain: {:?} ---", chain); //chain: BlockChain { chain_id: 1, start_height: 1, max_height: 2 }
let last_retarget = chain
.max_height //2025
.saturating_div(DIFFICULTY_ADJUSTMENT_INTERVAL) // should be mod floor / divide also looks good , but //
.saturating_mul(DIFFICULTY_ADJUSTMENT_INTERVAL)
.saturating_less_one(); // 2025 / 2016 * 2016 -1 = 2015

let mut cached_height = last_retarget; //2015

while Self::get_block_chainwork(Self::get_block_hash(0, cached_height)?).is_none() {
//0=chain.chain_id, cached_height=0
if cached_height == 0 {
break;
}
cached_height = cached_height.saturating_sub(DIFFICULTY_ADJUSTMENT_INTERVAL)
}

let cache_chain_work = Self::get_block_chainwork(Self::get_block_hash(0, cached_height)?);
let mut running_total = if let Some(chain_work) = cache_chain_work {
chain_work
} else {
//genesis chainwork should be 0
0_u32
};

// Loop to calculate chainwork for blocks between 'cached_height' and 'last_retarget' 0 to 2015
while cached_height < last_retarget {
cached_height = cached_height.saturating_add(DIFFICULTY_ADJUSTMENT_INTERVAL); //should this be 2017 since cached_height will not be -1
let work_in_single_header = Self::chainwork_of_header_at_height(chain.chain_id, cached_height)?;
let work_in_chunk = DIFFICULTY_ADJUSTMENT_INTERVAL.saturating_mul(work_in_single_header); //2016 * work_in_single_header
running_total = running_total.saturating_add(work_in_chunk);
Self::set_block_chainwork(Self::get_block_hash(chain.chain_id, cached_height)?, running_total);
}
cached_height = cached_height.saturating_add(DIFFICULTY_ADJUSTMENT_INTERVAL); //2016

let work_in_single_header = Self::chainwork_of_header_at_height(chain.chain_id, cached_height)?; // cached_height: 2016,chain.chain_id:1

let work_in_last_partial_chunk = ((chain.max_height % DIFFICULTY_ADJUSTMENT_INTERVAL).saturating_add(1))
.saturating_mul(work_in_single_header);

Ok(running_total.saturating_add(work_in_last_partial_chunk))
}

/// Insert a new fork into the Chains mapping sorted by its max height
///
/// # Arguments
Expand Down
1 change: 1 addition & 0 deletions crates/btc-relay/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@ mod store_block_header_tests {
);
}

// ToDo: Fix failing test
#[test]
fn store_block_header_simple_fork_succeeds() {
run_test(|| {
Expand Down

0 comments on commit ea09b0e

Please sign in to comment.