// Copyright 2019-2021 Parity Technologies (UK) Ltd. // This file is part of Parity Bridges Common. // Parity Bridges Common is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Parity Bridges Common is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . //! Utilities to build bridged chain and BEEFY+MMR structures. use crate::{ mock::{ sign_commitment, validator_pairs, BeefyPair, TestBridgedBlockNumber, TestBridgedCommitment, TestBridgedHeader, TestBridgedMmrHash, TestBridgedMmrHashing, TestBridgedMmrNode, TestBridgedMmrProof, TestBridgedRawMmrLeaf, TestBridgedValidatorSet, TestBridgedValidatorSignature, TestRuntime, }, utils::get_authorities_mmr_root, }; use bp_beefy::{BeefyPayload, Commitment, ValidatorSetId, MMR_ROOT_PAYLOAD_ID}; use codec::Encode; use pallet_mmr::NodeIndex; use rand::Rng; use sp_beefy::mmr::{BeefyNextAuthoritySet, MmrLeafVersion}; use sp_core::Pair; use sp_runtime::traits::{Hash, Header as HeaderT}; use std::collections::HashMap; #[derive(Debug, Clone)] pub struct HeaderAndCommitment { pub header: TestBridgedHeader, pub commitment: Option, pub validator_set: TestBridgedValidatorSet, pub leaf: TestBridgedRawMmrLeaf, pub leaf_proof: TestBridgedMmrProof, pub mmr_root: TestBridgedMmrHash, } impl HeaderAndCommitment { pub fn customize_signatures( &mut self, f: impl FnOnce(&mut Vec>), ) { if let Some(commitment) = &mut self.commitment { f(&mut commitment.signatures); } } pub fn customize_commitment( &mut self, f: impl FnOnce(&mut Commitment), validator_pairs: &[BeefyPair], signature_count: usize, ) { if let Some(mut commitment) = self.commitment.take() { f(&mut commitment.commitment); self.commitment = Some(sign_commitment(commitment.commitment, validator_pairs, signature_count)); } } } pub struct ChainBuilder { headers: Vec, validator_set_id: ValidatorSetId, validator_keys: Vec, mmr: mmr_lib::MMR, } struct BridgedMmrStorage { nodes: HashMap, } impl mmr_lib::MMRStore for BridgedMmrStorage { fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result> { Ok(self.nodes.get(&pos).cloned()) } fn append(&mut self, pos: NodeIndex, elems: Vec) -> mmr_lib::Result<()> { for (i, elem) in elems.into_iter().enumerate() { self.nodes.insert(pos + i as NodeIndex, elem); } Ok(()) } } impl ChainBuilder { /// Creates new chain builder with given validator set size. pub fn new(initial_validators_count: u32) -> Self { ChainBuilder { headers: Vec::new(), validator_set_id: 0, validator_keys: validator_pairs(0, initial_validators_count), mmr: mmr_lib::MMR::new(0, BridgedMmrStorage { nodes: HashMap::new() }), } } /// Get header with given number. pub fn header(&self, number: TestBridgedBlockNumber) -> HeaderAndCommitment { self.headers[number as usize - 1].clone() } /// Returns single built header. pub fn to_header(&self) -> HeaderAndCommitment { assert_eq!(self.headers.len(), 1); self.headers[0].clone() } /// Returns built chain. pub fn to_chain(&self) -> Vec { self.headers.clone() } /// Appends header, that has been finalized by BEEFY (so it has a linked signed commitment). pub fn append_finalized_header(self) -> Self { let next_validator_set_id = self.validator_set_id; let next_validator_keys = self.validator_keys.clone(); HeaderBuilder::with_chain(self, next_validator_set_id, next_validator_keys).finalize() } /// Append multiple finalized headers at once. pub fn append_finalized_headers(mut self, count: usize) -> Self { for _ in 0..count { self = self.append_finalized_header(); } self } /// Appends header, that enacts new validator set. /// /// Such headers are explicitly finalized by BEEFY. pub fn append_handoff_header(self, next_validators_len: u32) -> Self { let new_validator_set_id = self.validator_set_id + 1; let new_validator_pairs = validator_pairs(rand::thread_rng().gen::() % (u32::MAX / 2), next_validators_len); HeaderBuilder::with_chain(self, new_validator_set_id, new_validator_pairs).finalize() } /// Append several default header without commitment. pub fn append_default_headers(mut self, count: usize) -> Self { for _ in 0..count { let next_validator_set_id = self.validator_set_id; let next_validator_keys = self.validator_keys.clone(); self = HeaderBuilder::with_chain(self, next_validator_set_id, next_validator_keys).build() } self } } /// Custom header builder. pub struct HeaderBuilder { chain: ChainBuilder, header: TestBridgedHeader, leaf: TestBridgedRawMmrLeaf, leaf_proof: Option, next_validator_set_id: ValidatorSetId, next_validator_keys: Vec, } impl HeaderBuilder { fn with_chain( chain: ChainBuilder, next_validator_set_id: ValidatorSetId, next_validator_keys: Vec, ) -> Self { // we're starting with header#1, since header#0 is always finalized let header_number = chain.headers.len() as TestBridgedBlockNumber + 1; let header = TestBridgedHeader::new( header_number, Default::default(), Default::default(), chain.headers.last().map(|h| h.header.hash()).unwrap_or_default(), Default::default(), ); let next_validators = next_validator_keys.iter().map(|pair| pair.public()).collect::>(); let next_validators_mmr_root = get_authorities_mmr_root::(next_validators.iter()); let leaf = sp_beefy::mmr::MmrLeaf { version: MmrLeafVersion::new(1, 0), parent_number_and_hash: (header.number().saturating_sub(1), *header.parent_hash()), beefy_next_authority_set: BeefyNextAuthoritySet { id: next_validator_set_id, len: next_validators.len() as u32, root: next_validators_mmr_root, }, leaf_extra: (), }; HeaderBuilder { chain, header, leaf, leaf_proof: None, next_validator_keys, next_validator_set_id, } } /// Customize generated proof of header MMR leaf. /// /// Can only be called once. pub fn customize_proof( mut self, f: impl FnOnce(TestBridgedMmrProof) -> TestBridgedMmrProof, ) -> Self { assert!(self.leaf_proof.is_none()); let leaf_hash = TestBridgedMmrHashing::hash(&self.leaf.encode()); let node = TestBridgedMmrNode::Hash(leaf_hash); let leaf_position = self.chain.mmr.push(node).unwrap(); let proof = self.chain.mmr.gen_proof(vec![leaf_position]).unwrap(); // genesis has no leaf => leaf index is header number minus 1 let leaf_index = *self.header.number() - 1; let leaf_count = *self.header.number(); self.leaf_proof = Some(f(TestBridgedMmrProof { leaf_indices: vec![leaf_index], leaf_count, items: proof.proof_items().iter().map(|i| i.hash()).collect(), })); self } /// Build header without commitment. pub fn build(mut self) -> ChainBuilder { if self.leaf_proof.is_none() { self = self.customize_proof(|proof| proof); } let validators = self.chain.validator_keys.iter().map(|pair| pair.public()).collect::>(); self.chain.headers.push(HeaderAndCommitment { header: self.header, commitment: None, validator_set: TestBridgedValidatorSet::new(validators, self.chain.validator_set_id) .unwrap(), leaf: self.leaf, leaf_proof: self.leaf_proof.expect("guaranteed by the customize_proof call above; qed"), mmr_root: self.chain.mmr.get_root().unwrap().hash(), }); self.chain.validator_set_id = self.next_validator_set_id; self.chain.validator_keys = self.next_validator_keys; self.chain } /// Build header with commitment. pub fn finalize(self) -> ChainBuilder { let validator_count = self.chain.validator_keys.len(); let current_validator_set_id = self.chain.validator_set_id; let current_validator_set_keys = self.chain.validator_keys.clone(); let mut chain = self.build(); let last_header = chain.headers.last_mut().expect("added by append_header; qed"); last_header.commitment = Some(sign_commitment( Commitment { payload: BeefyPayload::from_single_entry( MMR_ROOT_PAYLOAD_ID, chain.mmr.get_root().unwrap().hash().encode(), ), block_number: *last_header.header.number(), validator_set_id: current_validator_set_id, }, ¤t_validator_set_keys, validator_count * 2 / 3 + 1, )); chain } } /// Default Merging & Hashing behavior for MMR. pub struct BridgedMmrHashMerge; impl mmr_lib::Merge for BridgedMmrHashMerge { type Item = TestBridgedMmrNode; fn merge(left: &Self::Item, right: &Self::Item) -> Self::Item { let mut concat = left.hash().as_ref().to_vec(); concat.extend_from_slice(right.hash().as_ref()); TestBridgedMmrNode::Hash(TestBridgedMmrHashing::hash(&concat)) } }