mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 07:31:02 +00:00
Reward submitters only when submitted block is finalized (#34)
* reward submitters on finalization * fix grumble * make submitter part of ImportContext
This commit is contained in:
committed by
Bastian Köcher
parent
8232bdfe30
commit
d904a282c8
@@ -49,9 +49,10 @@ pub fn finalize_blocks<S: Storage>(
|
|||||||
best_finalized_hash: &H256,
|
best_finalized_hash: &H256,
|
||||||
header_validators: (&H256, &[Address]),
|
header_validators: (&H256, &[Address]),
|
||||||
hash: &H256,
|
hash: &H256,
|
||||||
|
submitter: Option<&S::Submitter>,
|
||||||
header: &Header,
|
header: &Header,
|
||||||
two_thirds_majority_transition: u64,
|
two_thirds_majority_transition: u64,
|
||||||
) -> Result<Vec<(u64, H256)>, Error> {
|
) -> Result<Vec<(u64, H256, Option<S::Submitter>)>, Error> {
|
||||||
// compute count of voters for every unfinalized block in ancestry
|
// compute count of voters for every unfinalized block in ancestry
|
||||||
let validators = header_validators.1.iter().collect();
|
let validators = header_validators.1.iter().collect();
|
||||||
let (mut votes, mut headers) = prepare_votes(
|
let (mut votes, mut headers) = prepare_votes(
|
||||||
@@ -61,18 +62,19 @@ pub fn finalize_blocks<S: Storage>(
|
|||||||
&validators,
|
&validators,
|
||||||
hash,
|
hash,
|
||||||
header,
|
header,
|
||||||
|
submitter,
|
||||||
two_thirds_majority_transition,
|
two_thirds_majority_transition,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// now let's iterate in reverse order && find just finalized blocks
|
// now let's iterate in reverse order && find just finalized blocks
|
||||||
let mut newly_finalized = Vec::new();
|
let mut newly_finalized = Vec::new();
|
||||||
while let Some((oldest_hash, oldest_number, signers)) = headers.pop_front() {
|
while let Some((oldest_hash, oldest_number, submitter, signers)) = headers.pop_front() {
|
||||||
if !is_finalized(&validators, &votes, oldest_number >= two_thirds_majority_transition) {
|
if !is_finalized(&validators, &votes, oldest_number >= two_thirds_majority_transition) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
remove_signers_votes(&signers, &mut votes);
|
remove_signers_votes(&signers, &mut votes);
|
||||||
newly_finalized.push((oldest_number, oldest_hash));
|
newly_finalized.push((oldest_number, oldest_hash, submitter));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(newly_finalized)
|
Ok(newly_finalized)
|
||||||
@@ -96,8 +98,9 @@ fn prepare_votes<S: Storage>(
|
|||||||
validators: &BTreeSet<&Address>,
|
validators: &BTreeSet<&Address>,
|
||||||
hash: &H256,
|
hash: &H256,
|
||||||
header: &Header,
|
header: &Header,
|
||||||
|
submitter: Option<&S::Submitter>,
|
||||||
two_thirds_majority_transition: u64,
|
two_thirds_majority_transition: u64,
|
||||||
) -> Result<(BTreeMap<Address, u64>, VecDeque<(H256, u64, BTreeSet<Address>)>), Error> {
|
) -> Result<(BTreeMap<Address, u64>, VecDeque<(H256, u64, Option<S::Submitter>, BTreeSet<Address>)>), Error> {
|
||||||
// this fn can only work with single validators set
|
// this fn can only work with single validators set
|
||||||
if !validators.contains(&header.author) {
|
if !validators.contains(&header.author) {
|
||||||
return Err(Error::NotValidator);
|
return Err(Error::NotValidator);
|
||||||
@@ -108,17 +111,17 @@ fn prepare_votes<S: Storage>(
|
|||||||
// the same set of validators
|
// the same set of validators
|
||||||
let mut parent_empty_step_signers = empty_steps_signers(header);
|
let mut parent_empty_step_signers = empty_steps_signers(header);
|
||||||
let ancestry = ancestry(storage, header)
|
let ancestry = ancestry(storage, header)
|
||||||
.map(|(hash, header)| {
|
.map(|(hash, header, submitter)| {
|
||||||
let mut signers = BTreeSet::new();
|
let mut signers = BTreeSet::new();
|
||||||
sp_std::mem::swap(&mut signers, &mut parent_empty_step_signers);
|
sp_std::mem::swap(&mut signers, &mut parent_empty_step_signers);
|
||||||
signers.insert(header.author);
|
signers.insert(header.author);
|
||||||
|
|
||||||
let empty_step_signers = empty_steps_signers(&header);
|
let empty_step_signers = empty_steps_signers(&header);
|
||||||
let res = (hash, header.number, signers);
|
let res = (hash, header.number, submitter, signers);
|
||||||
parent_empty_step_signers = empty_step_signers;
|
parent_empty_step_signers = empty_step_signers;
|
||||||
res
|
res
|
||||||
})
|
})
|
||||||
.take_while(|&(hash, _, _)| hash != *validators_begin && hash != *best_finalized_hash);
|
.take_while(|&(hash, _, _, _)| hash != *validators_begin && hash != *best_finalized_hash);
|
||||||
|
|
||||||
// now let's iterate built iterator and compute number of validators
|
// now let's iterate built iterator and compute number of validators
|
||||||
// 'voted' for each header
|
// 'voted' for each header
|
||||||
@@ -126,21 +129,21 @@ fn prepare_votes<S: Storage>(
|
|||||||
// just finalized blocks)
|
// just finalized blocks)
|
||||||
let mut votes = BTreeMap::new();
|
let mut votes = BTreeMap::new();
|
||||||
let mut headers = VecDeque::new();
|
let mut headers = VecDeque::new();
|
||||||
for (hash, number, signers) in ancestry {
|
for (hash, number, submitter, signers) in ancestry {
|
||||||
add_signers_votes(validators, &signers, &mut votes)?;
|
add_signers_votes(validators, &signers, &mut votes)?;
|
||||||
if is_finalized(validators, &votes, number >= two_thirds_majority_transition) {
|
if is_finalized(validators, &votes, number >= two_thirds_majority_transition) {
|
||||||
remove_signers_votes(&signers, &mut votes);
|
remove_signers_votes(&signers, &mut votes);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
headers.push_front((hash, number, signers));
|
headers.push_front((hash, number, submitter, signers));
|
||||||
}
|
}
|
||||||
|
|
||||||
// update votes with last header vote
|
// update votes with last header vote
|
||||||
let mut header_signers = BTreeSet::new();
|
let mut header_signers = BTreeSet::new();
|
||||||
header_signers.insert(header.author);
|
header_signers.insert(header.author);
|
||||||
*votes.entry(header.author).or_insert(0) += 1;
|
*votes.entry(header.author).or_insert(0) += 1;
|
||||||
headers.push_back((*hash, header.number, header_signers));
|
headers.push_back((*hash, header.number, submitter.cloned(), header_signers));
|
||||||
|
|
||||||
Ok((votes, headers))
|
Ok((votes, headers))
|
||||||
}
|
}
|
||||||
@@ -211,6 +214,7 @@ mod tests {
|
|||||||
&Default::default(),
|
&Default::default(),
|
||||||
(&Default::default(), &[]),
|
(&Default::default(), &[]),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
|
None,
|
||||||
&Header::default(),
|
&Header::default(),
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
@@ -233,7 +237,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
let hash1 = header1.hash();
|
let hash1 = header1.hash();
|
||||||
let mut header_to_import = HeaderToImport {
|
let mut header_to_import = HeaderToImport {
|
||||||
context: storage.import_context(&genesis().hash()).unwrap(),
|
context: storage.import_context(None, &genesis().hash()).unwrap(),
|
||||||
is_best: true,
|
is_best: true,
|
||||||
hash: hash1,
|
hash: hash1,
|
||||||
header: header1,
|
header: header1,
|
||||||
@@ -247,6 +251,7 @@ mod tests {
|
|||||||
&Default::default(),
|
&Default::default(),
|
||||||
(&Default::default(), &validators_addresses(5)),
|
(&Default::default(), &validators_addresses(5)),
|
||||||
&hash1,
|
&hash1,
|
||||||
|
None,
|
||||||
&header_to_import.header,
|
&header_to_import.header,
|
||||||
u64::max_value(),
|
u64::max_value(),
|
||||||
),
|
),
|
||||||
@@ -269,6 +274,7 @@ mod tests {
|
|||||||
&Default::default(),
|
&Default::default(),
|
||||||
(&Default::default(), &validators_addresses(5)),
|
(&Default::default(), &validators_addresses(5)),
|
||||||
&hash2,
|
&hash2,
|
||||||
|
None,
|
||||||
&header_to_import.header,
|
&header_to_import.header,
|
||||||
u64::max_value(),
|
u64::max_value(),
|
||||||
),
|
),
|
||||||
@@ -291,10 +297,11 @@ mod tests {
|
|||||||
&Default::default(),
|
&Default::default(),
|
||||||
(&Default::default(), &validators_addresses(5)),
|
(&Default::default(), &validators_addresses(5)),
|
||||||
&hash3,
|
&hash3,
|
||||||
|
None,
|
||||||
&header_to_import.header,
|
&header_to_import.header,
|
||||||
u64::max_value(),
|
u64::max_value(),
|
||||||
),
|
),
|
||||||
Ok(vec![(1, hash1)]),
|
Ok(vec![(1, hash1, None)]),
|
||||||
);
|
);
|
||||||
storage.insert_header(header_to_import);
|
storage.insert_header(header_to_import);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,10 @@ use crate::validators::{Validators, ValidatorsConfiguration};
|
|||||||
use crate::verification::verify_aura_header;
|
use crate::verification::verify_aura_header;
|
||||||
use crate::{AuraConfiguration, Storage};
|
use crate::{AuraConfiguration, Storage};
|
||||||
use primitives::{Header, Receipt, H256};
|
use primitives::{Header, Receipt, H256};
|
||||||
use sp_std::prelude::*;
|
use sp_std::{
|
||||||
|
collections::btree_map::BTreeMap,
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
|
||||||
/// Maximal number of headers behind best blocks that we are aiming to store. When there
|
/// Maximal number of headers behind best blocks that we are aiming to store. When there
|
||||||
/// are too many unfinalized headers, it slows down finalization tracking significantly.
|
/// are too many unfinalized headers, it slows down finalization tracking significantly.
|
||||||
@@ -61,15 +64,32 @@ pub fn import_headers<S: Storage>(
|
|||||||
aura_config: &AuraConfiguration,
|
aura_config: &AuraConfiguration,
|
||||||
validators_config: &ValidatorsConfiguration,
|
validators_config: &ValidatorsConfiguration,
|
||||||
prune_depth: u64,
|
prune_depth: u64,
|
||||||
|
submitter: Option<S::Submitter>,
|
||||||
headers: Vec<(Header, Option<Vec<Receipt>>)>,
|
headers: Vec<(Header, Option<Vec<Receipt>>)>,
|
||||||
|
finalized_headers: &mut BTreeMap<S::Submitter, u64>,
|
||||||
) -> Result<(u64, u64), Error> {
|
) -> Result<(u64, u64), Error> {
|
||||||
let mut useful = 0;
|
let mut useful = 0;
|
||||||
let mut useless = 0;
|
let mut useless = 0;
|
||||||
for (header, receipts) in headers {
|
for (header, receipts) in headers {
|
||||||
let import_result = import_header(storage, aura_config, validators_config, prune_depth, header, receipts);
|
let import_result = import_header(
|
||||||
|
storage,
|
||||||
|
aura_config,
|
||||||
|
validators_config,
|
||||||
|
prune_depth,
|
||||||
|
submitter.clone(),
|
||||||
|
header,
|
||||||
|
receipts,
|
||||||
|
);
|
||||||
|
|
||||||
match import_result {
|
match import_result {
|
||||||
Ok(_) => useful += 1,
|
Ok((_, finalized)) => {
|
||||||
|
for (_, _, submitter) in finalized {
|
||||||
|
if let Some(submitter) = submitter {
|
||||||
|
*finalized_headers.entry(submitter).or_default() += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
useful += 1;
|
||||||
|
},
|
||||||
Err(Error::AncientHeader) | Err(Error::KnownHeader) => useless += 1,
|
Err(Error::AncientHeader) | Err(Error::KnownHeader) => useless += 1,
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
}
|
}
|
||||||
@@ -82,19 +102,22 @@ pub fn import_headers<S: Storage>(
|
|||||||
///
|
///
|
||||||
/// Transactions receipts must be provided if `header_import_requires_receipts()`
|
/// Transactions receipts must be provided if `header_import_requires_receipts()`
|
||||||
/// has returned true.
|
/// has returned true.
|
||||||
|
///
|
||||||
|
/// Returns imported block hash.
|
||||||
pub fn import_header<S: Storage>(
|
pub fn import_header<S: Storage>(
|
||||||
storage: &mut S,
|
storage: &mut S,
|
||||||
aura_config: &AuraConfiguration,
|
aura_config: &AuraConfiguration,
|
||||||
validators_config: &ValidatorsConfiguration,
|
validators_config: &ValidatorsConfiguration,
|
||||||
prune_depth: u64,
|
prune_depth: u64,
|
||||||
|
submitter: Option<S::Submitter>,
|
||||||
header: Header,
|
header: Header,
|
||||||
receipts: Option<Vec<Receipt>>,
|
receipts: Option<Vec<Receipt>>,
|
||||||
) -> Result<H256, Error> {
|
) -> Result<(H256, Vec<(u64, H256, Option<S::Submitter>)>), Error> {
|
||||||
// first check that we are able to import this header at all
|
// first check that we are able to import this header at all
|
||||||
let (hash, prev_finalized_hash) = is_importable_header(storage, &header)?;
|
let (hash, prev_finalized_hash) = is_importable_header(storage, &header)?;
|
||||||
|
|
||||||
// verify header
|
// verify header
|
||||||
let import_context = verify_aura_header(storage, aura_config, &header)?;
|
let import_context = verify_aura_header(storage, aura_config, submitter, &header)?;
|
||||||
|
|
||||||
// check if block schedules new validators
|
// check if block schedules new validators
|
||||||
let validators = Validators::new(validators_config);
|
let validators = Validators::new(validators_config);
|
||||||
@@ -106,6 +129,7 @@ pub fn import_header<S: Storage>(
|
|||||||
&prev_finalized_hash,
|
&prev_finalized_hash,
|
||||||
(import_context.validators_start(), import_context.validators()),
|
(import_context.validators_start(), import_context.validators()),
|
||||||
&hash,
|
&hash,
|
||||||
|
import_context.submitter(),
|
||||||
&header,
|
&header,
|
||||||
aura_config.two_thirds_majority_transition,
|
aura_config.two_thirds_majority_transition,
|
||||||
)?;
|
)?;
|
||||||
@@ -119,25 +143,27 @@ pub fn import_header<S: Storage>(
|
|||||||
let total_difficulty = import_context.total_difficulty() + header.difficulty;
|
let total_difficulty = import_context.total_difficulty() + header.difficulty;
|
||||||
let is_best = total_difficulty > best_total_difficulty;
|
let is_best = total_difficulty > best_total_difficulty;
|
||||||
let header_number = header.number;
|
let header_number = header.number;
|
||||||
storage.insert_header(import_context.into_import_header(
|
storage.insert_header(
|
||||||
|
import_context.into_import_header(
|
||||||
is_best,
|
is_best,
|
||||||
hash,
|
hash,
|
||||||
header,
|
header,
|
||||||
total_difficulty,
|
total_difficulty,
|
||||||
enacted_change,
|
enacted_change,
|
||||||
scheduled_change,
|
scheduled_change,
|
||||||
));
|
),
|
||||||
|
);
|
||||||
|
|
||||||
// now mark finalized headers && prune old headers
|
// now mark finalized headers && prune old headers
|
||||||
storage.finalize_headers(
|
storage.finalize_headers(
|
||||||
finalized_blocks.last().cloned(),
|
finalized_blocks.last().map(|(number, hash, _)| (*number, *hash)),
|
||||||
match is_best {
|
match is_best {
|
||||||
true => header_number.checked_sub(prune_depth),
|
true => header_number.checked_sub(prune_depth),
|
||||||
false => None,
|
false => None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(hash)
|
Ok((hash, finalized_blocks))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if transactions receipts are required to import given header.
|
/// Returns true if transactions receipts are required to import given header.
|
||||||
@@ -189,6 +215,7 @@ mod tests {
|
|||||||
&kovan_aura_config(),
|
&kovan_aura_config(),
|
||||||
&kovan_validators_config(),
|
&kovan_validators_config(),
|
||||||
PRUNE_DEPTH,
|
PRUNE_DEPTH,
|
||||||
|
None,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
@@ -207,6 +234,7 @@ mod tests {
|
|||||||
&kovan_aura_config(),
|
&kovan_aura_config(),
|
||||||
&kovan_validators_config(),
|
&kovan_validators_config(),
|
||||||
PRUNE_DEPTH,
|
PRUNE_DEPTH,
|
||||||
|
None,
|
||||||
block.clone(),
|
block.clone(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -219,6 +247,7 @@ mod tests {
|
|||||||
&kovan_aura_config(),
|
&kovan_aura_config(),
|
||||||
&kovan_validators_config(),
|
&kovan_validators_config(),
|
||||||
PRUNE_DEPTH,
|
PRUNE_DEPTH,
|
||||||
|
None,
|
||||||
block,
|
block,
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -243,6 +272,7 @@ mod tests {
|
|||||||
&kovan_aura_config(),
|
&kovan_aura_config(),
|
||||||
&validators_config,
|
&validators_config,
|
||||||
PRUNE_DEPTH,
|
PRUNE_DEPTH,
|
||||||
|
None,
|
||||||
header,
|
header,
|
||||||
None
|
None
|
||||||
)
|
)
|
||||||
@@ -267,43 +297,67 @@ mod tests {
|
|||||||
|
|
||||||
// header [0..11] are finalizing blocks [0; 9]
|
// header [0..11] are finalizing blocks [0; 9]
|
||||||
// => since we want to keep 10 finalized blocks, we aren't pruning anything
|
// => since we want to keep 10 finalized blocks, we aren't pruning anything
|
||||||
let mut last_block_hash = Default::default();
|
let mut latest_block_hash = Default::default();
|
||||||
for i in 1..11 {
|
for i in 1..11 {
|
||||||
let header = block_i(&storage, i, &validators);
|
let header = block_i(&storage, i, &validators);
|
||||||
last_block_hash =
|
let (rolling_last_block_hash, finalized_blocks) =
|
||||||
import_header(&mut storage, &kovan_aura_config(), &validators_config, 10, header, None).unwrap();
|
import_header(
|
||||||
|
&mut storage,
|
||||||
|
&kovan_aura_config(),
|
||||||
|
&validators_config,
|
||||||
|
10,
|
||||||
|
Some(100),
|
||||||
|
header,
|
||||||
|
None,
|
||||||
|
).unwrap();
|
||||||
|
match i {
|
||||||
|
2 ..= 10 => assert_eq!(
|
||||||
|
finalized_blocks,
|
||||||
|
vec![(i - 1, block_i(&storage, i - 1, &validators).hash(), Some(100))],
|
||||||
|
"At {}",
|
||||||
|
i,
|
||||||
|
),
|
||||||
|
_ => assert_eq!(finalized_blocks, vec![], "At {}", i),
|
||||||
|
}
|
||||||
|
latest_block_hash = rolling_last_block_hash;
|
||||||
}
|
}
|
||||||
assert!(storage.header(&genesis().hash()).is_some());
|
assert!(storage.header(&genesis().hash()).is_some());
|
||||||
|
|
||||||
// header 11 finalizes headers [10] AND schedules change
|
// header 11 finalizes headers [10] AND schedules change
|
||||||
// => we prune header#0
|
// => we prune header#0
|
||||||
let header = custom_block_i(&storage, 11, &validators, |header| {
|
let header11 = custom_block_i(&storage, 11, &validators, |header| {
|
||||||
header.log_bloom = (&[0xff; 256]).into();
|
header.log_bloom = (&[0xff; 256]).into();
|
||||||
header.receipts_root = "2e60346495092587026484e868a5b3063749032b2ea3843844509a6320d7f951"
|
header.receipts_root = "2e60346495092587026484e868a5b3063749032b2ea3843844509a6320d7f951"
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
});
|
});
|
||||||
last_block_hash = import_header(
|
let (rolling_last_block_hash, finalized_blocks) = import_header(
|
||||||
&mut storage,
|
&mut storage,
|
||||||
&kovan_aura_config(),
|
&kovan_aura_config(),
|
||||||
&validators_config,
|
&validators_config,
|
||||||
10,
|
10,
|
||||||
header,
|
Some(101),
|
||||||
|
header11.clone(),
|
||||||
Some(vec![crate::validators::tests::validators_change_recept(
|
Some(vec![crate::validators::tests::validators_change_recept(
|
||||||
last_block_hash,
|
latest_block_hash,
|
||||||
)]),
|
)]),
|
||||||
)
|
).unwrap();
|
||||||
.unwrap();
|
assert_eq!(
|
||||||
|
finalized_blocks,
|
||||||
|
vec![(10, block_i(&storage, 10, &validators).hash(), Some(100))],
|
||||||
|
);
|
||||||
assert!(storage.header(&genesis().hash()).is_none());
|
assert!(storage.header(&genesis().hash()).is_none());
|
||||||
|
latest_block_hash = rolling_last_block_hash;
|
||||||
|
|
||||||
// and now let's say validators 1 && 2 went offline
|
// 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
|
// => 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
|
// until header#11 is met. we can't prune #11, because it schedules change
|
||||||
let mut step = 56;
|
let mut step = 56;
|
||||||
|
let mut expected_blocks = vec![(11, header11.hash(), Some(101))];
|
||||||
for i in 12..25 {
|
for i in 12..25 {
|
||||||
let header = Header {
|
let header = Header {
|
||||||
number: i as _,
|
number: i as _,
|
||||||
parent_hash: last_block_hash,
|
parent_hash: latest_block_hash,
|
||||||
gas_limit: 0x2000.into(),
|
gas_limit: 0x2000.into(),
|
||||||
author: validator(2).address().to_fixed_bytes().into(),
|
author: validator(2).address().to_fixed_bytes().into(),
|
||||||
seal: vec![vec![step].into(), vec![].into()],
|
seal: vec![vec![step].into(), vec![].into()],
|
||||||
@@ -311,8 +365,22 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let header = signed_header(&validators, header, step as _);
|
let header = signed_header(&validators, header, step as _);
|
||||||
last_block_hash =
|
expected_blocks.push((i, header.hash(), Some(102)));
|
||||||
import_header(&mut storage, &kovan_aura_config(), &validators_config, 10, header, None).unwrap();
|
let (rolling_last_block_hash, finalized_blocks) =
|
||||||
|
import_header(
|
||||||
|
&mut storage,
|
||||||
|
&kovan_aura_config(),
|
||||||
|
&validators_config,
|
||||||
|
10,
|
||||||
|
Some(102),
|
||||||
|
header,
|
||||||
|
None,
|
||||||
|
).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
finalized_blocks,
|
||||||
|
vec![],
|
||||||
|
);
|
||||||
|
latest_block_hash = rolling_last_block_hash;
|
||||||
step += 3;
|
step += 3;
|
||||||
}
|
}
|
||||||
assert_eq!(storage.oldest_unpruned_block(), 11);
|
assert_eq!(storage.oldest_unpruned_block(), 11);
|
||||||
@@ -322,7 +390,7 @@ mod tests {
|
|||||||
step -= 2;
|
step -= 2;
|
||||||
let header = Header {
|
let header = Header {
|
||||||
number: 25,
|
number: 25,
|
||||||
parent_hash: last_block_hash,
|
parent_hash: latest_block_hash,
|
||||||
gas_limit: 0x2000.into(),
|
gas_limit: 0x2000.into(),
|
||||||
author: validator(0).address().to_fixed_bytes().into(),
|
author: validator(0).address().to_fixed_bytes().into(),
|
||||||
seal: vec![vec![step].into(), vec![].into()],
|
seal: vec![vec![step].into(), vec![].into()],
|
||||||
@@ -330,7 +398,16 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let header = signed_header(&validators, header, step as _);
|
let header = signed_header(&validators, header, step as _);
|
||||||
import_header(&mut storage, &kovan_aura_config(), &validators_config, 10, header, None).unwrap();
|
let (_, finalized_blocks) = import_header(
|
||||||
|
&mut storage,
|
||||||
|
&kovan_aura_config(),
|
||||||
|
&validators_config,
|
||||||
|
10,
|
||||||
|
Some(103),
|
||||||
|
header,
|
||||||
|
None,
|
||||||
|
).unwrap();
|
||||||
|
assert_eq!(finalized_blocks, expected_blocks);
|
||||||
assert_eq!(storage.oldest_unpruned_block(), 15);
|
assert_eq!(storage.oldest_unpruned_block(), 15);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,12 @@ use codec::{Decode, Encode};
|
|||||||
use frame_support::{decl_module, decl_storage};
|
use frame_support::{decl_module, decl_storage};
|
||||||
use primitives::{Address, Header, Receipt, H256, U256};
|
use primitives::{Address, Header, Receipt, H256, U256};
|
||||||
use sp_runtime::RuntimeDebug;
|
use sp_runtime::RuntimeDebug;
|
||||||
use sp_std::{iter::from_fn, prelude::*};
|
use sp_std::{
|
||||||
|
prelude::*,
|
||||||
|
cmp::Ord,
|
||||||
|
collections::btree_map::BTreeMap,
|
||||||
|
iter::from_fn,
|
||||||
|
};
|
||||||
use validators::{ValidatorsConfiguration, ValidatorsSource};
|
use validators::{ValidatorsConfiguration, ValidatorsSource};
|
||||||
|
|
||||||
pub use import::{header_import_requires_receipts, import_header};
|
pub use import::{header_import_requires_receipts, import_header};
|
||||||
@@ -70,7 +75,10 @@ pub struct AuraConfiguration {
|
|||||||
|
|
||||||
/// Block header as it is stored in the runtime storage.
|
/// Block header as it is stored in the runtime storage.
|
||||||
#[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug)]
|
#[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug)]
|
||||||
pub struct StoredHeader {
|
pub struct StoredHeader<Submitter> {
|
||||||
|
/// Submitter of this header. May be `None` if header has been submitted
|
||||||
|
/// using unsigned transaction.
|
||||||
|
pub submitter: Option<Submitter>,
|
||||||
/// The block header itself.
|
/// The block header itself.
|
||||||
pub header: Header,
|
pub header: Header,
|
||||||
/// Total difficulty of the chain.
|
/// Total difficulty of the chain.
|
||||||
@@ -85,9 +93,9 @@ pub struct StoredHeader {
|
|||||||
/// Header that we're importing.
|
/// Header that we're importing.
|
||||||
#[derive(RuntimeDebug)]
|
#[derive(RuntimeDebug)]
|
||||||
#[cfg_attr(test, derive(Clone, PartialEq))]
|
#[cfg_attr(test, derive(Clone, PartialEq))]
|
||||||
pub struct HeaderToImport {
|
pub struct HeaderToImport<Submitter> {
|
||||||
/// Header import context,
|
/// Header import context,
|
||||||
pub context: ImportContext,
|
pub context: ImportContext<Submitter>,
|
||||||
/// Should we consider this header as best?
|
/// Should we consider this header as best?
|
||||||
pub is_best: bool,
|
pub is_best: bool,
|
||||||
/// The hash of the header.
|
/// The hash of the header.
|
||||||
@@ -105,22 +113,25 @@ pub struct HeaderToImport {
|
|||||||
/// Header import context.
|
/// Header import context.
|
||||||
#[derive(RuntimeDebug)]
|
#[derive(RuntimeDebug)]
|
||||||
#[cfg_attr(test, derive(Clone, PartialEq))]
|
#[cfg_attr(test, derive(Clone, PartialEq))]
|
||||||
pub struct ImportContext {
|
pub struct ImportContext<Submitter> {
|
||||||
|
submitter: Option<Submitter>,
|
||||||
parent_header: Header,
|
parent_header: Header,
|
||||||
parent_total_difficulty: U256,
|
parent_total_difficulty: U256,
|
||||||
next_validators_set_id: u64,
|
next_validators_set_id: u64,
|
||||||
next_validators_set: (H256, Vec<Address>),
|
next_validators_set: (H256, Vec<Address>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImportContext {
|
impl<Submitter> ImportContext<Submitter> {
|
||||||
/// Create import context using passing parameters;
|
/// Create import context using passing parameters;
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
submitter: Option<Submitter>,
|
||||||
parent_header: Header,
|
parent_header: Header,
|
||||||
parent_total_difficulty: U256,
|
parent_total_difficulty: U256,
|
||||||
next_validators_set_id: u64,
|
next_validators_set_id: u64,
|
||||||
next_validators_set: (H256, Vec<Address>),
|
next_validators_set: (H256, Vec<Address>),
|
||||||
) -> Self {
|
) -> Self {
|
||||||
ImportContext {
|
ImportContext {
|
||||||
|
submitter,
|
||||||
parent_header,
|
parent_header,
|
||||||
parent_total_difficulty,
|
parent_total_difficulty,
|
||||||
next_validators_set_id,
|
next_validators_set_id,
|
||||||
@@ -128,6 +139,11 @@ impl ImportContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns reference to header submitter (if known).
|
||||||
|
pub fn submitter(&self) -> Option<&Submitter> {
|
||||||
|
self.submitter.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns reference to parent header.
|
/// Returns reference to parent header.
|
||||||
pub fn parent_header(&self) -> &Header {
|
pub fn parent_header(&self) -> &Header {
|
||||||
&self.parent_header
|
&self.parent_header
|
||||||
@@ -162,7 +178,7 @@ impl ImportContext {
|
|||||||
total_difficulty: U256,
|
total_difficulty: U256,
|
||||||
enacted_change: Option<Vec<Address>>,
|
enacted_change: Option<Vec<Address>>,
|
||||||
scheduled_change: Option<Vec<Address>>,
|
scheduled_change: Option<Vec<Address>>,
|
||||||
) -> HeaderToImport {
|
) -> HeaderToImport<Submitter> {
|
||||||
HeaderToImport {
|
HeaderToImport {
|
||||||
context: self,
|
context: self,
|
||||||
is_best,
|
is_best,
|
||||||
@@ -179,18 +195,27 @@ impl ImportContext {
|
|||||||
///
|
///
|
||||||
/// Storage modification must be discarded if block import has failed.
|
/// Storage modification must be discarded if block import has failed.
|
||||||
pub trait Storage {
|
pub trait Storage {
|
||||||
|
/// Header submitter identifier.
|
||||||
|
type Submitter: Clone + Ord;
|
||||||
|
|
||||||
/// Get best known block.
|
/// Get best known block.
|
||||||
fn best_block(&self) -> (u64, H256, U256);
|
fn best_block(&self) -> (u64, H256, U256);
|
||||||
/// Get last finalized block.
|
/// Get last finalized block.
|
||||||
fn finalized_block(&self) -> (u64, H256);
|
fn finalized_block(&self) -> (u64, H256);
|
||||||
/// Get imported header by its hash.
|
/// Get imported header by its hash.
|
||||||
fn header(&self, hash: &H256) -> Option<Header>;
|
///
|
||||||
|
/// Returns header and its submitter (if known).
|
||||||
|
fn header(&self, hash: &H256) -> Option<(Header, Option<Self::Submitter>)>;
|
||||||
/// Get header import context by parent header hash.
|
/// Get header import context by parent header hash.
|
||||||
fn import_context(&self, parent_hash: &H256) -> Option<ImportContext>;
|
fn import_context(
|
||||||
|
&self,
|
||||||
|
submitter: Option<Self::Submitter>,
|
||||||
|
parent_hash: &H256,
|
||||||
|
) -> Option<ImportContext<Self::Submitter>>;
|
||||||
/// Get new validators that are scheduled by given header.
|
/// Get new validators that are scheduled by given header.
|
||||||
fn scheduled_change(&self, hash: &H256) -> Option<Vec<Address>>;
|
fn scheduled_change(&self, hash: &H256) -> Option<Vec<Address>>;
|
||||||
/// Insert imported header.
|
/// Insert imported header.
|
||||||
fn insert_header(&mut self, header: HeaderToImport);
|
fn insert_header(&mut self, header: HeaderToImport<Self::Submitter>);
|
||||||
/// Finalize given block and prune all headers with number < prune_end.
|
/// Finalize given block and prune all headers with number < prune_end.
|
||||||
/// The headers in the pruning range could be either finalized, or not.
|
/// The headers in the pruning range could be either finalized, or not.
|
||||||
/// It is the storage duty to ensure that unfinalized headers that have
|
/// It is the storage duty to ensure that unfinalized headers that have
|
||||||
@@ -202,14 +227,25 @@ pub trait Storage {
|
|||||||
/// Decides whether the session should be ended.
|
/// Decides whether the session should be ended.
|
||||||
pub trait OnHeadersSubmitted<AccountId> {
|
pub trait OnHeadersSubmitted<AccountId> {
|
||||||
/// Called when valid headers have been submitted.
|
/// 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.
|
||||||
fn on_valid_headers_submitted(submitter: AccountId, useful: u64, useless: u64);
|
fn on_valid_headers_submitted(submitter: AccountId, useful: u64, useless: u64);
|
||||||
/// Called when invalid headers have been submitted.
|
/// Called when invalid headers have been submitted.
|
||||||
fn on_invalid_headers_submitted(submitter: AccountId);
|
fn on_invalid_headers_submitted(submitter: AccountId);
|
||||||
|
/// Called when earlier submitted headers have been finalized.
|
||||||
|
///
|
||||||
|
/// finalized is the number of headers that submitter has submitted and which
|
||||||
|
/// have been finalized.
|
||||||
|
fn on_valid_headers_finalized(submitter: AccountId, finalized: u64);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<AccountId> OnHeadersSubmitted<AccountId> for () {
|
impl<AccountId> OnHeadersSubmitted<AccountId> for () {
|
||||||
fn on_valid_headers_submitted(_submitter: AccountId, _useful: u64, _useless: u64) {}
|
fn on_valid_headers_submitted(_submitter: AccountId, _useful: u64, _useless: u64) {}
|
||||||
fn on_invalid_headers_submitted(_submitter: AccountId) {}
|
fn on_invalid_headers_submitted(_submitter: AccountId) {}
|
||||||
|
fn on_valid_headers_finalized(_submitter: AccountId, _finalized: u64) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The module configuration trait
|
/// The module configuration trait
|
||||||
@@ -228,14 +264,27 @@ decl_module! {
|
|||||||
/// enormous block production/import time.
|
/// enormous block production/import time.
|
||||||
pub fn import_headers(origin, headers_with_receipts: Vec<(Header, Option<Vec<Receipt>>)>) {
|
pub fn import_headers(origin, headers_with_receipts: Vec<(Header, Option<Vec<Receipt>>)>) {
|
||||||
let submitter = frame_system::ensure_signed(origin)?;
|
let submitter = frame_system::ensure_signed(origin)?;
|
||||||
|
let mut finalized_headers = BTreeMap::new();
|
||||||
let import_result = import::import_headers(
|
let import_result = import::import_headers(
|
||||||
&mut BridgeStorage,
|
&mut BridgeStorage::<T>::new(),
|
||||||
&kovan_aura_config(),
|
&kovan_aura_config(),
|
||||||
&kovan_validators_config(),
|
&kovan_validators_config(),
|
||||||
crate::import::PRUNE_DEPTH,
|
crate::import::PRUNE_DEPTH,
|
||||||
|
Some(submitter.clone()),
|
||||||
headers_with_receipts,
|
headers_with_receipts,
|
||||||
|
&mut finalized_headers,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// if we have finalized some headers, we will reward their submitters even
|
||||||
|
// if current submitter has provided some invalid headers
|
||||||
|
for (f_submitter, f_count) in finalized_headers {
|
||||||
|
T::OnHeadersSubmitted::on_valid_headers_finalized(
|
||||||
|
f_submitter,
|
||||||
|
f_count,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// now track/penalize current submitter for providing new headers
|
||||||
match import_result {
|
match import_result {
|
||||||
Ok((useful, useless)) =>
|
Ok((useful, useless)) =>
|
||||||
T::OnHeadersSubmitted::on_valid_headers_submitted(submitter, useful, useless),
|
T::OnHeadersSubmitted::on_valid_headers_submitted(submitter, useful, useless),
|
||||||
@@ -259,7 +308,7 @@ decl_storage! {
|
|||||||
/// Oldest unpruned block(s) number.
|
/// Oldest unpruned block(s) number.
|
||||||
OldestUnprunedBlock: u64;
|
OldestUnprunedBlock: u64;
|
||||||
/// Map of imported headers by hash.
|
/// Map of imported headers by hash.
|
||||||
Headers: map hasher(blake2_256) H256 => Option<StoredHeader>;
|
Headers: map hasher(blake2_256) H256 => Option<StoredHeader<T::AccountId>>;
|
||||||
/// Map of imported header hashes by number.
|
/// Map of imported header hashes by number.
|
||||||
HeadersByNumber: map hasher(blake2_256) u64 => Option<Vec<H256>>;
|
HeadersByNumber: map hasher(blake2_256) u64 => Option<Vec<H256>>;
|
||||||
/// The ID of next validator set.
|
/// The ID of next validator set.
|
||||||
@@ -293,7 +342,8 @@ decl_storage! {
|
|||||||
FinalizedBlock::put((config.initial_header.number, initial_hash));
|
FinalizedBlock::put((config.initial_header.number, initial_hash));
|
||||||
OldestUnprunedBlock::put(config.initial_header.number);
|
OldestUnprunedBlock::put(config.initial_header.number);
|
||||||
HeadersByNumber::insert(config.initial_header.number, vec![initial_hash]);
|
HeadersByNumber::insert(config.initial_header.number, vec![initial_hash]);
|
||||||
Headers::insert(initial_hash, StoredHeader {
|
Headers::<T>::insert(initial_hash, StoredHeader {
|
||||||
|
submitter: None,
|
||||||
header: config.initial_header.clone(),
|
header: config.initial_header.clone(),
|
||||||
total_difficulty: config.initial_difficulty,
|
total_difficulty: config.initial_difficulty,
|
||||||
next_validators_set_id: 0,
|
next_validators_set_id: 0,
|
||||||
@@ -310,25 +360,34 @@ impl<T: Trait> Module<T> {
|
|||||||
/// The caller should only submit `import_header` transaction that makes
|
/// The caller should only submit `import_header` transaction that makes
|
||||||
/// (or leads to making) other header the best one.
|
/// (or leads to making) other header the best one.
|
||||||
pub fn best_block() -> (u64, H256) {
|
pub fn best_block() -> (u64, H256) {
|
||||||
let (number, hash, _) = BridgeStorage.best_block();
|
let (number, hash, _) = BridgeStorage::<T>::new().best_block();
|
||||||
(number, hash)
|
(number, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the import of given block requires transactions receipts.
|
/// Returns true if the import of given block requires transactions receipts.
|
||||||
pub fn is_import_requires_receipts(header: Header) -> bool {
|
pub fn is_import_requires_receipts(header: Header) -> bool {
|
||||||
import::header_import_requires_receipts(&BridgeStorage, &kovan_validators_config(), &header)
|
import::header_import_requires_receipts(&BridgeStorage::<T>::new(), &kovan_validators_config(), &header)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if header is known to the runtime.
|
/// Returns true if header is known to the runtime.
|
||||||
pub fn is_known_block(hash: H256) -> bool {
|
pub fn is_known_block(hash: H256) -> bool {
|
||||||
BridgeStorage.header(&hash).is_some()
|
BridgeStorage::<T>::new().header(&hash).is_some()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runtime bridge storage.
|
/// Runtime bridge storage.
|
||||||
struct BridgeStorage;
|
#[derive(Default)]
|
||||||
|
struct BridgeStorage<T>(sp_std::marker::PhantomData<T>);
|
||||||
|
|
||||||
|
impl<T> BridgeStorage<T> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
BridgeStorage(sp_std::marker::PhantomData::<T>::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Trait> Storage for BridgeStorage<T> {
|
||||||
|
type Submitter = T::AccountId;
|
||||||
|
|
||||||
impl Storage for BridgeStorage {
|
|
||||||
fn best_block(&self) -> (u64, H256, U256) {
|
fn best_block(&self) -> (u64, H256, U256) {
|
||||||
BestBlock::get()
|
BestBlock::get()
|
||||||
}
|
}
|
||||||
@@ -337,16 +396,21 @@ impl Storage for BridgeStorage {
|
|||||||
FinalizedBlock::get()
|
FinalizedBlock::get()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn header(&self, hash: &H256) -> Option<Header> {
|
fn header(&self, hash: &H256) -> Option<(Header, Option<Self::Submitter>)> {
|
||||||
Headers::get(hash).map(|header| header.header)
|
Headers::<T>::get(hash).map(|header| (header.header, header.submitter))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn import_context(&self, parent_hash: &H256) -> Option<ImportContext> {
|
fn import_context(
|
||||||
Headers::get(parent_hash).map(|parent_header| {
|
&self,
|
||||||
|
submitter: Option<Self::Submitter>,
|
||||||
|
parent_hash: &H256,
|
||||||
|
) -> Option<ImportContext<Self::Submitter>> {
|
||||||
|
Headers::<T>::get(parent_hash).map(|parent_header| {
|
||||||
let (next_validators_set_start, next_validators) =
|
let (next_validators_set_start, next_validators) =
|
||||||
ValidatorsSets::get(parent_header.next_validators_set_id)
|
ValidatorsSets::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");
|
||||||
ImportContext {
|
ImportContext {
|
||||||
|
submitter,
|
||||||
parent_header: parent_header.header,
|
parent_header: parent_header.header,
|
||||||
parent_total_difficulty: parent_header.total_difficulty,
|
parent_total_difficulty: parent_header.total_difficulty,
|
||||||
next_validators_set_id: parent_header.next_validators_set_id,
|
next_validators_set_id: parent_header.next_validators_set_id,
|
||||||
@@ -359,7 +423,7 @@ impl Storage for BridgeStorage {
|
|||||||
ScheduledChanges::get(hash)
|
ScheduledChanges::get(hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_header(&mut self, header: HeaderToImport) {
|
fn insert_header(&mut self, header: HeaderToImport<Self::Submitter>) {
|
||||||
if header.is_best {
|
if header.is_best {
|
||||||
BestBlock::put((header.header.number, header.hash, header.total_difficulty));
|
BestBlock::put((header.header.number, header.hash, header.total_difficulty));
|
||||||
}
|
}
|
||||||
@@ -387,9 +451,10 @@ impl Storage for BridgeStorage {
|
|||||||
};
|
};
|
||||||
|
|
||||||
HeadersByNumber::append_or_insert(header.header.number, vec![header.hash]);
|
HeadersByNumber::append_or_insert(header.header.number, vec![header.hash]);
|
||||||
Headers::insert(
|
Headers::<T>::insert(
|
||||||
&header.hash,
|
&header.hash,
|
||||||
StoredHeader {
|
StoredHeader {
|
||||||
|
submitter: header.context.submitter,
|
||||||
header: header.header,
|
header: header.header,
|
||||||
total_difficulty: header.total_difficulty,
|
total_difficulty: header.total_difficulty,
|
||||||
next_validators_set_id,
|
next_validators_set_id,
|
||||||
@@ -426,7 +491,7 @@ impl Storage for BridgeStorage {
|
|||||||
|
|
||||||
// physically remove headers and (probably) obsolete validators sets
|
// physically remove headers and (probably) obsolete validators sets
|
||||||
for hash in blocks_at_number.into_iter().flat_map(|x| x) {
|
for hash in blocks_at_number.into_iter().flat_map(|x| x) {
|
||||||
let header = Headers::take(&hash);
|
let header = Headers::<T>::take(&hash);
|
||||||
ScheduledChanges::remove(hash);
|
ScheduledChanges::remove(hash);
|
||||||
if let Some(header) = header {
|
if let Some(header) = header {
|
||||||
ValidatorsSetsRc::mutate(header.next_validators_set_id, |rc| match *rc {
|
ValidatorsSetsRc::mutate(header.next_validators_set_id, |rc| match *rc {
|
||||||
@@ -570,19 +635,22 @@ pub fn kovan_validators_config() -> ValidatorsConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return iterator of given header ancestors.
|
/// Return iterator of given header ancestors.
|
||||||
pub(crate) fn ancestry<'a, S: Storage>(storage: &'a S, header: &Header) -> impl Iterator<Item = (H256, Header)> + 'a {
|
pub(crate) fn ancestry<'a, S: Storage>(
|
||||||
|
storage: &'a S,
|
||||||
|
header: &Header,
|
||||||
|
) -> impl Iterator<Item = (H256, Header, Option<S::Submitter>)> + 'a {
|
||||||
let mut parent_hash = header.parent_hash.clone();
|
let mut parent_hash = header.parent_hash.clone();
|
||||||
from_fn(move || {
|
from_fn(move || {
|
||||||
let header = storage.header(&parent_hash);
|
let header_and_submitter = storage.header(&parent_hash);
|
||||||
match header {
|
match header_and_submitter {
|
||||||
Some(header) => {
|
Some((header, submitter)) => {
|
||||||
if header.number == 0 {
|
if header.number == 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let hash = parent_hash.clone();
|
let hash = parent_hash.clone();
|
||||||
parent_hash = header.parent_hash.clone();
|
parent_hash = header.parent_hash.clone();
|
||||||
Some((hash, header))
|
Some((hash, header, submitter))
|
||||||
}
|
}
|
||||||
None => None,
|
None => None,
|
||||||
}
|
}
|
||||||
@@ -596,6 +664,8 @@ pub(crate) mod tests {
|
|||||||
use primitives::{rlp_encode, H520};
|
use primitives::{rlp_encode, H520};
|
||||||
use std::collections::{hash_map::Entry, HashMap};
|
use std::collections::{hash_map::Entry, HashMap};
|
||||||
|
|
||||||
|
pub type AccountId = u64;
|
||||||
|
|
||||||
pub fn genesis() -> Header {
|
pub fn genesis() -> Header {
|
||||||
Header {
|
Header {
|
||||||
seal: vec![vec![42].into(), vec![].into()],
|
seal: vec![vec![42].into(), vec![].into()],
|
||||||
@@ -651,7 +721,7 @@ pub(crate) mod tests {
|
|||||||
best_block: (u64, H256, U256),
|
best_block: (u64, H256, U256),
|
||||||
finalized_block: (u64, H256),
|
finalized_block: (u64, H256),
|
||||||
oldest_unpruned_block: u64,
|
oldest_unpruned_block: u64,
|
||||||
headers: HashMap<H256, StoredHeader>,
|
headers: HashMap<H256, StoredHeader<AccountId>>,
|
||||||
headers_by_number: HashMap<u64, Vec<H256>>,
|
headers_by_number: HashMap<u64, Vec<H256>>,
|
||||||
next_validators_set_id: u64,
|
next_validators_set_id: u64,
|
||||||
validators_sets: HashMap<u64, (H256, Vec<Address>)>,
|
validators_sets: HashMap<u64, (H256, Vec<Address>)>,
|
||||||
@@ -670,6 +740,7 @@ pub(crate) mod tests {
|
|||||||
headers: vec![(
|
headers: vec![(
|
||||||
hash,
|
hash,
|
||||||
StoredHeader {
|
StoredHeader {
|
||||||
|
submitter: None,
|
||||||
header: initial_header,
|
header: initial_header,
|
||||||
total_difficulty: 0.into(),
|
total_difficulty: 0.into(),
|
||||||
next_validators_set_id: 0,
|
next_validators_set_id: 0,
|
||||||
@@ -688,12 +759,14 @@ pub(crate) mod tests {
|
|||||||
self.oldest_unpruned_block
|
self.oldest_unpruned_block
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn stored_header(&self, hash: &H256) -> Option<&StoredHeader> {
|
pub(crate) fn stored_header(&self, hash: &H256) -> Option<&StoredHeader<AccountId>> {
|
||||||
self.headers.get(hash)
|
self.headers.get(hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Storage for InMemoryStorage {
|
impl Storage for InMemoryStorage {
|
||||||
|
type Submitter = AccountId;
|
||||||
|
|
||||||
fn best_block(&self) -> (u64, H256, U256) {
|
fn best_block(&self) -> (u64, H256, U256) {
|
||||||
self.best_block.clone()
|
self.best_block.clone()
|
||||||
}
|
}
|
||||||
@@ -702,15 +775,20 @@ pub(crate) mod tests {
|
|||||||
self.finalized_block.clone()
|
self.finalized_block.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn header(&self, hash: &H256) -> Option<Header> {
|
fn header(&self, hash: &H256) -> Option<(Header, Option<Self::Submitter>)> {
|
||||||
self.headers.get(hash).map(|header| header.header.clone())
|
self.headers.get(hash).map(|header| (header.header.clone(), header.submitter.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn import_context(&self, parent_hash: &H256) -> Option<ImportContext> {
|
fn import_context(
|
||||||
|
&self,
|
||||||
|
submitter: Option<Self::Submitter>,
|
||||||
|
parent_hash: &H256,
|
||||||
|
) -> Option<ImportContext<Self::Submitter>> {
|
||||||
self.headers.get(parent_hash).map(|parent_header| {
|
self.headers.get(parent_hash).map(|parent_header| {
|
||||||
let (next_validators_set_start, next_validators) =
|
let (next_validators_set_start, next_validators) =
|
||||||
self.validators_sets.get(&parent_header.next_validators_set_id).unwrap();
|
self.validators_sets.get(&parent_header.next_validators_set_id).unwrap();
|
||||||
ImportContext {
|
ImportContext {
|
||||||
|
submitter,
|
||||||
parent_header: parent_header.header.clone(),
|
parent_header: parent_header.header.clone(),
|
||||||
parent_total_difficulty: parent_header.total_difficulty,
|
parent_total_difficulty: parent_header.total_difficulty,
|
||||||
next_validators_set_id: parent_header.next_validators_set_id,
|
next_validators_set_id: parent_header.next_validators_set_id,
|
||||||
@@ -723,7 +801,7 @@ pub(crate) mod tests {
|
|||||||
self.scheduled_changes.get(hash).cloned()
|
self.scheduled_changes.get(hash).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_header(&mut self, header: HeaderToImport) {
|
fn insert_header(&mut self, header: HeaderToImport<Self::Submitter>) {
|
||||||
if header.is_best {
|
if header.is_best {
|
||||||
self.best_block = (header.header.number, header.hash, header.total_difficulty);
|
self.best_block = (header.header.number, header.hash, header.total_difficulty);
|
||||||
}
|
}
|
||||||
@@ -755,6 +833,7 @@ pub(crate) mod tests {
|
|||||||
self.headers.insert(
|
self.headers.insert(
|
||||||
header.hash,
|
header.hash,
|
||||||
StoredHeader {
|
StoredHeader {
|
||||||
|
submitter: header.context.submitter,
|
||||||
header: header.header,
|
header: header.header,
|
||||||
total_difficulty: header.total_difficulty,
|
total_difficulty: header.total_difficulty,
|
||||||
next_validators_set_id,
|
next_validators_set_id,
|
||||||
|
|||||||
@@ -198,9 +198,9 @@ impl<'a> Validators<'a> {
|
|||||||
pub fn finalize_validators_change<S: Storage>(
|
pub fn finalize_validators_change<S: Storage>(
|
||||||
&self,
|
&self,
|
||||||
storage: &mut S,
|
storage: &mut S,
|
||||||
finalized_blocks: &[(u64, H256)],
|
finalized_blocks: &[(u64, H256, Option<S::Submitter>)],
|
||||||
) -> Option<Vec<Address>> {
|
) -> Option<Vec<Address>> {
|
||||||
for (_, finalized_hash) in finalized_blocks.iter().rev() {
|
for (_, finalized_hash, _) in finalized_blocks.iter().rev() {
|
||||||
if let Some(changes) = storage.scheduled_change(finalized_hash) {
|
if let Some(changes) = storage.scheduled_change(finalized_hash) {
|
||||||
return Some(changes);
|
return Some(changes);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,14 +40,15 @@ use sp_io::crypto::secp256k1_ecdsa_recover;
|
|||||||
pub fn verify_aura_header<S: Storage>(
|
pub fn verify_aura_header<S: Storage>(
|
||||||
storage: &S,
|
storage: &S,
|
||||||
params: &AuraConfiguration,
|
params: &AuraConfiguration,
|
||||||
|
submitter: Option<S::Submitter>,
|
||||||
header: &Header,
|
header: &Header,
|
||||||
) -> Result<ImportContext, Error> {
|
) -> Result<ImportContext<S::Submitter>, Error> {
|
||||||
// let's do the lightest check first
|
// let's do the lightest check first
|
||||||
contextless_checks(params, header)?;
|
contextless_checks(params, header)?;
|
||||||
|
|
||||||
// the rest of checks requires parent
|
// the rest of checks requires parent
|
||||||
let context = storage
|
let context = storage
|
||||||
.import_context(&header.parent_hash)
|
.import_context(submitter, &header.parent_hash)
|
||||||
.ok_or(Error::MissingParentBlock)?;
|
.ok_or(Error::MissingParentBlock)?;
|
||||||
let validators = context.validators();
|
let validators = context.validators();
|
||||||
let header_step = header.step().ok_or(Error::MissingStep)?;
|
let header_step = header.step().ok_or(Error::MissingStep)?;
|
||||||
@@ -179,7 +180,7 @@ fn verify_signature(expected_validator: &Address, signature: &H520, message: &H2
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::kovan_aura_config;
|
use crate::kovan_aura_config;
|
||||||
use crate::tests::{genesis, signed_header, validator, validators_addresses, InMemoryStorage};
|
use crate::tests::{genesis, signed_header, validator, validators_addresses, AccountId, InMemoryStorage};
|
||||||
use parity_crypto::publickey::{sign, KeyPair};
|
use parity_crypto::publickey::{sign, KeyPair};
|
||||||
use primitives::{rlp_encode, H520};
|
use primitives::{rlp_encode, H520};
|
||||||
|
|
||||||
@@ -197,12 +198,15 @@ mod tests {
|
|||||||
empty_step
|
empty_step
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_with_config(config: &AuraConfiguration, header: &Header) -> Result<ImportContext, Error> {
|
fn verify_with_config(
|
||||||
|
config: &AuraConfiguration,
|
||||||
|
header: &Header,
|
||||||
|
) -> Result<ImportContext<AccountId>, Error> {
|
||||||
let storage = InMemoryStorage::new(genesis(), validators_addresses(3));
|
let storage = InMemoryStorage::new(genesis(), validators_addresses(3));
|
||||||
verify_aura_header(&storage, &config, header)
|
verify_aura_header(&storage, &config, None, header)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_verify(header: &Header) -> Result<ImportContext, Error> {
|
fn default_verify(header: &Header) -> Result<ImportContext<AccountId>, Error> {
|
||||||
verify_with_config(&kovan_aura_config(), header)
|
verify_with_config(&kovan_aura_config(), header)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user