Skip to content
This repository has been archived by the owner on Mar 13, 2023. It is now read-only.

bsc light client #988

Merged
merged 32 commits into from
Jan 8, 2022
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
96e32c2
add storage proof
xiaoch05 Dec 7, 2021
f3458c7
add bsc precompile
xiaoch05 Dec 8, 2021
99bbe58
move verify locally
xiaoch05 Dec 8, 2021
18161f9
Merge branch 'master' into bsc-light-client
xiaoch05 Dec 8, 2021
cd0ff23
update version
xiaoch05 Dec 8, 2021
ede89ea
merge master
xiaoch05 Dec 14, 2021
f990fe8
add benchmark
xiaoch05 Dec 15, 2021
8c6aa26
Fix compile issue
boundless-forest Dec 15, 2021
8f6e9a9
use codec
xiaoch05 Dec 16, 2021
84ba712
merge main
xiaoch05 Dec 17, 2021
d0c41d5
weight for verify_and_update_authority_set_signed
xiaoch05 Dec 17, 2021
9b0b9b7
repair test
xiaoch05 Dec 20, 2021
fd7370e
multi-key verify
xiaoch05 Dec 20, 2021
b75756d
interface
xiaoch05 Dec 20, 2021
82d2fa5
add single proof verify
xiaoch05 Dec 21, 2021
dc41786
weight for submit_header
xiaoch05 Dec 22, 2021
959d377
reserve latest epochs
xiaoch05 Dec 22, 2021
23c3956
repair test
xiaoch05 Dec 27, 2021
e46d76b
repair ci
xiaoch05 Dec 27, 2021
46ff332
fix compile
xiaoch05 Dec 27, 2021
9b6c0cc
fmt
xiaoch05 Dec 27, 2021
90d793f
limit multi-key size
xiaoch05 Dec 27, 2021
a355b3a
rmv submit header
xiaoch05 Jan 5, 2022
c27f9a1
fix tests
hackfisher Jan 5, 2022
20bdb23
rename dispatch call
hackfisher Jan 5, 2022
b65f30f
rmv function prefix
xiaoch05 Jan 5, 2022
70712d3
Merge branch 'bsc-light-client' of github.com:darwinia-network/darwin…
xiaoch05 Jan 5, 2022
965141c
fmt dependencies
xiaoch05 Jan 6, 2022
18faa7d
use Result directly
xiaoch05 Jan 6, 2022
dcd90da
Try Merge
aurexav Jan 7, 2022
0533641
Fix Version
aurexav Jan 7, 2022
cfad41a
Format
aurexav Jan 7, 2022
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
17 changes: 17 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ members = [
"frame/evm/precompile/contracts/transfer",
"frame/evm/precompile/contracts/sha3fips",
"frame/evm/precompile/contracts/utils",
"frame/evm/precompile/contracts/bridge/s2s",
"frame/evm/precompile/contracts/bridge/bsc",
"frame/evm/precompile/contracts/bridge/ethereum",
"frame/evm/precompile/contracts/bridge/s2s",
"frame/fee-market",
"frame/fee-market/rpc",
"frame/fee-market/rpc/runtime-api",
Expand Down
30 changes: 21 additions & 9 deletions frame/bridge/bsc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,37 +11,49 @@ version = "2.7.2"

[dependencies]
# crates.io
codec = { package = "parity-scale-codec", version = "2.1", default-features = false }
array-bytes = { version = "1.4", optional = true }
codec = { package = "parity-scale-codec", version = "2.1", default-features = false }
rlp = { version = "0.5", default-features = false }
# darwinia-network
bsc-primitives = { default-features = false, path = "../../../primitives/bsc" }
bsc-primitives = { default-features = false, path = "../../../primitives/bsc" }
ethereum-primitives = { default-features = false, features = ["full-codec", "full-rlp"], path = "../../../primitives/ethereum" }
# paritytech
frame-support = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.11.8" }
frame-system = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.11.8" }
sp-core = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.11.8" }
sp-io = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.11.8" }
sp-runtime = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.11.8" }
sp-std = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.11.8" }
frame-benchmarking = { optional = true, default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.11.8" }
frame-support = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.11.8" }
frame-system = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.11.8" }
sp-core = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.11.8" }
sp-io = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.11.8" }
sp-runtime = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.11.8" }
sp-std = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.11.8" }

[dev-dependencies]
# crates.io
array-bytes = { version = "1.4" }
serde_json = { version = "1.0" }
# paritytech
pallet-timestamp = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.11.8" }
serde_json = { version = "1.0" }

[features]
default = ["std"]

std = [
# crates.io
"codec/std",
"rlp/std",
# darwinia-network
"bsc-primitives/std",
"ethereum-primitives/std",
# paritytech
"frame-benchmarking/std",
"frame-support/std",
"frame-system/std",
"sp-core/std",
"sp-io/std",
"sp-runtime/std",
"sp-std/std",
]

runtime-benchmarks = [
"frame-benchmarking",
"array-bytes",
]
78 changes: 78 additions & 0 deletions frame/bridge/bsc/src/benchmarking.rs

Large diffs are not rendered by default.

110 changes: 41 additions & 69 deletions frame/bridge/bsc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,39 +66,47 @@
//! "id": 83
//! }
//!```
//! If you only want to verify a single header, use verify_header fn is enough. The important tip is the header's number you want verify should greater
//! than genesis header, or the answer will be NO.
//! According to the official doc of Binance Smart Chain, when the authority set changed at checkpoint header, the new authority set is not taken as finalized immediately.
//! We will wait(accept and verify) N / 2 blocks(only headers) to make sure it's safe to finalize the new authority set. N is the authority set size.

#![cfg_attr(not(feature = "std"), no_std)]

#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;

pub mod weight;

#[frame_support::pallet]
pub mod pallet {
// --- paritytech ---
use frame_support::{pallet_prelude::*, traits::UnixTime};
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
use rlp::Decodable;
use sp_core::U256;
use sp_io::crypto;
use sp_runtime::{DispatchError, DispatchResult, RuntimeDebug};
#[cfg(not(feature = "std"))]
use sp_std::borrow::ToOwned;
use sp_std::{collections::btree_set::BTreeSet, prelude::*};
// --- darwinia-network ---
pub use super::weight::WeightInfo;
use bsc_primitives::*;
use ethereum_primitives::storage::{EthereumStorage, EthereumStorageProof};

#[pallet::config]
pub trait Config: frame_system::Config {
type UnixTime: UnixTime;
/// BSC configuration.
type BSCConfiguration: Get<BSCConfiguration>;
/// Handler for headers submission result.
type OnHeadersSubmitted: OnHeadersSubmitted<Self::AccountId>;
/// Max epoch length stored, cannot verify header older than this epoch length
type EpochInStorage: Get<u64>;
/// Weight information for extrinsics in this pallet.
type WeightInfo: WeightInfo;
}

#[pallet::error]
Expand Down Expand Up @@ -166,6 +174,9 @@ pub mod pallet {
///
/// Recover pubkey from signature error
RecoverPubkeyFail,

/// Verfiy Storage Proof Failed
VerifyStorageFail,
}

#[pallet::storage]
Expand Down Expand Up @@ -215,29 +226,15 @@ pub mod pallet {
pub struct Pallet<T>(_);
#[pallet::call]
impl<T: Config> Pallet<T> {
/// Verify unsigned relayed headers and finalize authority set
#[pallet::weight(0)]
pub fn verify_and_update_authority_set_unsigned(
origin: OriginFor<T>,
headers: Vec<BSCHeader>,
) -> DispatchResultWithPostInfo {
// ensure not signed
frame_system::ensure_none(origin)?;

Self::verify_and_update_authority_set(&headers)?;

Ok(().into())
}

/// Verify signed relayed headers and finalize authority set
#[pallet::weight(0)]
pub fn verify_and_update_authority_set_signed(
#[pallet::weight(<T as Config>::WeightInfo::relay_finalized_epoch_header())]
pub fn relay_finalized_epoch_header(
origin: OriginFor<T>,
headers: Vec<BSCHeader>,
proof: Vec<BSCHeader>,
) -> DispatchResultWithPostInfo {
let submitter = frame_system::ensure_signed(origin)?;

match Self::verify_and_update_authority_set(&headers) {
match Self::verify_and_update_authority_set_and_checkpoint(&proof) {
Ok(new_authority_set) => {
T::OnHeadersSubmitted::on_valid_authority_finalized(
submitter,
Expand All @@ -255,11 +252,6 @@ pub mod pallet {
}
}
impl<T: Config> Pallet<T> {
/// Return true if the header's timestamp greater or equal than current on-chain time
pub fn is_timestamp_ahead(timestamp: u64) -> bool {
T::UnixTime::now().as_millis() as u64 <= timestamp
}

/// Perform basic checks that only require header itself.
pub fn contextless_checks(config: &BSCConfiguration, header: &BSCHeader) -> DispatchResult {
// he genesis block is the always valid dead-end
Expand Down Expand Up @@ -298,12 +290,6 @@ pub mod pallet {
// Ensure that the mix digest is zero as we don't have fork protection currently
ensure!(header.mix_digest.is_zero(), <Error<T>>::InvalidMixDigest);

// Don't waste time checking blocks from the future
ensure!(
!Self::is_timestamp_ahead(header.timestamp),
<Error<T>>::HeaderTimestampIsAhead
);

// Check that the extra-data contains the vanity, validators and signature.
ensure!(
header.extra_data.len() > VANITY_LENGTH,
Expand Down Expand Up @@ -428,42 +414,8 @@ pub mod pallet {
Ok(signers)
}

/// Verify single header
/// The header number should in the range `[genesis_header.number, finalized_checkpoint.number + N]`
/// Before the first call of verify_and_update_authority_set extrinsic, genesis_header == finalized_checkpoint
pub fn verify_header(header: &BSCHeader) -> DispatchResult {
let cfg = T::BSCConfiguration::get();
// ensure the number is in the range
let round = header.number / cfg.epoch_length;

ensure!(
<AuthoritiesOfRound<T>>::contains_key(round),
// it could be the signer which signed your header has not been finalized yet
// or your header.number is less than the genesis header number
<Error::<T>>::RidiculousNumber
);

Self::contextless_checks(&cfg, header)?;

// get index vec
let authorities_of_round = <AuthoritiesOfRound<T>>::get(round);
// get all authorities
let authorities = <Authorities<T>>::get();
// filter authorities of this round out
let signers = authorities_of_round
.into_iter()
.map(|i| authorities[i as usize])
.collect::<Vec<_>>();
// check signer
let signer = Self::recover_creator(cfg.chain_id, header)?;

ensure!(contains(&signers, signer), <Error::<T>>::InvalidSigner);

Ok(())
}

/// Verify unsigned relayed headers and finalize authority set
pub fn verify_and_update_authority_set(
pub fn verify_and_update_authority_set_and_checkpoint(
headers: &[BSCHeader],
) -> Result<Vec<Address>, DispatchError> {
// get finalized authority set from storage
Expand Down Expand Up @@ -553,7 +505,13 @@ pub mod pallet {

<Authorities<T>>::put(authorities);
// insert this epoch's authority indexes
<AuthoritiesOfRound<T>>::insert(checkpoint.number / cfg.epoch_length, indexes);
let epoch = checkpoint.number / cfg.epoch_length;
<AuthoritiesOfRound<T>>::insert(epoch, indexes);
if epoch > T::EpochInStorage::get()
&& <AuthoritiesOfRound<T>>::contains_key(epoch - T::EpochInStorage::get())
{
<AuthoritiesOfRound<T>>::remove(epoch - T::EpochInStorage::get());
}

// skip the rest submitted headers
return Ok(new_authority_set);
Expand Down Expand Up @@ -605,5 +563,19 @@ pub mod pallet {
fn contains(signers: &[Address], signer: Address) -> bool {
signers.iter().any(|i| *i == signer)
}

pub trait StorageVerifier<S> {
fn verify_storage(proof: &EthereumStorageProof) -> Result<S, DispatchError>;
}

impl<T: Config, S: Decodable> StorageVerifier<S> for Pallet<T> {
fn verify_storage(proof: &EthereumStorageProof) -> Result<S, DispatchError> {
let finalized_header = <FinalizedCheckpoint<T>>::get();
let storage =
EthereumStorage::<S>::verify_storage_proof(finalized_header.state_root, proof)
.map_err(|_| <Error<T>>::VerifyStorageFail)?;
Ok(storage.0)
}
}
}
pub use pallet::*;
4 changes: 3 additions & 1 deletion frame/bridge/bsc/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,13 @@ frame_support::parameter_types! {
period: 0x03,
epoch_length: 0xc8,
};
pub const EpochInStorage: u64 = 128;
}
impl Config for Test {
type WeightInfo = ();
type BSCConfiguration = Configuration;
type UnixTime = Timestamp;
type OnHeadersSubmitted = ();
type EpochInStorage = EpochInStorage;
}

frame_support::construct_runtime! {
Expand Down
Loading