Match substrate's fmt (#1148)

* Alter gitlab.

* Use substrate's rustfmt.toml

* cargo +nightly fmt --all

* Fix spellcheck.

* cargo +nightly fmt --all

* format.

* Fix spellcheck and fmt

* fmt?

* Fix spellcheck

Co-authored-by: Tomasz Drwięga <tomasz@parity.io>
This commit is contained in:
hacpy
2021-09-24 19:29:31 +08:00
committed by Bastian Köcher
parent 87cbb382d9
commit bd70de8b8b
174 changed files with 6095 additions and 4962 deletions
+2 -2
View File
@@ -17,8 +17,8 @@
use super::*;
use crate::test_utils::{
build_custom_header, build_genesis_header, insert_header, validator_utils::*, validators_change_receipt,
HeaderBuilder,
build_custom_header, build_genesis_header, insert_header, validator_utils::*,
validators_change_receipt, HeaderBuilder,
};
use bp_eth_poa::{compute_merkle_root, U256};
+2 -1
View File
@@ -85,7 +85,8 @@ impl Error {
Error::InsufficientProof => "Header has insufficient proof",
Error::InvalidDifficulty => "Header has invalid difficulty",
Error::NotValidator => "Header is sealed by unexpected validator",
Error::MissingTransactionsReceipts => "The import operation requires transactions receipts",
Error::MissingTransactionsReceipts =>
"The import operation requires transactions receipts",
Error::RedundantTransactionsReceipts => "Redundant transactions receipts are provided",
Error::TransactionsReceiptsMismatch => "Invalid transactions receipts provided",
Error::UnsignedTooFarInTheFuture => "The unsigned header is too far in future",
+45 -44
View File
@@ -14,18 +14,19 @@
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
use crate::error::Error;
use crate::Storage;
use crate::{error::Error, Storage};
use bp_eth_poa::{public_to_address, Address, AuraHeader, HeaderId, SealedEmptyStep, H256};
use codec::{Decode, Encode};
use sp_io::crypto::secp256k1_ecdsa_recover;
use sp_runtime::RuntimeDebug;
use sp_std::collections::{
btree_map::{BTreeMap, Entry},
btree_set::BTreeSet,
vec_deque::VecDeque,
use sp_std::{
collections::{
btree_map::{BTreeMap, Entry},
btree_set::BTreeSet,
vec_deque::VecDeque,
},
prelude::*,
};
use sp_std::prelude::*;
/// Cached finality votes for given block.
#[derive(RuntimeDebug)]
@@ -116,17 +117,14 @@ pub fn finalize_blocks<S: Storage>(
&current_votes,
ancestor.id.number >= two_thirds_majority_transition,
) {
break;
break
}
remove_signers_votes(&ancestor.signers, &mut current_votes);
finalized_headers.push((ancestor.id, ancestor.submitter.clone()));
}
Ok(FinalityEffects {
finalized_headers,
votes,
})
Ok(FinalityEffects { finalized_headers, votes })
}
/// Returns true if there are enough votes to treat this header as finalized.
@@ -135,8 +133,8 @@ fn is_finalized(
votes: &BTreeMap<Address, u64>,
requires_two_thirds_majority: bool,
) -> bool {
(!requires_two_thirds_majority && votes.len() * 2 > validators.len())
|| (requires_two_thirds_majority && votes.len() * 3 > validators.len() * 2)
(!requires_two_thirds_majority && votes.len() * 2 > validators.len()) ||
(requires_two_thirds_majority && votes.len() * 3 > validators.len() * 2)
}
/// Prepare 'votes' of header and its ancestors' signers.
@@ -151,12 +149,12 @@ pub(crate) fn prepare_votes<Submitter>(
// if we have reached finalized block sibling, then we're trying
// to switch finalized blocks
if cached_votes.stopped_at_finalized_sibling {
return Err(Error::TryingToFinalizeSibling);
return Err(Error::TryingToFinalizeSibling)
}
// this fn can only work with single validators set
if !validators.contains(&header.author) {
return Err(Error::NotValidator);
return Err(Error::NotValidator)
}
// now we have votes that were valid when some block B has been inserted
@@ -171,7 +169,7 @@ pub(crate) fn prepare_votes<Submitter>(
while let Some(old_ancestor) = votes.ancestry.pop_front() {
if old_ancestor.id.number > best_finalized.number {
votes.ancestry.push_front(old_ancestor);
break;
break
}
remove_signers_votes(&old_ancestor.signers, &mut votes.votes);
@@ -180,7 +178,9 @@ pub(crate) fn prepare_votes<Submitter>(
// add votes from new blocks
let mut parent_empty_step_signers = empty_steps_signers(header);
let mut unaccounted_ancestry = VecDeque::new();
while let Some((ancestor_id, ancestor_submitter, ancestor)) = cached_votes.unaccounted_ancestry.pop_front() {
while let Some((ancestor_id, ancestor_submitter, ancestor)) =
cached_votes.unaccounted_ancestry.pop_front()
{
let mut signers = empty_steps_signers(&ancestor);
sp_std::mem::swap(&mut signers, &mut parent_empty_step_signers);
signers.insert(ancestor.author);
@@ -199,11 +199,9 @@ pub(crate) fn prepare_votes<Submitter>(
let mut header_signers = BTreeSet::new();
header_signers.insert(header.author);
*votes.votes.entry(header.author).or_insert(0) += 1;
votes.ancestry.push_back(FinalityAncestor {
id,
submitter,
signers: header_signers,
});
votes
.ancestry
.push_back(FinalityAncestor { id, submitter, signers: header_signers });
Ok(votes)
}
@@ -217,7 +215,7 @@ fn add_signers_votes(
) -> Result<(), Error> {
for signer in signers_to_add {
if !validators.contains(signer) {
return Err(Error::NotValidator);
return Err(Error::NotValidator)
}
*votes.entry(*signer).or_insert(0) += 1;
@@ -230,13 +228,12 @@ fn add_signers_votes(
fn remove_signers_votes(signers_to_remove: &BTreeSet<Address>, votes: &mut BTreeMap<Address, u64>) {
for signer in signers_to_remove {
match votes.entry(*signer) {
Entry::Occupied(mut entry) => {
Entry::Occupied(mut entry) =>
if *entry.get() <= 1 {
entry.remove();
} else {
*entry.get_mut() -= 1;
}
}
},
Entry::Vacant(_) => unreachable!("we only remove signers that have been added; qed"),
}
}
@@ -272,18 +269,19 @@ impl<Submitter> Default for CachedFinalityVotes<Submitter> {
impl<Submitter> Default for FinalityVotes<Submitter> {
fn default() -> Self {
FinalityVotes {
votes: BTreeMap::new(),
ancestry: VecDeque::new(),
}
FinalityVotes { votes: BTreeMap::new(), ancestry: VecDeque::new() }
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mock::{insert_header, run_test, validator, validators_addresses, HeaderBuilder, TestRuntime};
use crate::{BridgeStorage, FinalityCache, HeaderToImport};
use crate::{
mock::{
insert_header, run_test, validator, validators_addresses, HeaderBuilder, TestRuntime,
},
BridgeStorage, FinalityCache, HeaderToImport,
};
const TOTAL_VALIDATORS: usize = 5;
@@ -341,7 +339,8 @@ mod tests {
storage.insert_header(header_to_import.clone());
// when header#2 is inserted, nothing is finalized (2 votes)
header_to_import.header = HeaderBuilder::with_parent_hash(id1.hash).sign_by(&validator(1));
header_to_import.header =
HeaderBuilder::with_parent_hash(id1.hash).sign_by(&validator(1));
header_to_import.id = header_to_import.header.compute_id();
let id2 = header_to_import.header.compute_id();
assert_eq!(
@@ -360,7 +359,8 @@ mod tests {
storage.insert_header(header_to_import.clone());
// when header#3 is inserted, header#1 is finalized (3 votes)
header_to_import.header = HeaderBuilder::with_parent_hash(id2.hash).sign_by(&validator(2));
header_to_import.header =
HeaderBuilder::with_parent_hash(id2.hash).sign_by(&validator(2));
header_to_import.id = header_to_import.header.compute_id();
let id3 = header_to_import.header.compute_id();
assert_eq!(
@@ -390,7 +390,9 @@ mod tests {
// 2) add votes from header#4 and header#5
let validators = validators_addresses(5);
let headers = (1..6)
.map(|number| HeaderBuilder::with_number(number).sign_by(&validator(number as usize - 1)))
.map(|number| {
HeaderBuilder::with_number(number).sign_by(&validator(number as usize - 1))
})
.collect::<Vec<_>>();
let ancestry = headers
.iter()
@@ -405,9 +407,10 @@ mod tests {
prepare_votes::<()>(
CachedFinalityVotes {
stopped_at_finalized_sibling: false,
unaccounted_ancestry: vec![(headers[3].compute_id(), None, headers[3].clone()),]
.into_iter()
.collect(),
unaccounted_ancestry:
vec![(headers[3].compute_id(), None, headers[3].clone()),]
.into_iter()
.collect(),
votes: Some(FinalityVotes {
votes: vec![(validators[0], 1), (validators[1], 1), (validators[2], 1),]
.into_iter()
@@ -445,7 +448,8 @@ mod tests {
let mut ancestry = Vec::new();
let mut parent_hash = ctx.genesis.compute_hash();
for i in 1..10 {
let header = HeaderBuilder::with_parent_hash(parent_hash).sign_by(&validator((i - 1) / 3));
let header =
HeaderBuilder::with_parent_hash(parent_hash).sign_by(&validator((i - 1) / 3));
let id = header.compute_id();
insert_header(&mut storage, header.clone());
hashes.push(id.hash);
@@ -539,10 +543,7 @@ mod tests {
fn prepare_votes_fails_when_finalized_sibling_is_in_ancestry() {
assert_eq!(
prepare_votes::<()>(
CachedFinalityVotes {
stopped_at_finalized_sibling: true,
..Default::default()
},
CachedFinalityVotes { stopped_at_finalized_sibling: true, ..Default::default() },
Default::default(),
&validators_addresses(3).iter().collect(),
Default::default(),
+44 -52
View File
@@ -14,11 +14,13 @@
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
use crate::error::Error;
use crate::finality::finalize_blocks;
use crate::validators::{Validators, ValidatorsConfiguration};
use crate::verification::{is_importable_header, verify_aura_header};
use crate::{AuraConfiguration, ChainTime, ChangeToEnact, PruningStrategy, Storage};
use crate::{
error::Error,
finality::finalize_blocks,
validators::{Validators, ValidatorsConfiguration},
verification::{is_importable_header, verify_aura_header},
AuraConfiguration, ChainTime, ChangeToEnact, PruningStrategy, Storage,
};
use bp_eth_poa::{AuraHeader, HeaderId, Receipt};
use sp_std::{collections::btree_map::BTreeMap, prelude::*};
@@ -65,7 +67,7 @@ pub fn import_headers<S: Storage, PS: PruningStrategy, CT: ChainTime>(
}
}
useful += 1;
}
},
Err(Error::AncientHeader) | Err(Error::KnownHeader) => useless += 1,
Err(error) => return Err(error),
}
@@ -103,7 +105,8 @@ pub fn import_header<S: Storage, PS: PruningStrategy, CT: ChainTime>(
// check if block schedules new validators
let validators = Validators::new(validators_config);
let (scheduled_change, enacted_change) = validators.extract_validators_change(&header, receipts)?;
let (scheduled_change, enacted_change) =
validators.extract_validators_change(&header, receipts)?;
// check if block finalizes some other blocks and corresponding scheduled validators
let validators_set = import_context.validators_set();
@@ -117,11 +120,10 @@ pub fn import_header<S: Storage, PS: PruningStrategy, CT: ChainTime>(
aura_config.two_thirds_majority_transition,
)?;
let enacted_change = enacted_change
.map(|validators| ChangeToEnact {
signal_block: None,
validators,
})
.or_else(|| validators.finalize_validators_change(storage, &finalized_blocks.finalized_headers));
.map(|validators| ChangeToEnact { signal_block: None, validators })
.or_else(|| {
validators.finalize_validators_change(storage, &finalized_blocks.finalized_headers)
});
// NOTE: we can't return Err() from anywhere below this line
// (because otherwise we'll have inconsistent storage if transaction will fail)
@@ -145,9 +147,7 @@ pub fn import_header<S: Storage, PS: PruningStrategy, CT: ChainTime>(
let new_best_finalized_block_id = finalized_blocks.finalized_headers.last().map(|(id, _)| *id);
let pruning_upper_bound = pruning_strategy.pruning_upper_bound(
new_best_block_id.number,
new_best_finalized_block_id
.map(|id| id.number)
.unwrap_or(finalized_id.number),
new_best_finalized_block_id.map(|id| id.number).unwrap_or(finalized_id.number),
);
// now mark finalized headers && prune old headers
@@ -171,12 +171,15 @@ pub fn header_import_requires_receipts<S: Storage>(
#[cfg(test)]
mod tests {
use super::*;
use crate::mock::{
run_test, secret_to_address, test_aura_config, test_validators_config, validator, validators_addresses,
validators_change_receipt, HeaderBuilder, KeepSomeHeadersBehindBest, TestRuntime, GAS_LIMIT,
use crate::{
mock::{
run_test, secret_to_address, test_aura_config, test_validators_config, validator,
validators_addresses, validators_change_receipt, HeaderBuilder,
KeepSomeHeadersBehindBest, TestRuntime, GAS_LIMIT,
},
validators::ValidatorsSource,
BlocksToPrune, BridgeStorage, Headers, PruningRange,
};
use crate::validators::ValidatorsSource;
use crate::{BlocksToPrune, BridgeStorage, Headers, PruningRange};
use secp256k1::SecretKey;
const TOTAL_VALIDATORS: usize = 3;
@@ -186,10 +189,7 @@ mod tests {
run_test(TOTAL_VALIDATORS, |_| {
let mut storage = BridgeStorage::<TestRuntime>::new();
storage.finalize_and_prune_headers(
Some(HeaderId {
number: 100,
..Default::default()
}),
Some(HeaderId { number: 100, ..Default::default() }),
0,
);
assert_eq!(
@@ -281,8 +281,10 @@ mod tests {
#[test]
fn headers_are_pruned_during_import() {
run_test(TOTAL_VALIDATORS, |ctx| {
let validators_config =
ValidatorsConfiguration::Single(ValidatorsSource::Contract([3; 20].into(), ctx.addresses.clone()));
let validators_config = ValidatorsConfiguration::Single(ValidatorsSource::Contract(
[3; 20].into(),
ctx.addresses.clone(),
));
let validators = vec![validator(0), validator(1), validator(2)];
let mut storage = BridgeStorage::<TestRuntime>::new();
@@ -305,7 +307,8 @@ mod tests {
)
.unwrap();
match i {
2..=10 => assert_eq!(finalized_blocks, vec![(parent_id, Some(100))], "At {}", i,),
2..=10 =>
assert_eq!(finalized_blocks, vec![(parent_id, Some(100))], "At {}", i,),
_ => assert_eq!(finalized_blocks, vec![], "At {}", i),
}
latest_block_id = rolling_last_block_id;
@@ -339,8 +342,8 @@ mod tests {
latest_block_id = rolling_last_block_id;
// and now let's say validators 1 && 2 went offline
// => in the range 12-25 no blocks are finalized, but we still continue to prune old headers
// until header#11 is met. we can't prune #11, because it schedules change
// => in the range 12-25 no blocks are finalized, but we still continue to prune old
// headers until header#11 is met. we can't prune #11, because it schedules change
let mut step = 56u64;
let mut expected_blocks = vec![(header11.compute_id(), Some(101))];
for i in 12..25 {
@@ -366,10 +369,7 @@ mod tests {
}
assert_eq!(
BlocksToPrune::<TestRuntime, ()>::get(),
PruningRange {
oldest_unpruned_block: 11,
oldest_block_to_keep: 14,
},
PruningRange { oldest_unpruned_block: 11, oldest_block_to_keep: 14 },
);
// now let's insert block signed by validator 1
@@ -393,10 +393,7 @@ mod tests {
assert_eq!(finalized_blocks, expected_blocks);
assert_eq!(
BlocksToPrune::<TestRuntime, ()>::get(),
PruningRange {
oldest_unpruned_block: 15,
oldest_block_to_keep: 15,
},
PruningRange { oldest_unpruned_block: 15, oldest_block_to_keep: 15 },
);
});
}
@@ -483,9 +480,7 @@ mod tests {
let header1 = import_custom_block(
&mut storage,
&ctx.validators,
HeaderBuilder::with_parent_number(0)
.step(2)
.sign_by_set(&ctx.validators),
HeaderBuilder::with_parent_number(0).step(2).sign_by_set(&ctx.validators),
)
.unwrap();
assert_eq!(storage.best_block().0, header1);
@@ -495,9 +490,7 @@ mod tests {
let header2 = import_custom_block(
&mut storage,
&ctx.validators,
HeaderBuilder::with_parent_number(1)
.step(3)
.sign_by_set(&ctx.validators),
HeaderBuilder::with_parent_number(1).step(3).sign_by_set(&ctx.validators),
)
.unwrap();
assert_eq!(storage.best_block().0, header2);
@@ -507,9 +500,7 @@ mod tests {
let header3 = import_custom_block(
&mut storage,
&ctx.validators,
HeaderBuilder::with_parent_number(2)
.step(4)
.sign_by_set(&ctx.validators),
HeaderBuilder::with_parent_number(2).step(4).sign_by_set(&ctx.validators),
)
.unwrap();
assert_eq!(storage.best_block().0, header3);
@@ -552,19 +543,19 @@ mod tests {
assert_eq!(storage.best_block().0, header5_1);
assert_eq!(storage.finalized_block(), header1);
// when we import header4 { parent = header3 }, authored by validator[0], header2 is finalized
// when we import header4 { parent = header3 }, authored by validator[0], header2 is
// finalized
let header4 = import_custom_block(
&mut storage,
&ctx.validators,
HeaderBuilder::with_parent_number(3)
.step(5)
.sign_by_set(&ctx.validators),
HeaderBuilder::with_parent_number(3).step(5).sign_by_set(&ctx.validators),
)
.unwrap();
assert_eq!(storage.best_block().0, header5_1);
assert_eq!(storage.finalized_block(), header2);
// when we import header5 { parent = header4 }, authored by validator[1], header3 is finalized
// when we import header5 { parent = header4 }, authored by validator[1], header3 is
// finalized
let header5 = import_custom_block(
&mut storage,
&ctx.validators,
@@ -576,7 +567,8 @@ mod tests {
assert_eq!(storage.best_block().0, header5);
assert_eq!(storage.finalized_block(), header3);
// import of header2'' { parent = header1 } fails, because it has number < best_finalized
// import of header2'' { parent = header1 } fails, because it has number <
// best_finalized
assert_eq!(
import_custom_block(
&mut storage,
+94 -97
View File
@@ -19,7 +19,9 @@
#![allow(clippy::large_enum_variant)]
use crate::finality::{CachedFinalityVotes, FinalityVotes};
use bp_eth_poa::{Address, AuraHeader, HeaderId, RawTransaction, RawTransactionReceipt, Receipt, H256, U256};
use bp_eth_poa::{
Address, AuraHeader, HeaderId, RawTransaction, RawTransactionReceipt, Receipt, H256, U256,
};
use codec::{Decode, Encode};
use frame_support::traits::Get;
use sp_runtime::RuntimeDebug;
@@ -222,10 +224,7 @@ impl<Submitter> ImportContext<Submitter> {
/// This may point to parent if parent has signaled change.
pub fn last_signal_block(&self) -> Option<HeaderId> {
match self.parent_scheduled_change {
Some(_) => Some(HeaderId {
number: self.parent_header.number,
hash: self.parent_hash,
}),
Some(_) => Some(HeaderId { number: self.parent_header.number, hash: self.parent_hash }),
None => self.last_signal_block,
}
}
@@ -313,8 +312,8 @@ pub trait PruningStrategy: Default {
/// number greater than or equal to N even if strategy allows that.
///
/// If your strategy allows pruning unfinalized blocks, this could lead to switch
/// between finalized forks (only if authorities are misbehaving). But since 50 percent plus one (or 2/3)
/// authorities are able to do whatever they want with the chain, this isn't considered
/// between finalized forks (only if authorities are misbehaving). But since 50 percent plus one
/// (or 2/3) authorities are able to do whatever they want with the chain, this isn't considered
/// fatal. If your strategy only prunes finalized blocks, we'll never be able to finalize
/// header that isn't descendant of current best finalized block.
fn pruning_upper_bound(&mut self, best_number: u64, best_finalized_number: u64) -> u64;
@@ -343,10 +342,10 @@ impl ChainTime for () {
pub trait OnHeadersSubmitted<AccountId> {
/// Called when valid headers have been submitted.
///
/// The submitter **must not** be rewarded for submitting valid headers, because greedy authority
/// could produce and submit multiple valid headers (without relaying them to other peers) and
/// get rewarded. Instead, the provider could track submitters and stop rewarding if too many
/// headers have been submitted without finalization.
/// The submitter **must not** be rewarded for submitting valid headers, because greedy
/// authority could produce and submit multiple valid headers (without relaying them to other
/// peers) and get rewarded. Instead, the provider could track submitters and stop rewarding if
/// too many headers have been submitted without finalization.
fn on_valid_headers_submitted(submitter: AccountId, useful: u64, useless: u64);
/// Called when invalid headers have been submitted.
fn on_invalid_headers_submitted(submitter: AccountId);
@@ -459,13 +458,14 @@ pub mod pallet {
// now track/penalize current submitter for providing new headers
match import_result {
Ok((useful, useless)) => T::OnHeadersSubmitted::on_valid_headers_submitted(submitter, useful, useless),
Ok((useful, useless)) =>
T::OnHeadersSubmitted::on_valid_headers_submitted(submitter, useful, useless),
Err(error) => {
// even though we may have accept some headers, we do not want to reward someone
// who provides invalid headers
T::OnHeadersSubmitted::on_invalid_headers_submitted(submitter);
return Err(error.msg().into());
}
return Err(error.msg().into())
},
}
Ok(())
@@ -500,12 +500,13 @@ pub mod pallet {
// UnsignedTooFarInTheFuture is the special error code used to limit
// number of transactions in the pool - we do not want to ban transaction
// in this case (see verification.rs for details)
Err(error::Error::UnsignedTooFarInTheFuture) => {
UnknownTransaction::Custom(error::Error::UnsignedTooFarInTheFuture.code()).into()
}
Err(error::Error::UnsignedTooFarInTheFuture) => UnknownTransaction::Custom(
error::Error::UnsignedTooFarInTheFuture.code(),
)
.into(),
Err(error) => InvalidTransaction::Custom(error.code()).into(),
}
}
},
_ => InvalidTransaction::Call.into(),
}
}
@@ -513,23 +514,28 @@ pub mod pallet {
/// Best known block.
#[pallet::storage]
pub(super) type BestBlock<T: Config<I>, I: 'static = ()> = StorageValue<_, (HeaderId, U256), ValueQuery>;
pub(super) type BestBlock<T: Config<I>, I: 'static = ()> =
StorageValue<_, (HeaderId, U256), ValueQuery>;
/// Best finalized block.
#[pallet::storage]
pub(super) type FinalizedBlock<T: Config<I>, I: 'static = ()> = StorageValue<_, HeaderId, ValueQuery>;
pub(super) type FinalizedBlock<T: Config<I>, I: 'static = ()> =
StorageValue<_, HeaderId, ValueQuery>;
/// Range of blocks that we want to prune.
#[pallet::storage]
pub(super) type BlocksToPrune<T: Config<I>, I: 'static = ()> = StorageValue<_, PruningRange, ValueQuery>;
pub(super) type BlocksToPrune<T: Config<I>, I: 'static = ()> =
StorageValue<_, PruningRange, ValueQuery>;
/// Map of imported headers by hash.
#[pallet::storage]
pub(super) type Headers<T: Config<I>, I: 'static = ()> = StorageMap<_, Identity, H256, StoredHeader<T::AccountId>>;
pub(super) type Headers<T: Config<I>, I: 'static = ()> =
StorageMap<_, Identity, H256, StoredHeader<T::AccountId>>;
/// Map of imported header hashes by number.
#[pallet::storage]
pub(super) type HeadersByNumber<T: Config<I>, I: 'static = ()> = StorageMap<_, Blake2_128Concat, u64, Vec<H256>>;
pub(super) type HeadersByNumber<T: Config<I>, I: 'static = ()> =
StorageMap<_, Blake2_128Concat, u64, Vec<H256>>;
/// Map of cached finality data by header hash.
#[pallet::storage]
@@ -538,17 +544,20 @@ pub mod pallet {
/// The ID of next validator set.
#[pallet::storage]
pub(super) type NextValidatorsSetId<T: Config<I>, I: 'static = ()> = StorageValue<_, u64, ValueQuery>;
pub(super) type NextValidatorsSetId<T: Config<I>, I: 'static = ()> =
StorageValue<_, u64, ValueQuery>;
/// Map of validators sets by their id.
#[pallet::storage]
pub(super) type ValidatorsSets<T: Config<I>, I: 'static = ()> = StorageMap<_, Twox64Concat, u64, ValidatorsSet>;
pub(super) type ValidatorsSets<T: Config<I>, I: 'static = ()> =
StorageMap<_, Twox64Concat, u64, ValidatorsSet>;
/// Validators sets reference count. Each header that is authored by this set increases
/// the reference count. When we prune this header, we decrease the reference count.
/// When it reaches zero, we are free to prune validator set as well.
#[pallet::storage]
pub(super) type ValidatorsSetsRc<T: Config<I>, I: 'static = ()> = StorageMap<_, Twox64Concat, u64, u64>;
pub(super) type ValidatorsSetsRc<T: Config<I>, I: 'static = ()> =
StorageMap<_, Twox64Concat, u64, u64>;
/// Map of validators set changes scheduled by given header.
#[pallet::storage]
@@ -572,14 +581,16 @@ pub mod pallet {
// the initial blocks should be selected so that:
// 1) it doesn't signal validators changes;
// 2) there are no scheduled validators changes from previous blocks;
// 3) (implied) all direct children of initial block are authored by the same validators set.
// 3) (implied) all direct children of initial block are authored by the same validators
// set.
assert!(
!self.initial_validators.is_empty(),
"Initial validators set can't be empty",
assert!(!self.initial_validators.is_empty(), "Initial validators set can't be empty",);
initialize_storage::<T, I>(
&self.initial_header,
self.initial_difficulty,
&self.initial_validators,
);
initialize_storage::<T, I>(&self.initial_header, self.initial_difficulty, &self.initial_validators);
}
}
}
@@ -648,7 +659,7 @@ impl<T: Config<I>, I: 'static> BridgeStorage<T, I> {
for number in begin..end {
// if we can't prune anything => break
if max_blocks_to_prune == 0 {
break;
break
}
// read hashes of blocks with given number and try to prune these blocks
@@ -664,7 +675,7 @@ impl<T: Config<I>, I: 'static> BridgeStorage<T, I> {
// if we haven't pruned all blocks, remember unpruned
if !blocks_at_number.is_empty() {
HeadersByNumber::<T, I>::insert(number, blocks_at_number);
break;
break
}
}
@@ -692,8 +703,10 @@ impl<T: Config<I>, I: 'static> BridgeStorage<T, I> {
blocks_at_number: &mut Vec<H256>,
) {
// ensure that unfinalized headers we want to prune do not have scheduled changes
if number > finalized_number && blocks_at_number.iter().any(ScheduledChanges::<T, I>::contains_key) {
return;
if number > finalized_number &&
blocks_at_number.iter().any(ScheduledChanges::<T, I>::contains_key)
{
return
}
// physically remove headers and (probably) obsolete validators sets
@@ -718,7 +731,7 @@ impl<T: Config<I>, I: 'static> BridgeStorage<T, I> {
// check if we have already pruned too much headers in this call
*max_blocks_to_prune -= 1;
if *max_blocks_to_prune == 0 {
return;
return
}
}
}
@@ -749,21 +762,22 @@ impl<T: Config<I>, I: 'static> Storage for BridgeStorage<T, I> {
let mut current_id = *parent;
loop {
// if we have reached finalized block's sibling => stop with special signal
if current_id.number == best_finalized.number && current_id.hash != best_finalized.hash {
if current_id.number == best_finalized.number && current_id.hash != best_finalized.hash
{
votes.stopped_at_finalized_sibling = true;
return votes;
return votes
}
// if we have reached target header => stop
if stop_at(&current_id.hash) {
return votes;
return votes
}
// if we have found cached votes => stop
let cached_votes = FinalityCache::<T, I>::get(&current_id.hash);
if let Some(cached_votes) = cached_votes {
votes.votes = Some(cached_votes);
return votes;
return votes
}
// read next parent header id
@@ -792,7 +806,9 @@ impl<T: Config<I>, I: 'static> Storage for BridgeStorage<T, I> {
) -> Option<ImportContext<Self::Submitter>> {
Headers::<T, I>::get(parent_hash).map(|parent_header| {
let validators_set = ValidatorsSets::<T, I>::get(parent_header.next_validators_set_id)
.expect("validators set is only pruned when last ref is pruned; there is a ref; qed");
.expect(
"validators set is only pruned when last ref is pruned; there is a ref; qed",
);
let parent_scheduled_change = ScheduledChanges::<T, I>::get(parent_hash);
ImportContext {
submitter,
@@ -841,19 +857,20 @@ impl<T: Config<I>, I: 'static> Storage for BridgeStorage<T, I> {
);
ValidatorsSetsRc::<T, I>::insert(next_validators_set_id, 1);
next_validators_set_id
}
},
None => {
ValidatorsSetsRc::<T, I>::mutate(header.context.validators_set_id, |rc| {
*rc = Some(rc.map(|rc| rc + 1).unwrap_or(1));
*rc
});
header.context.validators_set_id
}
},
};
let finality_votes_caching_interval = T::FinalityVotesCachingInterval::get();
if let Some(finality_votes_caching_interval) = finality_votes_caching_interval {
let cache_entry_required = header.id.number != 0 && header.id.number % finality_votes_caching_interval == 0;
let cache_entry_required =
header.id.number != 0 && header.id.number % finality_votes_caching_interval == 0;
if cache_entry_required {
FinalityCache::<T, I>::insert(header.id.hash, header.finality_votes);
}
@@ -917,10 +934,7 @@ pub(crate) fn initialize_storage<T: Config<I>, I: 'static>(
initial_hash,
);
let initial_id = HeaderId {
number: initial_header.number,
hash: initial_hash,
};
let initial_id = HeaderId { number: initial_header.number, hash: initial_hash };
BestBlock::<T, I>::put((initial_id, initial_difficulty));
FinalizedBlock::<T, I>::put(initial_id);
BlocksToPrune::<T, I>::put(PruningRange {
@@ -965,7 +979,7 @@ pub fn verify_transaction_finalized<S: Storage>(
proof.len(),
);
return false;
return false
}
let header = match storage.header(&block) {
@@ -977,8 +991,8 @@ pub fn verify_transaction_finalized<S: Storage>(
block,
);
return false;
}
return false
},
};
let finalized = storage.finalized_block();
@@ -992,7 +1006,7 @@ pub fn verify_transaction_finalized<S: Storage>(
finalized.number,
);
return false;
return false
}
// check if header is actually finalized
@@ -1010,7 +1024,7 @@ pub fn verify_transaction_finalized<S: Storage>(
finalized.hash,
);
return false;
return false
}
// verify that transaction is included in the block
@@ -1022,7 +1036,7 @@ pub fn verify_transaction_finalized<S: Storage>(
computed_root,
);
return false;
return false
}
// verify that transaction receipt is included in the block
@@ -1034,7 +1048,7 @@ pub fn verify_transaction_finalized<S: Storage>(
computed_root,
);
return false;
return false
}
// check that transaction has completed successfully
@@ -1048,7 +1062,7 @@ pub fn verify_transaction_finalized<S: Storage>(
);
false
}
},
Err(err) => {
log::trace!(
target: "runtime",
@@ -1057,23 +1071,24 @@ pub fn verify_transaction_finalized<S: Storage>(
);
false
}
},
}
}
/// Transaction pool configuration.
fn pool_configuration() -> PoolConfiguration {
PoolConfiguration {
max_future_number_difference: 10,
}
PoolConfiguration { max_future_number_difference: 10 }
}
/// Return iterator of given header ancestors.
fn ancestry<S: Storage>(storage: &'_ S, mut parent_hash: H256) -> impl Iterator<Item = (H256, AuraHeader)> + '_ {
fn ancestry<S: Storage>(
storage: &'_ S,
mut parent_hash: H256,
) -> impl Iterator<Item = (H256, AuraHeader)> + '_ {
sp_std::iter::from_fn(move || {
let (header, _) = storage.header(&parent_hash)?;
if header.number == 0 {
return None;
return None
}
let hash = parent_hash;
@@ -1085,12 +1100,14 @@ fn ancestry<S: Storage>(storage: &'_ S, mut parent_hash: H256) -> impl Iterator<
#[cfg(test)]
pub(crate) mod tests {
use super::*;
use crate::finality::FinalityAncestor;
use crate::mock::{
genesis, insert_header, run_test, run_test_with_genesis, validators_addresses, HeaderBuilder, TestRuntime,
GAS_LIMIT,
use crate::{
finality::FinalityAncestor,
mock::{
genesis, insert_header, run_test, run_test_with_genesis, validators_addresses,
HeaderBuilder, TestRuntime, GAS_LIMIT,
},
test_utils::validator_utils::*,
};
use crate::test_utils::validator_utils::*;
use bp_eth_poa::compute_merkle_root;
const TOTAL_VALIDATORS: usize = 3;
@@ -1182,10 +1199,7 @@ pub(crate) mod tests {
assert_eq!(HeadersByNumber::<TestRuntime, ()>::get(&5).unwrap().len(), 5);
assert_eq!(
BlocksToPrune::<TestRuntime, ()>::get(),
PruningRange {
oldest_unpruned_block: 5,
oldest_block_to_keep: 5,
},
PruningRange { oldest_unpruned_block: 5, oldest_block_to_keep: 5 },
);
});
}
@@ -1202,10 +1216,7 @@ pub(crate) mod tests {
storage.prune_blocks(0xFFFF, 10, 3);
assert_eq!(
BlocksToPrune::<TestRuntime, ()>::get(),
PruningRange {
oldest_unpruned_block: 5,
oldest_block_to_keep: 5,
},
PruningRange { oldest_unpruned_block: 5, oldest_block_to_keep: 5 },
);
});
}
@@ -1221,10 +1232,7 @@ pub(crate) mod tests {
assert!(HeadersByNumber::<TestRuntime, ()>::get(&3).is_some());
assert_eq!(
BlocksToPrune::<TestRuntime, ()>::get(),
PruningRange {
oldest_unpruned_block: 0,
oldest_block_to_keep: 10,
},
PruningRange { oldest_unpruned_block: 0, oldest_block_to_keep: 10 },
);
});
}
@@ -1242,10 +1250,7 @@ pub(crate) mod tests {
assert_eq!(HeadersByNumber::<TestRuntime, ()>::get(&2).unwrap().len(), 4);
assert_eq!(
BlocksToPrune::<TestRuntime, ()>::get(),
PruningRange {
oldest_unpruned_block: 2,
oldest_block_to_keep: 10,
},
PruningRange { oldest_unpruned_block: 2, oldest_block_to_keep: 10 },
);
// try to prune blocks [2; 10)
@@ -1258,10 +1263,7 @@ pub(crate) mod tests {
assert_eq!(HeadersByNumber::<TestRuntime, ()>::get(&4).unwrap().len(), 3);
assert_eq!(
BlocksToPrune::<TestRuntime, ()>::get(),
PruningRange {
oldest_unpruned_block: 4,
oldest_block_to_keep: 10,
},
PruningRange { oldest_unpruned_block: 4, oldest_block_to_keep: 10 },
);
});
}
@@ -1284,10 +1286,7 @@ pub(crate) mod tests {
assert_eq!(HeadersByNumber::<TestRuntime, ()>::get(&7).unwrap().len(), 5);
assert_eq!(
BlocksToPrune::<TestRuntime, ()>::get(),
PruningRange {
oldest_unpruned_block: 7,
oldest_block_to_keep: 10,
},
PruningRange { oldest_unpruned_block: 7, oldest_block_to_keep: 10 },
);
});
}
@@ -1307,7 +1306,8 @@ pub(crate) mod tests {
}
// for header with number = interval, cache entry is created
let header_with_entry = HeaderBuilder::with_parent_number(interval - 1).sign_by_set(&ctx.validators);
let header_with_entry =
HeaderBuilder::with_parent_number(interval - 1).sign_by_set(&ctx.validators);
let header_with_entry_hash = header_with_entry.compute_hash();
insert_header(&mut storage, header_with_entry);
assert!(FinalityCache::<TestRuntime>::get(&header_with_entry_hash).is_some());
@@ -1354,10 +1354,7 @@ pub(crate) mod tests {
let votes_at_3 = FinalityVotes {
votes: vec![([42; 20].into(), 21)].into_iter().collect(),
ancestry: vec![FinalityAncestor {
id: HeaderId {
number: 100,
hash: Default::default(),
},
id: HeaderId { number: 100, hash: Default::default() },
..Default::default()
}]
.into_iter()
+8 -11
View File
@@ -17,11 +17,15 @@
// From construct_runtime macro
#![allow(clippy::from_over_into)]
pub use crate::test_utils::{insert_header, validator_utils::*, validators_change_receipt, HeaderBuilder, GAS_LIMIT};
pub use crate::test_utils::{
insert_header, validator_utils::*, validators_change_receipt, HeaderBuilder, GAS_LIMIT,
};
pub use bp_eth_poa::signatures::secret_to_address;
use crate::validators::{ValidatorsConfiguration, ValidatorsSource};
use crate::{AuraConfiguration, ChainTime, Config, GenesisConfig as CrateGenesisConfig, PruningStrategy};
use crate::{
validators::{ValidatorsConfiguration, ValidatorsSource},
AuraConfiguration, ChainTime, Config, GenesisConfig as CrateGenesisConfig, PruningStrategy,
};
use bp_eth_poa::{Address, AuraHeader, H256, U256};
use frame_support::{parameter_types, traits::GenesisBuild, weights::Weight};
use secp256k1::SecretKey;
@@ -154,14 +158,7 @@ pub fn run_test_with_genesis<T>(
})
.unwrap(),
)
.execute_with(|| {
test(TestContext {
genesis,
total_validators,
validators,
addresses,
})
})
.execute_with(|| test(TestContext { genesis, total_validators, validators, addresses }))
}
/// Pruning strategy that keeps 10 headers behind best block.
+16 -13
View File
@@ -24,10 +24,10 @@
// Since this is test code it's fine that not everything is used
#![allow(dead_code)]
use crate::finality::FinalityVotes;
use crate::validators::CHANGE_EVENT_HASH;
use crate::verification::calculate_score;
use crate::{Config, HeaderToImport, Storage};
use crate::{
finality::FinalityVotes, validators::CHANGE_EVENT_HASH, verification::calculate_score, Config,
HeaderToImport, Storage,
};
use bp_eth_poa::{
rlp_encode,
@@ -130,10 +130,7 @@ impl HeaderBuilder {
let sealed_empty_steps = empty_steps
.iter()
.map(|(author, step)| {
let mut empty_step = SealedEmptyStep {
step: *step,
signature: Default::default(),
};
let mut empty_step = SealedEmptyStep { step: *step, signature: Default::default() };
let message = empty_step.message(&self.header.parent_hash);
let signature: [u8; 65] = sign(author, message).into();
empty_step.signature = signature.into();
@@ -216,7 +213,11 @@ pub fn build_genesis_header(author: &SecretKey) -> AuraHeader {
}
/// Helper function for building a custom child header which has been signed by an authority.
pub fn build_custom_header<F>(author: &SecretKey, previous: &AuraHeader, customize_header: F) -> AuraHeader
pub fn build_custom_header<F>(
author: &SecretKey,
previous: &AuraHeader,
customize_header: F,
) -> AuraHeader
where
F: FnOnce(AuraHeader) -> AuraHeader,
{
@@ -232,7 +233,8 @@ pub fn insert_header<S: Storage>(storage: &mut S, header: AuraHeader) {
let id = header.compute_id();
let best_finalized = storage.finalized_block();
let import_context = storage.import_context(None, &header.parent_hash).unwrap();
let parent_finality_votes = storage.cached_finality_votes(&header.parent_id().unwrap(), &best_finalized, |_| false);
let parent_finality_votes =
storage.cached_finality_votes(&header.parent_id().unwrap(), &best_finalized, |_| false);
let finality_votes = crate::finality::prepare_votes(
parent_finality_votes,
best_finalized,
@@ -284,9 +286,10 @@ pub fn validators_change_receipt(parent_hash: H256) -> Receipt {
address: [3; 20].into(),
topics: vec![CHANGE_EVENT_HASH.into(), parent_hash],
data: vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
],
}],
}
+45 -62
View File
@@ -14,15 +14,14 @@
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
use crate::error::Error;
use crate::{ChangeToEnact, Storage};
use crate::{error::Error, ChangeToEnact, Storage};
use bp_eth_poa::{Address, AuraHeader, HeaderId, LogEntry, Receipt, U256};
use sp_std::prelude::*;
/// The hash of InitiateChange event of the validators set contract.
pub(crate) const CHANGE_EVENT_HASH: &[u8; 32] = &[
0x55, 0x25, 0x2f, 0xa6, 0xee, 0xe4, 0x74, 0x1b, 0x4e, 0x24, 0xa7, 0x4a, 0x70, 0xe9, 0xc1, 0x1f, 0xd2, 0xc2, 0x28,
0x1d, 0xf8, 0xd6, 0xea, 0x13, 0x12, 0x6f, 0xf8, 0x45, 0xf7, 0x82, 0x5c, 0x89,
0x55, 0x25, 0x2f, 0xa6, 0xee, 0xe4, 0x74, 0x1b, 0x4e, 0x24, 0xa7, 0x4a, 0x70, 0xe9, 0xc1, 0x1f,
0xd2, 0xc2, 0x28, 0x1d, 0xf8, 0xd6, 0xea, 0x13, 0x12, 0x6f, 0xf8, 0x45, 0xf7, 0x82, 0x5c, 0x89,
];
/// Where source of validators addresses come from. This covers the chain lifetime.
@@ -104,7 +103,8 @@ impl<'a> Validators<'a> {
if next_starts_at == header.number {
match *next_source {
ValidatorsSource::List(ref new_list) => return Ok((None, Some(new_list.clone()))),
ValidatorsSource::Contract(_, ref new_list) => return Ok((Some(new_list.clone()), None)),
ValidatorsSource::Contract(_, ref new_list) =>
return Ok((Some(new_list.clone()), None)),
}
}
@@ -128,12 +128,12 @@ impl<'a> Validators<'a> {
.bloom();
if !header.log_bloom.contains(&expected_bloom) {
return Ok((None, None));
return Ok((None, None))
}
let receipts = receipts.ok_or(Error::MissingTransactionsReceipts)?;
if header.check_receipts_root(&receipts).is_err() {
return Err(Error::TransactionsReceiptsMismatch);
return Err(Error::TransactionsReceiptsMismatch)
}
// iterate in reverse because only the _last_ change in a given
@@ -145,24 +145,24 @@ impl<'a> Validators<'a> {
.filter(|r| r.log_bloom.contains(&expected_bloom))
.flat_map(|r| r.logs.iter())
.filter(|l| {
l.address == *contract_address
&& l.topics.len() == 2 && l.topics[0].as_fixed_bytes() == CHANGE_EVENT_HASH
&& l.topics[1] == header.parent_hash
l.address == *contract_address &&
l.topics.len() == 2 && l.topics[0].as_fixed_bytes() == CHANGE_EVENT_HASH &&
l.topics[1] == header.parent_hash
})
.filter_map(|l| {
let data_len = l.data.len();
if data_len < 64 {
return None;
return None
}
let new_validators_len_u256 = U256::from_big_endian(&l.data[32..64]);
let new_validators_len = new_validators_len_u256.low_u64();
if new_validators_len_u256 != new_validators_len.into() {
return None;
return None
}
if (data_len - 64) as u64 != new_validators_len.saturating_mul(32) {
return None;
return None
}
Some(
@@ -216,12 +216,10 @@ impl<'a> Validators<'a> {
}
})
.and_then(|signal_block| {
storage
.scheduled_change(&signal_block.hash)
.map(|change| ChangeToEnact {
signal_block: Some(signal_block),
validators: change.validators,
})
storage.scheduled_change(&signal_block.hash).map(|change| ChangeToEnact {
signal_block: Some(signal_block),
validators: change.validators,
})
})
}
@@ -243,7 +241,11 @@ impl<'a> Validators<'a> {
}
/// Returns source of validators that should author the next header.
fn source_at_next_header(&self, header_source_index: usize, header_number: u64) -> (u64, &ValidatorsSource) {
fn source_at_next_header(
&self,
header_source_index: usize,
header_number: u64,
) -> (u64, &ValidatorsSource) {
match self.config {
ValidatorsConfiguration::Single(ref source) => (0, source),
ValidatorsConfiguration::Multi(ref sources) => {
@@ -251,13 +253,13 @@ impl<'a> Validators<'a> {
if next_source_index < sources.len() {
let next_source = &sources[next_source_index];
if next_source.0 < header_number + 1 {
return (next_source.0, &next_source.1);
return (next_source.0, &next_source.1)
}
}
let source = &sources[header_source_index];
(source.0, &source.1)
}
},
}
}
}
@@ -275,8 +277,10 @@ impl ValidatorsSource {
#[cfg(test)]
pub(crate) mod tests {
use super::*;
use crate::mock::{run_test, validators_addresses, validators_change_receipt, TestRuntime};
use crate::{AuraScheduledChange, BridgeStorage, Headers, ScheduledChanges, StoredHeader};
use crate::{
mock::{run_test, validators_addresses, validators_change_receipt, TestRuntime},
AuraScheduledChange, BridgeStorage, Headers, ScheduledChanges, StoredHeader,
};
use bp_eth_poa::compute_merkle_root;
const TOTAL_VALIDATORS: usize = 3;
@@ -290,10 +294,7 @@ pub(crate) mod tests {
]);
let validators = Validators::new(&config);
assert_eq!(
validators.source_at(99),
(0, 0, &ValidatorsSource::List(vec![[1; 20].into()])),
);
assert_eq!(validators.source_at(99), (0, 0, &ValidatorsSource::List(vec![[1; 20].into()])),);
assert_eq!(
validators.source_at_next_header(0, 99),
(0, &ValidatorsSource::List(vec![[1; 20].into()])),
@@ -321,12 +322,12 @@ pub(crate) mod tests {
#[test]
fn maybe_signals_validators_change_works() {
// when contract is active, but bloom has no required bits set
let config = ValidatorsConfiguration::Single(ValidatorsSource::Contract(Default::default(), Vec::new()));
let config = ValidatorsConfiguration::Single(ValidatorsSource::Contract(
Default::default(),
Vec::new(),
));
let validators = Validators::new(&config);
let mut header = AuraHeader {
number: u64::max_value(),
..Default::default()
};
let mut header = AuraHeader { number: u64::max_value(), ..Default::default() };
assert!(!validators.maybe_signals_validators_change(&header));
// when contract is active and bloom has required bits set
@@ -347,10 +348,7 @@ pub(crate) mod tests {
(200, ValidatorsSource::Contract([3; 20].into(), vec![[3; 20].into()])),
]);
let validators = Validators::new(&config);
let mut header = AuraHeader {
number: 100,
..Default::default()
};
let mut header = AuraHeader { number: 100, ..Default::default() };
// when we're at the block that switches to list source
assert_eq!(
@@ -406,26 +404,20 @@ pub(crate) mod tests {
fn try_finalize_with_scheduled_change(scheduled_at: Option<HeaderId>) -> Option<ChangeToEnact> {
run_test(TOTAL_VALIDATORS, |_| {
let config = ValidatorsConfiguration::Single(ValidatorsSource::Contract(Default::default(), Vec::new()));
let config = ValidatorsConfiguration::Single(ValidatorsSource::Contract(
Default::default(),
Vec::new(),
));
let validators = Validators::new(&config);
let storage = BridgeStorage::<TestRuntime>::new();
// when we're finailizing blocks 10...100
let id10 = HeaderId {
number: 10,
hash: [10; 32].into(),
};
let id100 = HeaderId {
number: 100,
hash: [100; 32].into(),
};
let id10 = HeaderId { number: 10, hash: [10; 32].into() };
let id100 = HeaderId { number: 100, hash: [100; 32].into() };
let finalized_blocks = vec![(id10, None), (id100, None)];
let header100 = StoredHeader::<u64> {
submitter: None,
header: AuraHeader {
number: 100,
..Default::default()
},
header: AuraHeader { number: 100, ..Default::default() },
total_difficulty: 0.into(),
next_validators_set_id: 0,
last_signal_block: scheduled_at,
@@ -445,16 +437,10 @@ pub(crate) mod tests {
#[test]
fn finalize_validators_change_finalizes_scheduled_change() {
let id50 = HeaderId {
number: 50,
..Default::default()
};
let id50 = HeaderId { number: 50, ..Default::default() };
assert_eq!(
try_finalize_with_scheduled_change(Some(id50)),
Some(ChangeToEnact {
signal_block: Some(id50),
validators: validators_addresses(1),
}),
Some(ChangeToEnact { signal_block: Some(id50), validators: validators_addresses(1) }),
);
}
@@ -465,10 +451,7 @@ pub(crate) mod tests {
#[test]
fn finalize_validators_change_does_not_finalize_changes_when_they_are_outside_of_range() {
let id5 = HeaderId {
number: 5,
..Default::default()
};
let id5 = HeaderId { number: 5, ..Default::default() };
assert_eq!(try_finalize_with_scheduled_change(Some(id5)), None,);
}
}
+123 -95
View File
@@ -14,11 +14,14 @@
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
use crate::error::Error;
use crate::validators::{Validators, ValidatorsConfiguration};
use crate::{AuraConfiguration, AuraScheduledChange, ChainTime, ImportContext, PoolConfiguration, Storage};
use crate::{
error::Error,
validators::{Validators, ValidatorsConfiguration},
AuraConfiguration, AuraScheduledChange, ChainTime, ImportContext, PoolConfiguration, Storage,
};
use bp_eth_poa::{
public_to_address, step_validator, Address, AuraHeader, HeaderId, Receipt, SealedEmptyStep, H256, H520, U128, U256,
public_to_address, step_validator, Address, AuraHeader, HeaderId, Receipt, SealedEmptyStep,
H256, H520, U128, U256,
};
use codec::Encode;
use sp_io::crypto::secp256k1_ecdsa_recover;
@@ -28,16 +31,19 @@ use sp_std::{vec, vec::Vec};
/// Pre-check to see if should try and import this header.
/// Returns error if we should not try to import this block.
/// Returns ID of passed header and best finalized header.
pub fn is_importable_header<S: Storage>(storage: &S, header: &AuraHeader) -> Result<(HeaderId, HeaderId), Error> {
pub fn is_importable_header<S: Storage>(
storage: &S,
header: &AuraHeader,
) -> Result<(HeaderId, HeaderId), Error> {
// we never import any header that competes with finalized header
let finalized_id = storage.finalized_block();
if header.number <= finalized_id.number {
return Err(Error::AncientHeader);
return Err(Error::AncientHeader)
}
// we never import any header with known hash
let id = header.compute_id();
if storage.header(&id.hash).is_some() {
return Err(Error::KnownHeader);
return Err(Error::KnownHeader)
}
Ok((id, finalized_id))
@@ -64,7 +70,8 @@ pub fn accept_aura_header_into_pool<S: Storage, CT: ChainTime>(
// we want to avoid having same headers twice in the pool
// => we're strict about receipts here - if we need them, we require receipts to be Some,
// otherwise we require receipts to be None
let receipts_required = Validators::new(validators_config).maybe_signals_validators_change(header);
let receipts_required =
Validators::new(validators_config).maybe_signals_validators_change(header);
match (receipts_required, receipts.is_some()) {
(true, false) => return Err(Error::MissingTransactionsReceipts),
(false, true) => return Err(Error::RedundantTransactionsReceipts),
@@ -78,7 +85,7 @@ pub fn accept_aura_header_into_pool<S: Storage, CT: ChainTime>(
let (best_id, _) = storage.best_block();
let difference = header.number.saturating_sub(best_id.number);
if difference > pool_config.max_future_number_difference {
return Err(Error::UnsignedTooFarInTheFuture);
return Err(Error::UnsignedTooFarInTheFuture)
}
// TODO: only accept new headers when we're at the tip of PoA chain
@@ -104,11 +111,8 @@ pub fn accept_aura_header_into_pool<S: Storage, CT: ChainTime>(
// since our parent is already in the storage, we do not require it
// to be in the transaction pool
(
vec![],
vec![provides_number_and_authority_tag, provides_header_number_and_hash_tag],
)
}
(vec![], vec![provides_number_and_authority_tag, provides_header_number_and_hash_tag])
},
None => {
// we know nothing about parent header
// => the best thing we can do is to believe that there are no forks in
@@ -119,34 +123,37 @@ pub fn accept_aura_header_into_pool<S: Storage, CT: ChainTime>(
"import context is None only when header is missing from the storage;\
best header is always in the storage; qed",
);
let validators_check_result =
validator_checks(config, &best_context.validators_set().validators, header, header_step);
let validators_check_result = validator_checks(
config,
&best_context.validators_set().validators,
header,
header_step,
);
if let Err(error) = validators_check_result {
find_next_validators_signal(storage, &best_context)
.ok_or(error)
.and_then(|next_validators| validator_checks(config, &next_validators, header, header_step))?;
find_next_validators_signal(storage, &best_context).ok_or(error).and_then(
|next_validators| {
validator_checks(config, &next_validators, header, header_step)
},
)?;
}
// since our parent is missing from the storage, we **DO** require it
// to be in the transaction pool
// (- 1 can't underflow because there's always best block in the header)
let requires_header_number_and_hash_tag = HeaderId {
number: header.number - 1,
hash: header.parent_hash,
}
.encode();
let requires_header_number_and_hash_tag =
HeaderId { number: header.number - 1, hash: header.parent_hash }.encode();
(
vec![requires_header_number_and_hash_tag],
vec![provides_number_and_authority_tag, provides_header_number_and_hash_tag],
)
}
},
};
// the heaviest, but rare operation - we do not want invalid receipts in the pool
if let Some(receipts) = receipts {
log::trace!(target: "runtime", "Got receipts! {:?}", receipts);
if header.check_receipts_root(receipts).is_err() {
return Err(Error::TransactionsReceiptsMismatch);
return Err(Error::TransactionsReceiptsMismatch)
}
}
@@ -189,32 +196,32 @@ fn contextless_checks<CT: ChainTime>(
) -> Result<(), Error> {
let expected_seal_fields = expected_header_seal_fields(config, header);
if header.seal.len() != expected_seal_fields {
return Err(Error::InvalidSealArity);
return Err(Error::InvalidSealArity)
}
if header.number >= u64::max_value() {
return Err(Error::RidiculousNumber);
return Err(Error::RidiculousNumber)
}
if header.gas_used > header.gas_limit {
return Err(Error::TooMuchGasUsed);
return Err(Error::TooMuchGasUsed)
}
if header.gas_limit < config.min_gas_limit {
return Err(Error::InvalidGasLimit);
return Err(Error::InvalidGasLimit)
}
if header.gas_limit > config.max_gas_limit {
return Err(Error::InvalidGasLimit);
return Err(Error::InvalidGasLimit)
}
if header.number != 0 && header.extra_data.len() as u64 > config.maximum_extra_data_size {
return Err(Error::ExtraDataOutOfBounds);
return Err(Error::ExtraDataOutOfBounds)
}
// we can't detect if block is from future in runtime
// => let's only do an overflow check
if header.timestamp > i32::max_value() as u64 {
return Err(Error::TimestampOverflow);
return Err(Error::TimestampOverflow)
}
if chain_time.is_timestamp_ahead(header.timestamp) {
return Err(Error::HeaderTimestampIsAhead);
return Err(Error::HeaderTimestampIsAhead)
}
Ok(())
@@ -233,15 +240,16 @@ fn contextual_checks<Submitter>(
// Ensure header is from the step after context.
if header_step == parent_step {
return Err(Error::DoubleVote);
return Err(Error::DoubleVote)
}
#[allow(clippy::suspicious_operation_groupings)]
if header.number >= config.validate_step_transition && header_step < parent_step {
return Err(Error::DoubleVote);
return Err(Error::DoubleVote)
}
// If empty step messages are enabled we will validate the messages in the seal, missing messages are not
// reported as there's no way to tell whether the empty step message was never sent or simply not included.
// If empty step messages are enabled we will validate the messages in the seal, missing
// messages are not reported as there's no way to tell whether the empty step message was never
// sent or simply not included.
let empty_steps_len = match header.number >= config.empty_steps_transition {
true => {
let strict_empty_steps = header.number >= config.strict_empty_steps_transition;
@@ -251,16 +259,16 @@ fn contextual_checks<Submitter>(
for empty_step in empty_steps {
if empty_step.step <= parent_step || empty_step.step >= header_step {
return Err(Error::InsufficientProof);
return Err(Error::InsufficientProof)
}
if !verify_empty_step(&header.parent_hash, &empty_step, validators) {
return Err(Error::InsufficientProof);
return Err(Error::InsufficientProof)
}
if strict_empty_steps {
if empty_step.step <= prev_empty_step {
return Err(Error::InsufficientProof);
return Err(Error::InsufficientProof)
}
prev_empty_step = empty_step.step;
@@ -268,7 +276,7 @@ fn contextual_checks<Submitter>(
}
empty_steps_len
}
},
false => 0,
};
@@ -276,7 +284,7 @@ fn contextual_checks<Submitter>(
if header.number >= config.validate_score_transition {
let expected_difficulty = calculate_score(parent_step, header_step, empty_steps_len as _);
if header.difficulty != expected_difficulty {
return Err(Error::InvalidDifficulty);
return Err(Error::InvalidDifficulty)
}
}
@@ -292,16 +300,17 @@ fn validator_checks(
) -> Result<(), Error> {
let expected_validator = *step_validator(validators, header_step);
if header.author != expected_validator {
return Err(Error::NotValidator);
return Err(Error::NotValidator)
}
let validator_signature = header.signature().ok_or(Error::MissingSignature)?;
let header_seal_hash = header
.seal_hash(header.number >= config.empty_steps_transition)
.ok_or(Error::MissingEmptySteps)?;
let is_invalid_proposer = !verify_signature(&expected_validator, &validator_signature, &header_seal_hash);
let is_invalid_proposer =
!verify_signature(&expected_validator, &validator_signature, &header_seal_hash);
if is_invalid_proposer {
return Err(Error::NotValidator);
return Err(Error::NotValidator)
}
Ok(())
@@ -324,8 +333,13 @@ fn verify_empty_step(parent_hash: &H256, step: &SealedEmptyStep, validators: &[A
}
/// Chain scoring: total weight is sqrt(U256::max_value())*height - step
pub(crate) fn calculate_score(parent_step: u64, current_step: u64, current_empty_steps: usize) -> U256 {
U256::from(U128::max_value()) + U256::from(parent_step) - U256::from(current_step) + U256::from(current_empty_steps)
pub(crate) fn calculate_score(
parent_step: u64,
current_step: u64,
current_empty_steps: usize,
) -> U256 {
U256::from(U128::max_value()) + U256::from(parent_step) - U256::from(current_step) +
U256::from(current_empty_steps)
}
/// Verify that the signature over message has been produced by given validator.
@@ -337,7 +351,10 @@ fn verify_signature(expected_validator: &Address, signature: &H520, message: &H2
}
/// Find next unfinalized validators set change after finalized set.
fn find_next_validators_signal<S: Storage>(storage: &S, context: &ImportContext<S::Submitter>) -> Option<Vec<Address>> {
fn find_next_validators_signal<S: Storage>(
storage: &S,
context: &ImportContext<S::Submitter>,
) -> Option<Vec<Address>> {
// that's the earliest block number we may met in following loop
// it may be None if that's the first set
let best_set_signal_block = context.validators_set().signal_block;
@@ -352,14 +369,15 @@ fn find_next_validators_signal<S: Storage>(storage: &S, context: &ImportContext<
// next_current_block_hash points to the block that schedules next
// change
let current_scheduled_set = match current_set_signal_block {
Some(current_set_signal_block) if Some(&current_set_signal_block) == best_set_signal_block.as_ref() => {
return next_scheduled_set.map(|scheduled_set| scheduled_set.validators)
}
Some(current_set_signal_block)
if Some(&current_set_signal_block) == best_set_signal_block.as_ref() =>
return next_scheduled_set.map(|scheduled_set| scheduled_set.validators),
None => return next_scheduled_set.map(|scheduled_set| scheduled_set.validators),
Some(current_set_signal_block) => storage.scheduled_change(&current_set_signal_block.hash).expect(
"header that is associated with this change is not pruned;\
Some(current_set_signal_block) =>
storage.scheduled_change(&current_set_signal_block.hash).expect(
"header that is associated with this change is not pruned;\
scheduled changes are only removed when header is pruned; qed",
),
),
};
current_set_signal_block = current_scheduled_set.prev_signal_block;
@@ -370,13 +388,15 @@ fn find_next_validators_signal<S: Storage>(storage: &S, context: &ImportContext<
#[cfg(test)]
mod tests {
use super::*;
use crate::mock::{
insert_header, run_test_with_genesis, test_aura_config, validator, validator_address, validators_addresses,
validators_change_receipt, AccountId, ConstChainTime, HeaderBuilder, TestRuntime, GAS_LIMIT,
};
use crate::validators::ValidatorsSource;
use crate::{
pool_configuration, BridgeStorage, FinalizedBlock, Headers, HeadersByNumber, NextValidatorsSetId,
mock::{
insert_header, run_test_with_genesis, test_aura_config, validator, validator_address,
validators_addresses, validators_change_receipt, AccountId, ConstChainTime,
HeaderBuilder, TestRuntime, GAS_LIMIT,
},
pool_configuration,
validators::ValidatorsSource,
BridgeStorage, FinalizedBlock, Headers, HeadersByNumber, NextValidatorsSetId,
ScheduledChanges, ValidatorsSet, ValidatorsSets,
};
use bp_eth_poa::{compute_merkle_root, rlp_encode, TransactionOutcome, H520, U256};
@@ -391,7 +411,10 @@ mod tests {
HeaderBuilder::genesis().step(GENESIS_STEP).sign_by(&validator(0))
}
fn verify_with_config(config: &AuraConfiguration, header: &AuraHeader) -> Result<ImportContext<AccountId>, Error> {
fn verify_with_config(
config: &AuraConfiguration,
header: &AuraHeader,
) -> Result<ImportContext<AccountId>, Error> {
run_test_with_genesis(genesis(), TOTAL_VALIDATORS, |_| {
let storage = BridgeStorage::<TestRuntime>::new();
verify_aura_header(&storage, config, None, header, &ConstChainTime::default())
@@ -418,8 +441,10 @@ mod tests {
FinalizedBlock::<TestRuntime, ()>::put(block2_id);
let validators_config =
ValidatorsConfiguration::Single(ValidatorsSource::Contract(Default::default(), Vec::new()));
let validators_config = ValidatorsConfiguration::Single(ValidatorsSource::Contract(
Default::default(),
Vec::new(),
));
let (header, receipts) = make_header(&validators);
accept_aura_header_into_pool(
&storage,
@@ -433,7 +458,11 @@ mod tests {
})
}
fn change_validators_set_at(number: u64, finalized_set: Vec<Address>, signalled_set: Option<Vec<Address>>) {
fn change_validators_set_at(
number: u64,
finalized_set: Vec<Address>,
signalled_set: Option<Vec<Address>>,
) {
let set_id = NextValidatorsSetId::<TestRuntime, ()>::get();
NextValidatorsSetId::<TestRuntime, ()>::put(set_id + 1);
ValidatorsSets::<TestRuntime, ()>::insert(
@@ -458,10 +487,7 @@ mod tests {
});
ScheduledChanges::<TestRuntime, ()>::insert(
header.header.parent_hash,
AuraScheduledChange {
validators: signalled_set,
prev_signal_block: None,
},
AuraScheduledChange { validators: signalled_set, prev_signal_block: None },
);
}
@@ -520,21 +546,15 @@ mod tests {
config.max_gas_limit = 200.into();
// when limit is lower than expected
let header = HeaderBuilder::with_number(1)
.gas_limit(50.into())
.sign_by(&validator(0));
let header = HeaderBuilder::with_number(1).gas_limit(50.into()).sign_by(&validator(0));
assert_eq!(verify_with_config(&config, &header), Err(Error::InvalidGasLimit));
// when limit is larger than expected
let header = HeaderBuilder::with_number(1)
.gas_limit(250.into())
.sign_by(&validator(0));
let header = HeaderBuilder::with_number(1).gas_limit(250.into()).sign_by(&validator(0));
assert_eq!(verify_with_config(&config, &header), Err(Error::InvalidGasLimit));
// when limit is within expected range
let header = HeaderBuilder::with_number(1)
.gas_limit(150.into())
.sign_by(&validator(0));
let header = HeaderBuilder::with_number(1).gas_limit(150.into()).sign_by(&validator(0));
assert_ne!(verify_with_config(&config, &header), Err(Error::InvalidGasLimit));
}
@@ -573,7 +593,8 @@ mod tests {
// expected import context after verification
let expect = ImportContext::<AccountId> {
submitter: None,
parent_hash: hex!("6e41bff05578fc1db17f6816117969b07d2217f1f9039d8116a82764335991d3").into(),
parent_hash: hex!("6e41bff05578fc1db17f6816117969b07d2217f1f9039d8116a82764335991d3")
.into(),
parent_header: genesis(),
parent_total_difficulty: U256::zero(),
parent_scheduled_change: None,
@@ -587,7 +608,8 @@ mod tests {
signal_block: None,
enact_block: HeaderId {
number: 0,
hash: hex!("6e41bff05578fc1db17f6816117969b07d2217f1f9039d8116a82764335991d3").into(),
hash: hex!("6e41bff05578fc1db17f6816117969b07d2217f1f9039d8116a82764335991d3")
.into(),
},
},
last_signal_block: None,
@@ -729,7 +751,10 @@ mod tests {
fn pool_verifies_known_blocks() {
// when header is known
assert_eq!(
default_accept_into_pool(|validators| (HeaderBuilder::with_parent_number(2).sign_by_set(validators), None)),
default_accept_into_pool(|validators| (
HeaderBuilder::with_parent_number(2).sign_by_set(validators),
None
)),
Err(Error::KnownHeader),
);
}
@@ -785,7 +810,10 @@ mod tests {
fn pool_verifies_future_block_number() {
// when header is too far from the future
assert_eq!(
default_accept_into_pool(|validators| (HeaderBuilder::with_number(100).sign_by_set(validators), None),),
default_accept_into_pool(|validators| (
HeaderBuilder::with_number(100).sign_by_set(validators),
None
),),
Err(Error::UnsignedTooFarInTheFuture),
);
}
@@ -811,7 +839,10 @@ mod tests {
// (even if header will be considered invalid/duplicate later, we can use this signature
// as a proof of malicious action by this validator)
assert_eq!(
default_accept_into_pool(|_| (HeaderBuilder::with_number(8).step(8).sign_by(&validator(1)), None,)),
default_accept_into_pool(|_| (
HeaderBuilder::with_number(8).step(8).sign_by(&validator(1)),
None,
)),
Err(Error::NotValidator),
);
}
@@ -829,10 +860,7 @@ mod tests {
// no tags are required
vec![],
// header provides two tags
vec![
(4u64, validators_addresses(3)[1]).encode(),
(4u64, hash.unwrap()).encode(),
],
vec![(4u64, validators_addresses(3)[1]).encode(), (4u64, hash.unwrap()).encode(),],
)),
);
}
@@ -843,9 +871,8 @@ mod tests {
let mut parent_id = None;
assert_eq!(
default_accept_into_pool(|validators| {
let header = HeaderBuilder::with_number(5)
.step(GENESIS_STEP + 5)
.sign_by_set(validators);
let header =
HeaderBuilder::with_number(5).step(GENESIS_STEP + 5).sign_by_set(validators);
id = Some(header.compute_id());
parent_id = header.parent_id();
(header, None)
@@ -881,7 +908,11 @@ mod tests {
assert_eq!(
default_accept_into_pool(|actual_validators| {
// change finalized set at parent header + signal valid set at parent block
change_validators_set_at(3, validators_addresses(10), Some(validators_addresses(3)));
change_validators_set_at(
3,
validators_addresses(10),
Some(validators_addresses(3)),
);
// header is signed using wrong set
let header = HeaderBuilder::with_number(5)
@@ -933,10 +964,7 @@ mod tests {
// no tags are required
vec![],
// header provides two tags
vec![
(4u64, validators_addresses(3)[1]).encode(),
(4u64, hash.unwrap()).encode(),
],
vec![(4u64, validators_addresses(3)[1]).encode(), (4u64, hash.unwrap()).encode(),],
)),
);
}