Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
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 Cargo.lock

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

2 changes: 1 addition & 1 deletion srml/bridge/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ edition = "2018"
[dependencies]
codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false }
hash-db = { version = "0.15.2", default-features = false }
primitives = { package = "substrate-primitives", path = "../../core/primitives" }
session = { package = "srml-session", path = "../session", default-features = false, features = ["historical"] }
serde = { version = "1.0", optional = true }
sr-primitives = { path = "../../core/sr-primitives", default-features = false }
Expand All @@ -17,7 +18,6 @@ system = { package = "srml-system", path = "../system", default-features = false
trie = { package = "substrate-trie", path = "../../core/trie", default-features = false }

[dev-dependencies]
primitives = { package = "substrate-primitives", path = "../../core/primitives" }
runtime-io = { package = "sr-io", path = "../../core/sr-io", default-features = false }
state-machine = { package = "substrate-state-machine", path = "../../core/state-machine" }

Expand Down
5 changes: 3 additions & 2 deletions srml/bridge/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
#[derive(PartialEq)]
#[cfg_attr(feature = "std", derive(Debug))]
pub enum Error {
StorageRootMismatch,
StorageValueUnavailable,
StorageRootMismatch,
StorageValueUnavailable,
AncestorNotFound,
}

233 changes: 202 additions & 31 deletions srml/bridge/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
//! This will eventually have some useful documentation.
//! For now though, enjoy this cow's wisdom.
//!
//!```ignore
//!________________________________________
//! / You are only young once, but you can \
//! \ stay immature indefinitely. /
Expand All @@ -28,13 +29,15 @@
//! (__)\ )\/\
//! ||----w |
//! || ||
//!```

// Ensure we're `no_std` when compiling for Wasm.
#![cfg_attr(not(feature = "std"), no_std)]

mod error;
mod storage_proof;

use crate::storage_proof::StorageProofChecker;
use codec::{Encode, Decode};
use sr_primitives::traits::{Header, Member};
use support::{
Expand All @@ -49,15 +52,15 @@ pub struct BridgeInfo<T: Trait> {
last_finalized_block_number: T::BlockNumber,
last_finalized_block_hash: T::Hash,
last_finalized_state_root: T::Hash,
current_validator_set: Vec<T::ValidatorId>,
current_validator_set: Vec<(T::ValidatorId, ValidatorWeight)>,
}

impl<T: Trait> BridgeInfo<T> {
pub fn new(
block_number: &T::BlockNumber,
block_hash: &T::Hash,
state_root: &T::Hash,
validator_set: Vec<T::ValidatorId>,
validator_set: Vec<(T::ValidatorId, ValidatorWeight)>,
) -> Self
{
// I don't like how this is done, should come back to...
Expand All @@ -71,6 +74,7 @@ impl<T: Trait> BridgeInfo<T> {
}

type BridgeId = u64;
type ValidatorWeight = u64;

pub trait Trait: system::Trait {
/// A stable ID for a validator.
Expand All @@ -90,32 +94,27 @@ decl_storage! {

decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
// TODO: Figure out the proper type for these proofs
// TODO: Change proof type to StorageProof once #3834 is merged
fn initialize_bridge(
origin,
block_header: T::Header,
validator_set: Vec<T::ValidatorId>,
validator_set_proof: Vec<u8>,
storage_proof: Vec<u8>,
validator_set: Vec<(T::ValidatorId, ValidatorWeight)>,
validator_set_proof: Vec<Vec<u8>>,
) {
// NOTE: Will want to make this a governance issued call
let _sender = ensure_signed(origin)?;

Self::check_storage_proof(storage_proof)?;
Self::check_validator_set_proof(validator_set_proof)?;

let block_number = block_header.number();
let block_hash = block_header.hash();
let state_root = block_header.state_root();

Self::check_validator_set_proof(state_root, validator_set_proof, &validator_set)?;

let bridge_info = BridgeInfo::new(block_number, &block_hash, state_root, validator_set);

let new_bridge_id = NumBridges::get() + 1;

// Hmm, can a call to `insert` fail?
// If this is an Option, why do we not need to wrap it in Some(T)?
<TrackedBridges<T>>::insert(new_bridge_id, bridge_info);

// Only increase the number of bridges if the insert call succeeds
NumBridges::put(new_bridge_id);
}

Expand All @@ -130,30 +129,80 @@ decl_error! {
pub enum Error {
InvalidStorageProof,
InvalidValidatorSetProof,
ValidatorSetMismatch,
AncestorNotFound,
}
}

impl<T: Trait> Module<T> {
fn check_storage_proof(_proof: Vec<u8>) -> std::result::Result<(), Error> {
// ... Do some math ...
Ok(()) // Otherwise, Error::InvalidStorageProof
fn check_validator_set_proof(
state_root: &T::Hash,
proof: Vec<Vec<u8>>,
validator_set: &Vec<(T::ValidatorId, ValidatorWeight)>,
) -> std::result::Result<(), Error> {

// pub const GRANDPA_AUTHORITIES_KEY: &'static [u8] = b":grandpa_authorities";
// pub type AuthorityList = Vec<(AuthorityId, AuthorityWeight)>;
let checker = <StorageProofChecker<<T::Hashing as sr_primitives::traits::Hash>::Hasher>>::new(
*state_root,
proof.clone()
).unwrap();

// By encoding the given set we should have an easy way to compare
// with the stuff we get out of storage via `read_value`
let encoded_validator_set = validator_set.encode();
let actual_validator_set = checker.read_value(b":grandpa_authorities").unwrap().unwrap();

if encoded_validator_set == actual_validator_set {
Ok(())
} else {
Err(Error::ValidatorSetMismatch)
}
}
}

fn check_validator_set_proof(_proof: Vec<u8>) -> std::result::Result<(), Error> {
// ... Do some math ...
Ok(()) // Otherwise, Error::InvalidValidatorSetProof
// A naive way to check whether a `child` header is an ancestor
// of an `ancestor` header. For this it requires a proof which
// is a header chain, This could be updated to use something like
// Log2 Ancestors (#2053) in the future.
fn verify_ancestry<H>(proof: Vec<H>, ancestor: H, child: H) -> std::result::Result<(), Error>
where
H: Header
{
let mut curr_header = &proof[0];
if curr_header.hash() != child.hash() {
return Err(Error::AncestorNotFound);
}

let mut parent_hash = curr_header.parent_hash();

// If we find that the header's parent hash matches our ancestor's hash we're done
for i in 1..proof.len() {
curr_header = &proof[i];

// Need to check that blocks are actually related
if curr_header.hash() != *parent_hash {
break;
}

parent_hash = curr_header.parent_hash();
if *parent_hash == ancestor.hash() {
return Ok(())
}
}

Err(Error::AncestorNotFound)
}

#[cfg(test)]
mod tests {
use super::*;

use primitives::H256;
use primitives::{Blake2Hasher, H256};
use sr_primitives::{
Perbill, traits::{Header as HeaderT, IdentityLookup}, testing::Header, generic::Digest,
};
use support::{assert_ok, impl_outer_origin, parameter_types};
use support::{assert_ok, assert_err, impl_outer_origin, parameter_types};

// NOTE: What's this for?
impl_outer_origin! {
Expand All @@ -163,7 +212,7 @@ mod tests {
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Test;

type System = system::Module<Test>;
type _System = system::Module<Test>;
type MockBridge = Module<Test>;

// TODO: Figure out what I actually need from here
Expand Down Expand Up @@ -214,42 +263,164 @@ mod tests {
});
}

fn create_dummy_validator_proof(validator_set: Vec<(DummyValidatorId, ValidatorWeight)>) -> (H256, Vec<Vec<u8>>) {
use state_machine::{prove_read, backend::{Backend, InMemory}};

let encoded_set = validator_set.encode();

// construct storage proof
let backend = <InMemory<Blake2Hasher>>::from(vec![
(None, b":grandpa_authorities".to_vec(), Some(encoded_set)),
]);
let root = backend.storage_root(std::iter::empty()).0;

// Generates a storage read proof
let proof = prove_read(backend, &[&b":grandpa_authorities"[..]]).unwrap();

(root, proof)
}

#[test]
fn it_can_validate_validator_sets() {
let validators = vec![(1, 1), (2, 1), (3, 1)];
let (root, proof) = create_dummy_validator_proof(validators.clone());

assert_ok!(MockBridge::check_validator_set_proof(&root, proof, &validators));
}

#[test]
fn it_rejects_invalid_validator_sets() {
let validators = vec![(1, 1), (2, 1), (3, 1)];
let (root, proof) = create_dummy_validator_proof(validators.clone());

let invalid_validators = vec![(3, 1), (2, 1), (1, 1)];
assert_err!(
MockBridge::check_validator_set_proof(&root, proof, &invalid_validators),
Error::ValidatorSetMismatch
);
}

#[test]
fn it_creates_a_new_bridge() {
let dummy_state_root = H256::default();
let validators = vec![(1, 1), (2, 1), (3, 1)];
let (root, proof) = create_dummy_validator_proof(validators.clone());

let test_header = Header {
parent_hash: H256::default(),
number: 42,
state_root: dummy_state_root,
state_root: root,
extrinsics_root: H256::default(),
digest: Digest::default(),
};
let test_hash = test_header.hash();

new_test_ext().execute_with(|| {
assert_eq!(MockBridge::num_bridges(), 0);
assert_eq!(MockBridge::tracked_bridges(0), None);

dbg!(&test_header);
assert_ok!(
MockBridge::initialize_bridge(
Origin::signed(1),
test_header,
vec![1, 2, 3],
vec![],
vec![],
validators.clone(),
proof,
));

assert_eq!(
MockBridge::tracked_bridges(1),
Some(BridgeInfo {
last_finalized_block_number: 42,
last_finalized_block_hash: test_hash,
last_finalized_state_root: dummy_state_root,
current_validator_set: vec![1, 2, 3],
last_finalized_state_root: root,
current_validator_set: validators.clone(),
}));

assert_eq!(MockBridge::num_bridges(), 1);
});
}

fn get_related_block_headers() -> (Header, Header, Header) {
let grandparent = Header {
parent_hash: H256::default(),
number: 1,
state_root: H256::default(),
extrinsics_root: H256::default(),
digest: Digest::default(),
};

let parent = Header {
parent_hash: grandparent.hash(),
number: grandparent.number() + 1,
state_root: H256::default(),
extrinsics_root: H256::default(),
digest: Digest::default(),
};

let child = Header {
parent_hash: parent.hash(),
number: parent.number() + 1,
state_root: H256::default(),
extrinsics_root: H256::default(),
digest: Digest::default(),
};

(grandparent, parent, child)
}

#[test]
fn check_that_child_is_ancestor_of_grandparent() {
let (grandparent, parent, child) = get_related_block_headers();

let mut proof = Vec::new();
proof.push(child.clone());
proof.push(parent);
proof.push(grandparent.clone());

assert_ok!(verify_ancestry(proof, grandparent, child));
}

#[test]
fn check_that_child_ancestor_is_not_correct() {
let (grandparent, parent, child) = get_related_block_headers();

let mut proof = Vec::new();
proof.push(child.clone());
proof.push(parent);
proof.push(grandparent.clone());

let fake_grandparent = Header {
parent_hash: H256::from_slice(&[1u8; 32]),
number: 42,
state_root: H256::default(),
extrinsics_root: H256::default(),
digest: Digest::default(),
};

assert_err!(
verify_ancestry(proof, fake_grandparent, child),
Error::AncestorNotFound
);
}

#[test]
fn checker_fails_if_given_invalid_proof() {
let (grandparent, parent, child) = get_related_block_headers();
let fake_ancestor = Header {
parent_hash: H256::from_slice(&[1u8; 32]),
number: 42,
state_root: H256::default(),
extrinsics_root: H256::default(),
digest: Digest::default(),
};

let mut invalid_proof = Vec::new();
invalid_proof.push(child.clone());
invalid_proof.push(fake_ancestor);
invalid_proof.push(parent);
invalid_proof.push(grandparent.clone());

assert_err!(
verify_ancestry(invalid_proof, grandparent, child),
Error::AncestorNotFound
);
}
}