mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-08 19:28:01 +00:00
Benchmark Ethereum Pallet (#149)
* Add skeleton for worst case import_unsigned_header * Fix a typo * Add benchmark test for best case unsigned header import * Add finality verification to worst case bench * Move `insert_header()` from mock to test_utils Allows the benchmarking code to use this without having to pull it in from the mock. * Add a rough bench to test a finalizing a "long" chain * Try to use complexity parameter for finality bench * Improve long finality bench * Remove stray dot file * Remove old "worst" case bench * Scribble some ideas down for pruning bench * Prune headers during benchmarking * Clean up some comments * Make finality bench work for entire range of complexity parameter * Place initialization code into a function * Add bench for block finalization with caching * First attempt at bench with receipts * Try and trigger validator set change * Perform a validator set change during benchmarking * Move `validators_change_receipt()` to shared location Allows unit tests and benchmarks to access the same helper function and const * Extract a test receipt root into a constant * Clean up description of pruning bench * Fix cache and pruning tests * Remove unecessary `build_custom_header` usage * Get rid of warnings * Remove code duplication comment I don't think its entirely worth it to split out so few lines of code. The benches aren't particularly hard to read anyways. * Increase the range of the complexity parameter * Use dynamic number of receipts while benchmarking As part of this change we have removed the hardcoded TEST_RECEIPT_ROOT and instead chose to calculate the receipt root on the fly. This will make tests and benches less fragile. * Prune a dynamic number of headers
This commit is contained in:
committed by
Bastian Köcher
parent
1cd7be9214
commit
002e18a47c
@@ -215,6 +215,28 @@ impl pallet_aura::Trait for Runtime {
|
||||
type AuthorityId = AuraId;
|
||||
}
|
||||
|
||||
// We want to use a different validator configuration for benchmarking than what's used in Kovan,
|
||||
// but we can't configure a new validator set on the fly which means we need to wire the runtime
|
||||
// together like this
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
use pallet_bridge_eth_poa::{ValidatorsConfiguration, ValidatorsSource};
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
parameter_types! {
|
||||
pub const FinalityVotesCachingInterval: Option<u64> = Some(16);
|
||||
pub KovanAuraConfiguration: pallet_bridge_eth_poa::AuraConfiguration = kovan::kovan_aura_configuration();
|
||||
pub KovanValidatorsConfiguration: pallet_bridge_eth_poa::ValidatorsConfiguration = bench_validator_config();
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
fn bench_validator_config() -> ValidatorsConfiguration {
|
||||
ValidatorsConfiguration::Multi(vec![
|
||||
(0, ValidatorsSource::List(vec![[1; 20].into()])),
|
||||
(1, ValidatorsSource::Contract([3; 20].into(), vec![[1; 20].into()])),
|
||||
])
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "runtime-benchmarks"))]
|
||||
parameter_types! {
|
||||
pub const FinalityVotesCachingInterval: Option<u64> = Some(16);
|
||||
pub KovanAuraConfiguration: pallet_bridge_eth_poa::AuraConfiguration = kovan::kovan_aura_configuration();
|
||||
|
||||
@@ -16,11 +16,14 @@
|
||||
|
||||
use super::*;
|
||||
|
||||
use crate::test_utils::{build_custom_header, build_genesis_header, validator_utils::*};
|
||||
use crate::test_utils::{
|
||||
build_custom_header, build_genesis_header, insert_header, validator_utils::*, validators_change_receipt,
|
||||
HeaderBuilder,
|
||||
};
|
||||
|
||||
use frame_benchmarking::benchmarks;
|
||||
use frame_system::RawOrigin;
|
||||
use primitives::U256;
|
||||
use primitives::{compute_merkle_root, U256};
|
||||
|
||||
benchmarks! {
|
||||
_ { }
|
||||
@@ -33,15 +36,8 @@ benchmarks! {
|
||||
import_unsigned_header_best_case {
|
||||
let n in 1..1000;
|
||||
|
||||
// initialize storage with some initial header
|
||||
let initial_header = build_genesis_header(&validator(0));
|
||||
let initial_header_hash = initial_header.compute_hash();
|
||||
let initial_difficulty = initial_header.difficulty;
|
||||
initialize_storage::<T>(
|
||||
&initial_header,
|
||||
initial_difficulty,
|
||||
&validators_addresses(2),
|
||||
);
|
||||
let num_validators = 2;
|
||||
let initial_header = initialize_bench::<T>(num_validators);
|
||||
|
||||
// prepare header to be inserted
|
||||
let header = build_custom_header(
|
||||
@@ -55,6 +51,223 @@ benchmarks! {
|
||||
|
||||
}: import_unsigned_header(RawOrigin::None, header, None)
|
||||
verify {
|
||||
assert_eq!(BridgeStorage::<T>::new().best_block().0.number, 1);
|
||||
let storage = BridgeStorage::<T>::new();
|
||||
assert_eq!(storage.best_block().0.number, 1);
|
||||
assert_eq!(storage.finalized_block().number, 0);
|
||||
}
|
||||
|
||||
// Our goal with this bench is to try and see the effect that finalizing difference ranges of
|
||||
// blocks has on our import time. As such we need to make sure that we keep the number of
|
||||
// validators fixed while changing the number blocks finalized (the complexity parameter) by
|
||||
// importing the last header.
|
||||
//
|
||||
// One important thing to keep in mind is that the runtime provides a finality cache in order to
|
||||
// reduce the overhead of header finalization. However, this is only triggered every 16 blocks.
|
||||
import_unsigned_finality {
|
||||
// Our complexity parameter, n, will represent the number of blocks imported before
|
||||
// finalization.
|
||||
let n in 1..7;
|
||||
|
||||
let mut storage = BridgeStorage::<T>::new();
|
||||
let num_validators: u32 = 2;
|
||||
let initial_header = initialize_bench::<T>(num_validators as usize);
|
||||
|
||||
// Since we only have two validators we need to make sure the number of blocks is even to
|
||||
// make sure the right validator signs the final block
|
||||
let num_blocks = 2 * n;
|
||||
let mut headers = Vec::new();
|
||||
let mut parent = initial_header.clone();
|
||||
|
||||
// Import a bunch of headers without any verification, will ensure that they're not
|
||||
// finalized prematurely
|
||||
for i in 1..=num_blocks {
|
||||
let header = HeaderBuilder::with_parent(&parent).sign_by(&validator(0));
|
||||
let id = header.compute_id();
|
||||
insert_header(&mut storage, header.clone());
|
||||
headers.push(header.clone());
|
||||
parent = header;
|
||||
}
|
||||
|
||||
let last_header = headers.last().unwrap().clone();
|
||||
let last_authority = validator(1);
|
||||
|
||||
// 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)
|
||||
verify {
|
||||
let storage = BridgeStorage::<T>::new();
|
||||
assert_eq!(storage.best_block().0.number, (num_blocks + 1) as u64);
|
||||
assert_eq!(storage.finalized_block().number, num_blocks as u64);
|
||||
}
|
||||
|
||||
// Basically the exact same as `import_unsigned_finality` but with a different range for the
|
||||
// complexity parameter. In this bench we use a larger range of blocks to see how performance
|
||||
// changes when the finality cache kicks in (>16 blocks).
|
||||
import_unsigned_finality_with_cache {
|
||||
// Our complexity parameter, n, will represent the number of blocks imported before
|
||||
// finalization.
|
||||
let n in 7..100;
|
||||
|
||||
let mut storage = BridgeStorage::<T>::new();
|
||||
let num_validators: u32 = 2;
|
||||
let initial_header = initialize_bench::<T>(num_validators as usize);
|
||||
|
||||
// Since we only have two validators we need to make sure the number of blocks is even to
|
||||
// make sure the right validator signs the final block
|
||||
let num_blocks = 2 * n;
|
||||
let mut headers = Vec::new();
|
||||
let mut parent = initial_header.clone();
|
||||
|
||||
// Import a bunch of headers without any verification, will ensure that they're not
|
||||
// finalized prematurely
|
||||
for i in 1..=num_blocks {
|
||||
let header = HeaderBuilder::with_parent(&parent).sign_by(&validator(0));
|
||||
let id = header.compute_id();
|
||||
insert_header(&mut storage, header.clone());
|
||||
headers.push(header.clone());
|
||||
parent = header;
|
||||
}
|
||||
|
||||
let last_header = headers.last().unwrap().clone();
|
||||
let last_authority = validator(1);
|
||||
|
||||
// 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)
|
||||
verify {
|
||||
let storage = BridgeStorage::<T>::new();
|
||||
assert_eq!(storage.best_block().0.number, (num_blocks + 1) as u64);
|
||||
assert_eq!(storage.finalized_block().number, num_blocks as u64);
|
||||
}
|
||||
|
||||
// A block import may trigger a pruning event, which adds extra work to the import progress.
|
||||
// In this bench we trigger a pruning event in order to see how much extra time is spent by the
|
||||
// runtime dealing with it. In the Ethereum Pallet, we're limited pruning to eight blocks in a
|
||||
// single import, as dictated by MAX_BLOCKS_TO_PRUNE_IN_SINGLE_IMPORT.
|
||||
import_unsigned_pruning {
|
||||
let n in 1..MAX_BLOCKS_TO_PRUNE_IN_SINGLE_IMPORT as u32;
|
||||
|
||||
let mut storage = BridgeStorage::<T>::new();
|
||||
|
||||
let num_validators = 3;
|
||||
let initial_header = initialize_bench::<T>(num_validators as usize);
|
||||
let validators = validators(num_validators);
|
||||
|
||||
// Want to prune eligible blocks between [0, n)
|
||||
BlocksToPrune::put(PruningRange {
|
||||
oldest_unpruned_block: 0,
|
||||
oldest_block_to_keep: n as u64,
|
||||
});
|
||||
|
||||
let mut parent = initial_header;
|
||||
for i in 1..=n {
|
||||
let header = HeaderBuilder::with_parent(&parent).sign_by_set(&validators);
|
||||
let id = header.compute_id();
|
||||
insert_header(&mut storage, header.clone());
|
||||
parent = header;
|
||||
}
|
||||
|
||||
let header = HeaderBuilder::with_parent(&parent).sign_by_set(&validators);
|
||||
}: import_unsigned_header(RawOrigin::None, header, None)
|
||||
verify {
|
||||
let storage = BridgeStorage::<T>::new();
|
||||
let max_pruned: u64 = (n - 1) as _;
|
||||
assert_eq!(storage.best_block().0.number, (n + 1) as u64);
|
||||
assert!(HeadersByNumber::get(&0).is_none());
|
||||
assert!(HeadersByNumber::get(&max_pruned).is_none());
|
||||
}
|
||||
|
||||
// The goal of this bench is to import a block which contains a transaction receipt. The receipt
|
||||
// will contain a validator set change. Verifying the receipt root is an expensive operation to
|
||||
// do, which is why we're interested in benchmarking it.
|
||||
import_unsigned_with_receipts {
|
||||
let n in 1..100;
|
||||
|
||||
let mut storage = BridgeStorage::<T>::new();
|
||||
|
||||
let num_validators = 1;
|
||||
let initial_header = initialize_bench::<T>(num_validators as usize);
|
||||
|
||||
let mut receipts = vec![];
|
||||
for i in 1..=n {
|
||||
let receipt = validators_change_receipt(Default::default());
|
||||
receipts.push(receipt)
|
||||
}
|
||||
let encoded_receipts = receipts.iter().map(|r| r.rlp());
|
||||
|
||||
// We need this extra header since this is what signals a validator set transition. This
|
||||
// will ensure that the next header is within the "Contract" window
|
||||
let header1 = HeaderBuilder::with_parent(&initial_header).sign_by(&validator(0));
|
||||
insert_header(&mut storage, header1.clone());
|
||||
|
||||
let header = build_custom_header(
|
||||
&validator(0),
|
||||
&header1,
|
||||
|mut header| {
|
||||
// Logs Bloom signals a change in validator set
|
||||
header.log_bloom = (&[0xff; 256]).into();
|
||||
header.receipts_root = compute_merkle_root(encoded_receipts);
|
||||
header
|
||||
},
|
||||
);
|
||||
}: import_unsigned_header(RawOrigin::None, header, Some(receipts))
|
||||
verify {
|
||||
let storage = BridgeStorage::<T>::new();
|
||||
assert_eq!(storage.best_block().0.number, 2);
|
||||
}
|
||||
}
|
||||
|
||||
fn initialize_bench<T: Trait>(num_validators: usize) -> Header {
|
||||
// Initialize storage with some initial header
|
||||
let initial_header = build_genesis_header(&validator(0));
|
||||
let initial_difficulty = initial_header.difficulty;
|
||||
let initial_validators = validators_addresses(num_validators as usize);
|
||||
|
||||
initialize_storage::<T>(&initial_header, initial_difficulty, &initial_validators);
|
||||
|
||||
initial_header
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::mock::{run_test, TestRuntime};
|
||||
use frame_support::assert_ok;
|
||||
|
||||
#[test]
|
||||
fn insert_unsigned_header_best_case() {
|
||||
run_test(1, |_| {
|
||||
assert_ok!(test_benchmark_import_unsigned_header_best_case::<TestRuntime>());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_unsigned_header_finality() {
|
||||
run_test(1, |_| {
|
||||
assert_ok!(test_benchmark_import_unsigned_finality::<TestRuntime>());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_unsigned_header_finality_with_cache() {
|
||||
run_test(1, |_| {
|
||||
assert_ok!(test_benchmark_import_unsigned_finality_with_cache::<TestRuntime>());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_unsigned_header_pruning() {
|
||||
run_test(1, |_| {
|
||||
assert_ok!(test_benchmark_import_unsigned_pruning::<TestRuntime>());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_unsigned_header_receipts() {
|
||||
run_test(1, |_| {
|
||||
assert_ok!(test_benchmark_import_unsigned_with_receipts::<TestRuntime>());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::mock::{
|
||||
run_test, secret_to_address, test_aura_config, test_validators_config, validator, validators_addresses,
|
||||
HeaderBuilder, KeepSomeHeadersBehindBest, TestRuntime, GAS_LIMIT,
|
||||
validators_change_receipt, HeaderBuilder, KeepSomeHeadersBehindBest, TestRuntime, GAS_LIMIT,
|
||||
};
|
||||
use crate::validators::ValidatorsSource;
|
||||
use crate::{BlocksToPrune, BridgeStorage, Headers, PruningRange};
|
||||
@@ -316,9 +316,7 @@ mod tests {
|
||||
&validators_config,
|
||||
Some(101),
|
||||
header11.clone(),
|
||||
Some(vec![crate::validators::tests::validators_change_recept(
|
||||
latest_block_id.hash,
|
||||
)]),
|
||||
Some(vec![validators_change_receipt(latest_block_id.hash)]),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(finalized_blocks, vec![(parent_id, Some(100))],);
|
||||
|
||||
@@ -463,7 +463,7 @@ decl_storage! {
|
||||
// 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 authred by the same validators set.
|
||||
// 3) (implied) all direct children of initial block are authored by the same validators set.
|
||||
|
||||
assert!(
|
||||
!config.initial_validators.is_empty(),
|
||||
@@ -563,6 +563,7 @@ impl<T: Trait> BridgeStorage<T> {
|
||||
// start pruning blocks
|
||||
let begin = new_pruning_range.oldest_unpruned_block;
|
||||
let end = new_pruning_range.oldest_block_to_keep;
|
||||
frame_support::debug::trace!(target: "runtime", "Pruning blocks in range [{}..{})", begin, end);
|
||||
for number in begin..end {
|
||||
// if we can't prune anything => break
|
||||
if max_blocks_to_prune == 0 {
|
||||
@@ -588,6 +589,11 @@ impl<T: Trait> BridgeStorage<T> {
|
||||
|
||||
// we have pruned all headers at number
|
||||
new_pruning_range.oldest_unpruned_block = number + 1;
|
||||
frame_support::debug::trace!(
|
||||
target: "runtime",
|
||||
"Oldest unpruned PoA header is now: {}",
|
||||
new_pruning_range.oldest_unpruned_block,
|
||||
);
|
||||
}
|
||||
|
||||
// update pruning range in storage
|
||||
|
||||
@@ -14,12 +14,11 @@
|
||||
// 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/>.
|
||||
|
||||
pub use crate::test_utils::{validator_utils::*, HeaderBuilder, GAS_LIMIT};
|
||||
pub use crate::test_utils::{insert_header, validator_utils::*, validators_change_receipt, HeaderBuilder, GAS_LIMIT};
|
||||
pub use primitives::signatures::secret_to_address;
|
||||
|
||||
use crate::finality::FinalityVotes;
|
||||
use crate::validators::{ValidatorsConfiguration, ValidatorsSource};
|
||||
use crate::{AuraConfiguration, GenesisConfig, HeaderToImport, PruningStrategy, Storage, Trait};
|
||||
use crate::{AuraConfiguration, GenesisConfig, PruningStrategy, Trait};
|
||||
use frame_support::{impl_outer_origin, parameter_types, weights::Weight};
|
||||
use primitives::{Address, Header, H256, U256};
|
||||
use secp256k1::SecretKey;
|
||||
@@ -149,20 +148,6 @@ pub fn run_test_with_genesis<T>(genesis: Header, total_validators: usize, test:
|
||||
})
|
||||
}
|
||||
|
||||
/// Insert unverified header into storage.
|
||||
pub fn insert_header<S: Storage>(storage: &mut S, header: Header) {
|
||||
storage.insert_header(HeaderToImport {
|
||||
context: storage.import_context(None, &header.parent_hash).unwrap(),
|
||||
is_best: true,
|
||||
id: header.compute_id(),
|
||||
header,
|
||||
total_difficulty: 0.into(),
|
||||
enacted_change: None,
|
||||
scheduled_change: None,
|
||||
finality_votes: FinalityVotes::default(),
|
||||
});
|
||||
}
|
||||
|
||||
/// Pruning strategy that keeps 10 headers behind best block.
|
||||
pub struct KeepSomeHeadersBehindBest(pub u64);
|
||||
|
||||
|
||||
@@ -21,12 +21,18 @@
|
||||
//!
|
||||
//! On the other hand, they may be used directly by the bechmarking 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::{HeaderToImport, Storage};
|
||||
|
||||
use primitives::{
|
||||
rlp_encode,
|
||||
signatures::{secret_to_address, sign, SignHeader},
|
||||
Address, Bloom, Header, SealedEmptyStep, H256, U256,
|
||||
Address, Bloom, Header, Receipt, SealedEmptyStep, H256, U256,
|
||||
};
|
||||
use secp256k1::SecretKey;
|
||||
use sp_std::prelude::*;
|
||||
@@ -206,6 +212,39 @@ where
|
||||
custom_header.sign_by(author)
|
||||
}
|
||||
|
||||
/// Insert unverified header into storage.
|
||||
pub fn insert_header<S: Storage>(storage: &mut S, header: Header) {
|
||||
storage.insert_header(HeaderToImport {
|
||||
context: storage.import_context(None, &header.parent_hash).unwrap(),
|
||||
is_best: true,
|
||||
id: header.compute_id(),
|
||||
header,
|
||||
total_difficulty: 0.into(),
|
||||
enacted_change: None,
|
||||
scheduled_change: None,
|
||||
finality_votes: FinalityVotes::default(),
|
||||
});
|
||||
}
|
||||
|
||||
pub fn validators_change_receipt(parent_hash: H256) -> Receipt {
|
||||
use primitives::{LogEntry, TransactionOutcome};
|
||||
|
||||
Receipt {
|
||||
gas_used: 0.into(),
|
||||
log_bloom: (&[0xff; 256]).into(),
|
||||
outcome: TransactionOutcome::Unknown,
|
||||
logs: vec![LogEntry {
|
||||
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,
|
||||
],
|
||||
}],
|
||||
}
|
||||
}
|
||||
|
||||
pub mod validator_utils {
|
||||
use super::*;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ use primitives::{Address, Header, HeaderId, LogEntry, Receipt, U256};
|
||||
use sp_std::prelude::*;
|
||||
|
||||
/// The hash of InitiateChange event of the validators set contract.
|
||||
const CHANGE_EVENT_HASH: &'static [u8; 32] = &[
|
||||
pub(crate) const CHANGE_EVENT_HASH: &'static [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,
|
||||
];
|
||||
@@ -39,7 +39,7 @@ pub enum ValidatorsConfiguration {
|
||||
/// This source is valid within some blocks range. The blocks range could
|
||||
/// cover multiple epochs - i.e. the validators that are authoring blocks
|
||||
/// within this range could change, but the source itself can not.
|
||||
#[cfg_attr(test, derive(Debug, PartialEq))]
|
||||
#[cfg_attr(any(test, feature = "runtime-benchmarks"), derive(Debug, PartialEq))]
|
||||
pub enum ValidatorsSource {
|
||||
/// The validators addresses are hardcoded and never change.
|
||||
List(Vec<Address>),
|
||||
@@ -276,30 +276,13 @@ impl ValidatorsSource {
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::mock::{run_test, validators_addresses, TestRuntime};
|
||||
use crate::mock::{run_test, validators_addresses, validators_change_receipt, TestRuntime};
|
||||
use crate::{BridgeStorage, Headers, ScheduledChange, ScheduledChanges, StoredHeader};
|
||||
use frame_support::StorageMap;
|
||||
use primitives::{TransactionOutcome, H256};
|
||||
use primitives::compute_merkle_root;
|
||||
|
||||
const TOTAL_VALIDATORS: usize = 3;
|
||||
|
||||
pub(crate) fn validators_change_recept(parent_hash: H256) -> Receipt {
|
||||
Receipt {
|
||||
gas_used: 0.into(),
|
||||
log_bloom: (&[0xff; 256]).into(),
|
||||
outcome: TransactionOutcome::Unknown,
|
||||
logs: vec![LogEntry {
|
||||
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,
|
||||
],
|
||||
}],
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn source_at_works() {
|
||||
let config = ValidatorsConfiguration::Multi(vec![
|
||||
@@ -405,10 +388,8 @@ pub(crate) mod tests {
|
||||
|
||||
// when we're inside contract range and logs bloom signals change
|
||||
// and there's change in receipts
|
||||
let receipts = vec![validators_change_recept(Default::default())];
|
||||
header.receipts_root = "81ce88dc524403b796222046bf3daf543978329b87ffd50228f1d3987031dc45"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let receipts = vec![validators_change_receipt(Default::default())];
|
||||
header.receipts_root = compute_merkle_root(receipts.iter().map(|r| r.rlp()));
|
||||
assert_eq!(
|
||||
validators.extract_validators_change(&header, Some(receipts)),
|
||||
Ok((Some(vec![[7; 20].into()]), None)),
|
||||
|
||||
@@ -140,6 +140,7 @@ pub fn accept_aura_header_into_pool<S: Storage>(
|
||||
|
||||
// the heaviest, but rare operation - we do not want invalid receipts in the pool
|
||||
if let Some(receipts) = receipts {
|
||||
frame_support::debug::trace!(target: "runtime", "Got receipts! {:?}", receipts);
|
||||
if !header.verify_receipts_root(receipts) {
|
||||
return Err(Error::TransactionsReceiptsMismatch);
|
||||
}
|
||||
@@ -354,15 +355,15 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::mock::{
|
||||
insert_header, run_test_with_genesis, test_aura_config, validator, validator_address, validators_addresses,
|
||||
AccountId, HeaderBuilder, TestRuntime, GAS_LIMIT,
|
||||
validators_change_receipt, AccountId, HeaderBuilder, TestRuntime, GAS_LIMIT,
|
||||
};
|
||||
use crate::validators::{tests::validators_change_recept, ValidatorsSource};
|
||||
use crate::validators::ValidatorsSource;
|
||||
use crate::{
|
||||
pool_configuration, BridgeStorage, FinalizedBlock, Headers, HeadersByNumber, NextValidatorsSetId,
|
||||
ScheduledChanges, ValidatorsSet, ValidatorsSets,
|
||||
};
|
||||
use frame_support::{StorageMap, StorageValue};
|
||||
use primitives::{rlp_encode, TransactionOutcome, H520};
|
||||
use primitives::{compute_merkle_root, rlp_encode, TransactionOutcome, H520};
|
||||
use secp256k1::SecretKey;
|
||||
|
||||
const GENESIS_STEP: u64 = 42;
|
||||
@@ -844,7 +845,7 @@ mod tests {
|
||||
let header = HeaderBuilder::with_parent_number(3)
|
||||
.log_bloom((&[0xff; 256]).into())
|
||||
.sign_by_set(validators);
|
||||
(header, Some(vec![validators_change_recept(Default::default())]))
|
||||
(header, Some(vec![validators_change_receipt(Default::default())]))
|
||||
}),
|
||||
Err(Error::TransactionsReceiptsMismatch),
|
||||
);
|
||||
@@ -853,18 +854,17 @@ mod tests {
|
||||
#[test]
|
||||
fn pool_accepts_headers_with_valid_receipts() {
|
||||
let mut hash = None;
|
||||
let receipts = vec![validators_change_receipt(Default::default())];
|
||||
let receipts_root = compute_merkle_root(receipts.iter().map(|r| r.rlp()));
|
||||
|
||||
assert_eq!(
|
||||
default_accept_into_pool(|validators| {
|
||||
let header = HeaderBuilder::with_parent_number(3)
|
||||
.log_bloom((&[0xff; 256]).into())
|
||||
.receipts_root(
|
||||
"81ce88dc524403b796222046bf3daf543978329b87ffd50228f1d3987031dc45"
|
||||
.parse()
|
||||
.unwrap(),
|
||||
)
|
||||
.receipts_root(receipts_root)
|
||||
.sign_by_set(validators);
|
||||
hash = Some(header.compute_hash());
|
||||
(header, Some(vec![validators_change_recept(Default::default())]))
|
||||
(header, Some(receipts.clone()))
|
||||
}),
|
||||
Ok((
|
||||
// no tags are required
|
||||
|
||||
@@ -322,7 +322,7 @@ impl UnsignedTransaction {
|
||||
|
||||
impl Receipt {
|
||||
/// Returns receipt RLP.
|
||||
fn rlp(&self) -> Bytes {
|
||||
pub fn rlp(&self) -> Bytes {
|
||||
let mut s = RlpStream::new();
|
||||
match self.outcome {
|
||||
TransactionOutcome::Unknown => {
|
||||
|
||||
Reference in New Issue
Block a user