Merge commit '392447f5c8f986ded2559a78457f4cd87942f393' into update-bridges-subtree-r/w

This commit is contained in:
antonio-dropulic
2021-12-01 09:46:14 +01:00
321 changed files with 28385 additions and 10466 deletions
+11 -9
View File
@@ -7,9 +7,10 @@ edition = "2018"
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
[dependencies]
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false }
libsecp256k1 = { version = "0.3.4", default-features = false, features = ["hmac"], optional = true }
codec = { package = "parity-scale-codec", version = "2.2.0", default-features = false }
libsecp256k1 = { version = "0.7", default-features = false, features = ["hmac"], optional = true }
log = { version = "0.4.14", default-features = false }
scale-info = { version = "1.0", default-features = false, features = ["derive"] }
serde = { version = "1.0", optional = true }
# Bridge dependencies
@@ -18,15 +19,15 @@ bp-eth-poa = { path = "../../primitives/ethereum-poa", default-features = false
# Substrate Dependencies
frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false, optional = true }
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false }
frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false }
sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false }
sp-std = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false }
frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true }
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
[dev-dependencies]
libsecp256k1 = { version = "0.3.4", features = ["hmac"] }
libsecp256k1 = { version = "0.7", features = ["hmac"] }
hex-literal = "0.3"
[features]
@@ -38,6 +39,7 @@ std = [
"frame-support/std",
"frame-system/std",
"log/std",
"scale-info/std",
"serde",
"sp-io/std",
"sp-runtime/std",
@@ -17,15 +17,15 @@
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};
use frame_benchmarking::benchmarks_instance;
use frame_benchmarking::benchmarks_instance_pallet;
use frame_system::RawOrigin;
benchmarks_instance! {
benchmarks_instance_pallet! {
// Benchmark `import_unsigned_header` extrinsic with the best possible conditions:
// * Parent header is finalized.
// * New header doesn't require receipts.
@@ -46,7 +46,7 @@ benchmarks_instance! {
header
},
);
}: import_unsigned_header(RawOrigin::None, header, None)
}: import_unsigned_header(RawOrigin::None, Box::new(header), None)
verify {
let storage = BridgeStorage::<T, I>::new();
assert_eq!(storage.best_block().0.number, 1);
@@ -91,7 +91,7 @@ benchmarks_instance! {
// Need to make sure that the header we're going to import hasn't been inserted
// into storage already
let header = HeaderBuilder::with_parent(&last_header).sign_by(&last_authority);
}: import_unsigned_header(RawOrigin::None, header, None)
}: import_unsigned_header(RawOrigin::None, Box::new(header), None)
verify {
let storage = BridgeStorage::<T, I>::new();
assert_eq!(storage.best_block().0.number, (num_blocks + 1) as u64);
@@ -132,7 +132,7 @@ benchmarks_instance! {
// Need to make sure that the header we're going to import hasn't been inserted
// into storage already
let header = HeaderBuilder::with_parent(&last_header).sign_by(&last_authority);
}: import_unsigned_header(RawOrigin::None, header, None)
}: import_unsigned_header(RawOrigin::None, Box::new(header), None)
verify {
let storage = BridgeStorage::<T, I>::new();
assert_eq!(storage.best_block().0.number, (num_blocks + 1) as u64);
@@ -153,7 +153,7 @@ benchmarks_instance! {
let validators = validators(num_validators);
// Want to prune eligible blocks between [0, n)
BlocksToPrune::<I>::put(PruningRange {
BlocksToPrune::<T, I>::put(PruningRange {
oldest_unpruned_block: 0,
oldest_block_to_keep: n as u64,
});
@@ -167,13 +167,13 @@ benchmarks_instance! {
}
let header = HeaderBuilder::with_parent(&parent).sign_by_set(&validators);
}: import_unsigned_header(RawOrigin::None, header, None)
}: import_unsigned_header(RawOrigin::None, Box::new(header), None)
verify {
let storage = BridgeStorage::<T, I>::new();
let max_pruned: u64 = (n - 1) as _;
assert_eq!(storage.best_block().0.number, (n + 1) as u64);
assert!(HeadersByNumber::<I>::get(&0).is_none());
assert!(HeadersByNumber::<I>::get(&max_pruned).is_none());
assert!(HeadersByNumber::<T, I>::get(&0).is_none());
assert!(HeadersByNumber::<T, I>::get(&max_pruned).is_none());
}
// The goal of this bench is to import a block which contains a transaction receipt. The receipt
@@ -209,14 +209,14 @@ benchmarks_instance! {
header
},
);
}: import_unsigned_header(RawOrigin::None, header, Some(receipts))
}: import_unsigned_header(RawOrigin::None, Box::new(header), Some(receipts))
verify {
let storage = BridgeStorage::<T, I>::new();
assert_eq!(storage.best_block().0.number, 2);
}
}
fn initialize_bench<T: Config<I>, I: Instance>(num_validators: usize) -> AuraHeader {
fn initialize_bench<T: Config<I>, I: 'static>(num_validators: usize) -> AuraHeader {
// Initialize storage with some initial header
let initial_header = build_genesis_header(&validator(0));
let initial_difficulty = initial_header.difficulty;
@@ -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",
@@ -14,18 +14,20 @@
// 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 scale_info::TypeInfo;
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)]
@@ -36,7 +38,7 @@ pub struct CachedFinalityVotes<Submitter> {
/// best finalized.
pub stopped_at_finalized_sibling: bool,
/// Header ancestors that were read while we have been searching for
/// cached votes entry. Newest header has index 0.
/// cached votes entry. The newest header has index 0.
pub unaccounted_ancestry: VecDeque<(HeaderId, Option<Submitter>, AuraHeader)>,
/// Cached finality votes, if they have been found. The associated
/// header is not included into `unaccounted_ancestry`.
@@ -54,18 +56,18 @@ pub struct FinalityEffects<Submitter> {
}
/// Finality votes for given block.
#[derive(RuntimeDebug, Decode, Encode)]
#[derive(RuntimeDebug, Decode, Encode, TypeInfo)]
#[cfg_attr(test, derive(Clone, PartialEq))]
pub struct FinalityVotes<Submitter> {
/// Number of votes per each validator.
pub votes: BTreeMap<Address, u64>,
/// Ancestry blocks with oldest ancestors at the beginning and newest at the
/// Ancestry blocks with the oldest ancestors at the beginning and newest at the
/// end of the queue.
pub ancestry: VecDeque<FinalityAncestor<Submitter>>,
}
/// Information about block ancestor that is used in computations.
#[derive(RuntimeDebug, Decode, Encode)]
#[derive(RuntimeDebug, Decode, Encode, TypeInfo)]
#[cfg_attr(test, derive(Clone, Default, PartialEq))]
pub struct FinalityAncestor<Submitter> {
/// Bock id.
@@ -116,17 +118,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 +134,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 +150,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 +170,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 +179,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 +200,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 +216,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 +229,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,19 +270,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 frame_support::StorageMap;
use crate::{
mock::{
insert_header, run_test, validator, validators_addresses, HeaderBuilder, TestRuntime,
},
BridgeStorage, FinalityCache, HeaderToImport,
};
const TOTAL_VALIDATORS: usize = 5;
@@ -342,7 +340,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!(
@@ -361,7 +360,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!(
@@ -391,7 +391,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()
@@ -406,9 +408,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()
@@ -446,7 +449,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);
@@ -540,10 +544,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(),
+50 -59
View File
@@ -14,22 +14,24 @@
// 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::*};
/// Imports bunch of headers and updates blocks finality.
/// Imports a bunch of headers and updates blocks finality.
///
/// Transactions receipts must be provided if `header_import_requires_receipts()`
/// has returned true.
/// If successful, returns tuple where first element is the number of useful headers
/// we have imported and the second element is the number of useless headers (duplicate)
/// we have NOT imported.
/// Returns error if fatal error has occured during import. Some valid headers may be
/// Returns error if fatal error has occurred during import. Some valid headers may be
/// imported in this case.
/// TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/415)
#[allow(clippy::too_many_arguments)]
@@ -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,15 +171,16 @@ 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::DefaultInstance;
use crate::{BlocksToPrune, BridgeStorage, Headers, PruningRange};
use frame_support::{StorageMap, StorageValue};
use secp256k1::SecretKey;
use libsecp256k1::SecretKey;
const TOTAL_VALIDATORS: usize = 3;
@@ -188,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!(
@@ -283,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();
@@ -307,7 +307,9 @@ 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;
@@ -341,8 +343,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 {
@@ -367,11 +369,8 @@ mod tests {
step += 3;
}
assert_eq!(
BlocksToPrune::<DefaultInstance>::get(),
PruningRange {
oldest_unpruned_block: 11,
oldest_block_to_keep: 14,
},
BlocksToPrune::<TestRuntime, ()>::get(),
PruningRange { oldest_unpruned_block: 11, oldest_block_to_keep: 14 },
);
// now let's insert block signed by validator 1
@@ -394,11 +393,8 @@ mod tests {
.unwrap();
assert_eq!(finalized_blocks, expected_blocks);
assert_eq!(
BlocksToPrune::<DefaultInstance>::get(),
PruningRange {
oldest_unpruned_block: 15,
oldest_block_to_keep: 15,
},
BlocksToPrune::<TestRuntime, ()>::get(),
PruningRange { oldest_unpruned_block: 15, oldest_block_to_keep: 15 },
);
});
}
@@ -485,9 +481,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);
@@ -497,9 +491,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);
@@ -509,9 +501,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);
@@ -554,19 +544,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,
@@ -578,7 +568,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,
File diff suppressed because it is too large Load Diff
+13 -17
View File
@@ -17,14 +17,18 @@
// 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, weights::Weight};
use secp256k1::SecretKey;
use frame_support::{parameter_types, traits::GenesisBuild, weights::Weight};
use libsecp256k1::SecretKey;
use sp_runtime::{
testing::Header as SubstrateHeader,
traits::{BlakeTwo256, IdentityLookup},
@@ -146,23 +150,15 @@ pub fn run_test_with_genesis<T>(
) -> T {
let validators = validators(total_validators);
let addresses = validators_addresses(total_validators);
sp_io::TestExternalities::new(
CrateGenesisConfig {
sp_io::TestExternalities::from(
GenesisBuild::<TestRuntime>::build_storage(&CrateGenesisConfig {
initial_header: genesis.clone(),
initial_difficulty: 0.into(),
initial_validators: addresses.clone(),
}
.build_storage::<TestRuntime, crate::DefaultInstance>()
})
.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.
@@ -19,22 +19,22 @@
//! Although the name implies that it is used by tests, it shouldn't be be used _directly_ by tests.
//! Instead these utilities should be used by the Mock runtime, which in turn is used by tests.
//!
//! On the other hand, they may be used directly by the bechmarking module.
//! On the other hand, they may be used directly by the benchmark module.
// 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,
signatures::{secret_to_address, sign, SignHeader},
Address, AuraHeader, Bloom, Receipt, SealedEmptyStep, H256, U256,
};
use secp256k1::SecretKey;
use libsecp256k1::SecretKey;
use sp_std::prelude::*;
/// Gas limit valid in test environment.
@@ -63,30 +63,28 @@ impl HeaderBuilder {
/// Creates default header on top of test parent with given hash.
#[cfg(test)]
pub fn with_parent_hash(parent_hash: H256) -> Self {
Self::with_parent_hash_on_runtime::<crate::mock::TestRuntime, crate::DefaultInstance>(parent_hash)
Self::with_parent_hash_on_runtime::<crate::mock::TestRuntime, ()>(parent_hash)
}
/// Creates default header on top of test parent with given number. First parent is selected.
#[cfg(test)]
pub fn with_parent_number(parent_number: u64) -> Self {
Self::with_parent_number_on_runtime::<crate::mock::TestRuntime, crate::DefaultInstance>(parent_number)
Self::with_parent_number_on_runtime::<crate::mock::TestRuntime, ()>(parent_number)
}
/// Creates default header on top of parent with given hash.
pub fn with_parent_hash_on_runtime<T: Config<I>, I: crate::Instance>(parent_hash: H256) -> Self {
pub fn with_parent_hash_on_runtime<T: Config<I>, I: 'static>(parent_hash: H256) -> Self {
use crate::Headers;
use frame_support::StorageMap;
let parent_header = Headers::<T, I>::get(&parent_hash).unwrap().header;
Self::with_parent(&parent_header)
}
/// Creates default header on top of parent with given number. First parent is selected.
pub fn with_parent_number_on_runtime<T: Config<I>, I: crate::Instance>(parent_number: u64) -> Self {
pub fn with_parent_number_on_runtime<T: Config<I>, I: 'static>(parent_number: u64) -> Self {
use crate::HeadersByNumber;
use frame_support::StorageMap;
let parent_hash = HeadersByNumber::<I>::get(parent_number).unwrap()[0];
let parent_hash = HeadersByNumber::<T, I>::get(parent_number).unwrap()[0];
Self::with_parent_hash_on_runtime::<T, I>(parent_hash)
}
@@ -132,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();
@@ -218,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,
{
@@ -234,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,
@@ -286,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,
],
}],
}
@@ -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,13 @@ 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)?;
#[allow(clippy::question_mark)]
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 +146,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(
@@ -188,7 +189,10 @@ impl<'a> Validators<'a> {
finalized_blocks: &[(HeaderId, Option<S::Submitter>)],
) -> Option<ChangeToEnact> {
// if we haven't finalized any blocks, no changes may be finalized
let newest_finalized_id = finalized_blocks.last().map(|(id, _)| id)?;
let newest_finalized_id = match finalized_blocks.last().map(|(id, _)| id) {
Some(last_finalized_id) => last_finalized_id,
None => return None,
};
let oldest_finalized_id = finalized_blocks
.first()
.map(|(id, _)| id)
@@ -213,12 +217,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,
})
})
}
@@ -240,7 +242,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) => {
@@ -248,13 +254,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)
}
},
}
}
}
@@ -272,11 +278,11 @@ impl ValidatorsSource {
#[cfg(test)]
pub(crate) mod tests {
use super::*;
use crate::mock::{run_test, validators_addresses, validators_change_receipt, TestRuntime};
use crate::DefaultInstance;
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;
use frame_support::StorageMap;
const TOTAL_VALIDATORS: usize = 3;
@@ -289,10 +295,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()])),
@@ -320,12 +323,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,
..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
@@ -346,10 +349,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!(
@@ -405,26 +405,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,
@@ -435,7 +429,7 @@ pub(crate) mod tests {
};
Headers::<TestRuntime>::insert(id100.hash, header100);
if let Some(scheduled_at) = scheduled_at {
ScheduledChanges::<DefaultInstance>::insert(scheduled_at.hash, scheduled_change);
ScheduledChanges::<TestRuntime, ()>::insert(scheduled_at.hash, scheduled_change);
}
validators.finalize_validators_change(&storage, &finalized_blocks)
@@ -444,16 +438,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) }),
);
}
@@ -464,10 +452,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()
};
assert_eq!(try_finalize_with_scheduled_change(Some(id5)), None);
let id5 = HeaderId { number: 5, ..Default::default() };
assert_eq!(try_finalize_with_scheduled_change(Some(id5)), None,);
}
}
@@ -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,22 +31,25 @@ 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))
}
/// Try accept unsigned aura header into transaction pool.
/// Try to accept unsigned aura header into transaction pool.
///
/// Returns required and provided tags.
pub fn accept_aura_header_into_pool<S: Storage, CT: ChainTime>(
@@ -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,38 @@ 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);
#[allow(clippy::question_mark)]
if header.check_receipts_root(receipts).is_err() {
return Err(Error::TransactionsReceiptsMismatch);
return Err(Error::TransactionsReceiptsMismatch)
}
}
@@ -189,32 +197,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 {
return Err(Error::RidiculousNumber);
if header.number >= u64::max_value() {
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 as u64 {
return Err(Error::TimestampOverflow);
if header.timestamp > i32::max_value() as u64 {
return Err(Error::TimestampOverflow)
}
if chain_time.is_timestamp_ahead(header.timestamp) {
return Err(Error::HeaderTimestampIsAhead);
return Err(Error::HeaderTimestampIsAhead)
}
Ok(())
@@ -233,15 +241,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 +260,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 +277,7 @@ fn contextual_checks<Submitter>(
}
empty_steps_len
}
},
false => 0,
};
@@ -276,7 +285,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 +301,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(())
@@ -323,9 +333,14 @@ fn verify_empty_step(parent_hash: &H256, step: &SealedEmptyStep, validators: &[A
verify_signature(&expected_validator, &step.signature, &message)
}
/// Chain scoring: total weight is sqrt(U256::MAX)*height - step
pub(crate) fn calculate_score(parent_step: u64, current_step: u64, current_empty_steps: usize) -> U256 {
U256::from(U128::MAX) + U256::from(parent_step) - U256::from(current_step) + U256::from(current_empty_steps)
/// 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)
}
/// Verify that the signature over message has been produced by given validator.
@@ -337,7 +352,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 +370,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,20 +389,20 @@ 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::DefaultInstance;
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};
use frame_support::{StorageMap, StorageValue};
use hex_literal::hex;
use secp256k1::SecretKey;
use libsecp256k1::SecretKey;
use sp_runtime::transaction_validity::TransactionTag;
const GENESIS_STEP: u64 = 42;
@@ -393,7 +412,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,10 +440,12 @@ mod tests {
let block3 = HeaderBuilder::with_parent_number(2).sign_by_set(&validators);
insert_header(&mut storage, block3);
FinalizedBlock::<DefaultInstance>::put(block2_id);
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,
@@ -435,22 +459,26 @@ mod tests {
})
}
fn change_validators_set_at(number: u64, finalized_set: Vec<Address>, signalled_set: Option<Vec<Address>>) {
let set_id = NextValidatorsSetId::<DefaultInstance>::get();
NextValidatorsSetId::<DefaultInstance>::put(set_id + 1);
ValidatorsSets::<DefaultInstance>::insert(
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(
set_id,
ValidatorsSet {
validators: finalized_set,
signal_block: None,
enact_block: HeaderId {
number: 0,
hash: HeadersByNumber::<DefaultInstance>::get(&0).unwrap()[0],
hash: HeadersByNumber::<TestRuntime, ()>::get(&0).unwrap()[0],
},
},
);
let header_hash = HeadersByNumber::<DefaultInstance>::get(&number).unwrap()[0];
let header_hash = HeadersByNumber::<TestRuntime, ()>::get(&number).unwrap()[0];
let mut header = Headers::<TestRuntime>::get(&header_hash).unwrap();
header.next_validators_set_id = set_id;
if let Some(signalled_set) = signalled_set {
@@ -458,12 +486,9 @@ mod tests {
number: header.header.number - 1,
hash: header.header.parent_hash,
});
ScheduledChanges::<DefaultInstance>::insert(
ScheduledChanges::<TestRuntime, ()>::insert(
header.header.parent_hash,
AuraScheduledChange {
validators: signalled_set,
prev_signal_block: None,
},
AuraScheduledChange { validators: signalled_set, prev_signal_block: None },
);
}
@@ -522,21 +547,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));
}
@@ -564,9 +583,8 @@ mod tests {
assert_eq!(default_verify(&header), Err(Error::TimestampOverflow));
// when timestamp doesn't overflow i32
let header = HeaderBuilder::with_number(1)
.timestamp(i32::MAX as u64)
.sign_by(&validator(0));
let header =
HeaderBuilder::with_number(1).timestamp(i32::MAX as u64).sign_by(&validator(0));
assert_ne!(default_verify(&header), Err(Error::TimestampOverflow));
}
@@ -575,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,
@@ -589,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,
@@ -731,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),
);
}
@@ -787,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),
);
}
@@ -813,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),
);
}
@@ -831,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(),],
)),
);
}
@@ -845,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)
@@ -883,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)
@@ -935,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(),],
)),
);
}