Commit d297bbac authored by Stanislav Tkach's avatar Stanislav Tkach Committed by Gavin Wood
Browse files

Pass indices in serialized form (#318)

* Pass indices in serialized form

* Fix indentation and remove panic

* Fix tests and other code

* Remove unique voters tracking

* Restore validator group check

* Fix lock file

* Add test

* Add attestation sorting

* Add validation to the check_candidate function

* Update codec version one more time

* Remove patch versions
parent a8502968
Pipeline #48142 passed with stages
in 15 minutes and 16 seconds
This diff is collapsed.
...@@ -6,13 +6,14 @@ edition = "2018" ...@@ -6,13 +6,14 @@ edition = "2018"
[dependencies] [dependencies]
serde = { version = "1.0", optional = true, features = ["derive"] } serde = { version = "1.0", optional = true, features = ["derive"] }
parity-codec = { version = "4.1", default-features = false } parity-codec = { version = "4.1", default-features = false, features = ["bit-vec"] }
primitives = { package = "substrate-primitives", git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-master" } primitives = { package = "substrate-primitives", git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-master" }
substrate-client = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-master" } substrate-client = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-master" }
sr-version = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-master" } sr-version = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-master" }
rstd = { package = "sr-std", git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-master" } rstd = { package = "sr-std", git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-master" }
runtime_primitives = { package = "sr-primitives", git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-master" } runtime_primitives = { package = "sr-primitives", git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-master" }
polkadot-parachain = { path = "../parachain", default-features = false } polkadot-parachain = { path = "../parachain", default-features = false }
bitvec = { version = "0.11", default-features = false, features = ["alloc"] }
[dev-dependencies] [dev-dependencies]
substrate-serializer = { git = "https://github.com/paritytech/substrate", branch = "polkadot-master" } substrate-serializer = { git = "https://github.com/paritytech/substrate", branch = "polkadot-master" }
...@@ -29,4 +30,5 @@ std = [ ...@@ -29,4 +30,5 @@ std = [
"runtime_primitives/std", "runtime_primitives/std",
"serde", "serde",
"polkadot-parachain/std", "polkadot-parachain/std",
"bitvec/std"
] ]
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
use rstd::prelude::*; use rstd::prelude::*;
use rstd::cmp::Ordering; use rstd::cmp::Ordering;
use parity_codec::{Encode, Decode}; use parity_codec::{Encode, Decode};
use bitvec::vec::BitVec;
use super::{Hash, Balance, BlockNumber}; use super::{Hash, Balance, BlockNumber};
#[cfg(feature = "std")] #[cfg(feature = "std")]
...@@ -305,7 +306,9 @@ pub struct AttestedCandidate { ...@@ -305,7 +306,9 @@ pub struct AttestedCandidate {
/// The candidate data. /// The candidate data.
pub candidate: CandidateReceipt, pub candidate: CandidateReceipt,
/// Validity attestations. /// Validity attestations.
pub validity_votes: Vec<(ValidatorIndex, ValidityAttestation)>, pub validity_votes: Vec<ValidityAttestation>,
/// Indices of the corresponding validity votes.
pub validator_indices: BitVec,
} }
impl AttestedCandidate { impl AttestedCandidate {
......
...@@ -6,7 +6,7 @@ edition = "2018" ...@@ -6,7 +6,7 @@ edition = "2018"
build = "build.rs" build = "build.rs"
[dependencies] [dependencies]
bitvec = { version = "0.8", default-features = false, features = ["alloc"] } bitvec = { version = "0.11", default-features = false, features = ["alloc"] }
rustc-hex = { version = "2.0.1", default-features = false } rustc-hex = { version = "2.0.1", default-features = false }
log = { version = "0.3", optional = true } log = { version = "0.3", optional = true }
serde = { version = "1.0", default-features = false } serde = { version = "1.0", default-features = false }
......
...@@ -21,7 +21,6 @@ use rstd::collections::btree_map::BTreeMap; ...@@ -21,7 +21,6 @@ use rstd::collections::btree_map::BTreeMap;
use parity_codec::{Decode, HasCompact}; use parity_codec::{Decode, HasCompact};
use srml_support::{decl_storage, decl_module, fail, ensure}; use srml_support::{decl_storage, decl_module, fail, ensure};
use bitvec::{bitvec, BigEndian};
use sr_primitives::traits::{Hash as HashT, BlakeTwo256, Member, CheckedConversion, Saturating, One}; use sr_primitives::traits::{Hash as HashT, BlakeTwo256, Member, CheckedConversion, Saturating, One};
use sr_primitives::weights::SimpleDispatchInfo; use sr_primitives::weights::SimpleDispatchInfo;
use primitives::{Hash, Balance, parachain::{ use primitives::{Hash, Balance, parachain::{
...@@ -726,6 +725,11 @@ impl<T: Trait> Module<T> { ...@@ -726,6 +725,11 @@ impl<T: Trait> Module<T> {
"Not enough validity attestations" "Not enough validity attestations"
); );
ensure!(
candidate.validity_votes.len() <= authorities.len(),
"The number of attestations exceeds the number of authorities"
);
let fees = candidate.candidate().fees; let fees = candidate.candidate().fees;
T::ParachainCurrency::deduct(para_id, fees)?; T::ParachainCurrency::deduct(para_id, fees)?;
...@@ -733,19 +737,15 @@ impl<T: Trait> Module<T> { ...@@ -733,19 +737,15 @@ impl<T: Trait> Module<T> {
let mut encoded_implicit = None; let mut encoded_implicit = None;
let mut encoded_explicit = None; let mut encoded_explicit = None;
// track which voters have voted already, 1 bit per authority. for ((auth_index, _), validity_attestation) in candidate.validator_indices
let mut track_voters = bitvec![0; authorities.len()]; .iter()
for (auth_index, validity_attestation) in &candidate.validity_votes { .enumerate()
let auth_index = *auth_index as usize; .filter(|(_, bit)| *bit)
// protect against double-votes. .zip(candidate.validity_votes.iter())
match validator_group.iter().find(|&(idx, _)| *idx == auth_index) { {
None => return Err("Attesting validator not on this chain's validation duty."),
Some(&(idx, _)) => { if validator_group.iter().find(|&(idx, _)| *idx == auth_index).is_none() {
if track_voters.get(idx) { return Err("Attesting validator not on this chain's validation duty.");
return Err("Voter already attested validity once")
}
track_voters.set(idx, true)
}
} }
let (payload, sig) = match validity_attestation { let (payload, sig) = match validity_attestation {
...@@ -818,6 +818,7 @@ impl<T: Trait> ProvideInherent for Module<T> { ...@@ -818,6 +818,7 @@ impl<T: Trait> ProvideInherent for Module<T> {
mod tests { mod tests {
use super::*; use super::*;
use super::Call as ParachainsCall; use super::Call as ParachainsCall;
use bitvec::{bitvec, vec::BitVec};
use sr_io::{TestExternalities, with_externalities}; use sr_io::{TestExternalities, with_externalities};
use substrate_primitives::{H256, Blake2Hasher}; use substrate_primitives::{H256, Blake2Hasher};
use substrate_trie::NodeCodec; use substrate_trie::NodeCodec;
...@@ -827,7 +828,7 @@ mod tests { ...@@ -827,7 +828,7 @@ mod tests {
testing::{UintAuthorityId, Header}, testing::{UintAuthorityId, Header},
}; };
use primitives::{ use primitives::{
parachain::{CandidateReceipt, HeadData, ValidityAttestation, ValidatorIndex}, SessionKey, parachain::{CandidateReceipt, HeadData, ValidityAttestation}, SessionKey,
BlockNumber, AuraId, BlockNumber, AuraId,
}; };
use keyring::Ed25519Keyring; use keyring::Ed25519Keyring;
...@@ -1004,6 +1005,7 @@ mod tests { ...@@ -1004,6 +1005,7 @@ mod tests {
let validation_entries = duty_roster.validator_duty.iter() let validation_entries = duty_roster.validator_duty.iter()
.enumerate(); .enumerate();
let mut validator_indices = BitVec::new();
for (idx, &duty) in validation_entries { for (idx, &duty) in validation_entries {
if duty != Chain::Parachain(candidate.parachain_index()) { continue } if duty != Chain::Parachain(candidate.parachain_index()) { continue }
vote_implicit = !vote_implicit; vote_implicit = !vote_implicit;
...@@ -1019,17 +1021,24 @@ mod tests { ...@@ -1019,17 +1021,24 @@ mod tests {
let payload = localized_payload(statement, parent_hash); let payload = localized_payload(statement, parent_hash);
let signature = key.sign(&payload[..]).into(); let signature = key.sign(&payload[..]).into();
candidate.validity_votes.push((idx as ValidatorIndex, if vote_implicit { candidate.validity_votes.push(if vote_implicit {
ValidityAttestation::Implicit(signature) ValidityAttestation::Implicit(signature)
} else { } else {
ValidityAttestation::Explicit(signature) ValidityAttestation::Explicit(signature)
})); });
if validator_indices.len() <= idx {
validator_indices.resize(idx + 1, false);
}
validator_indices.set(idx, true);
} }
candidate.validator_indices = validator_indices;
} }
fn new_candidate_with_egress_roots(egress_queue_roots: Vec<(ParaId, H256)>) -> AttestedCandidate { fn new_candidate_with_egress_roots(egress_queue_roots: Vec<(ParaId, H256)>) -> AttestedCandidate {
AttestedCandidate { AttestedCandidate {
validity_votes: vec![], validity_votes: vec![],
validator_indices: BitVec::new(),
candidate: CandidateReceipt { candidate: CandidateReceipt {
parachain_index: 0.into(), parachain_index: 0.into(),
collator: Default::default(), collator: Default::default(),
...@@ -1049,6 +1058,7 @@ mod tests { ...@@ -1049,6 +1058,7 @@ mod tests {
) -> AttestedCandidate { ) -> AttestedCandidate {
AttestedCandidate { AttestedCandidate {
validity_votes: vec![], validity_votes: vec![],
validator_indices: BitVec::new(),
candidate: CandidateReceipt { candidate: CandidateReceipt {
parachain_index: id.into(), parachain_index: id.into(),
collator: Default::default(), collator: Default::default(),
...@@ -1411,6 +1421,7 @@ mod tests { ...@@ -1411,6 +1421,7 @@ mod tests {
with_externalities(&mut new_test_ext(parachains), || { with_externalities(&mut new_test_ext(parachains), || {
let candidate = AttestedCandidate { let candidate = AttestedCandidate {
validity_votes: vec![], validity_votes: vec![],
validator_indices: BitVec::new(),
candidate: CandidateReceipt { candidate: CandidateReceipt {
parachain_index: 0.into(), parachain_index: 0.into(),
collator: Default::default(), collator: Default::default(),
...@@ -1438,6 +1449,7 @@ mod tests { ...@@ -1438,6 +1449,7 @@ mod tests {
with_externalities(&mut new_test_ext(parachains), || { with_externalities(&mut new_test_ext(parachains), || {
let mut candidate_a = AttestedCandidate { let mut candidate_a = AttestedCandidate {
validity_votes: vec![], validity_votes: vec![],
validator_indices: BitVec::new(),
candidate: CandidateReceipt { candidate: CandidateReceipt {
parachain_index: 0.into(), parachain_index: 0.into(),
collator: Default::default(), collator: Default::default(),
...@@ -1452,6 +1464,7 @@ mod tests { ...@@ -1452,6 +1464,7 @@ mod tests {
let mut candidate_b = AttestedCandidate { let mut candidate_b = AttestedCandidate {
validity_votes: vec![], validity_votes: vec![],
validator_indices: BitVec::new(),
candidate: CandidateReceipt { candidate: CandidateReceipt {
parachain_index: 1.into(), parachain_index: 1.into(),
collator: Default::default(), collator: Default::default(),
...@@ -1489,6 +1502,7 @@ mod tests { ...@@ -1489,6 +1502,7 @@ mod tests {
with_externalities(&mut new_test_ext(parachains), || { with_externalities(&mut new_test_ext(parachains), || {
let mut candidate = AttestedCandidate { let mut candidate = AttestedCandidate {
validity_votes: vec![], validity_votes: vec![],
validator_indices: BitVec::new(),
candidate: CandidateReceipt { candidate: CandidateReceipt {
parachain_index: 0.into(), parachain_index: 0.into(),
collator: Default::default(), collator: Default::default(),
...@@ -1505,6 +1519,7 @@ mod tests { ...@@ -1505,6 +1519,7 @@ mod tests {
let mut double_validity = candidate.clone(); let mut double_validity = candidate.clone();
double_validity.validity_votes.push(candidate.validity_votes[0].clone()); double_validity.validity_votes.push(candidate.validity_votes[0].clone());
double_validity.validator_indices.push(true);
assert!(Parachains::dispatch( assert!(Parachains::dispatch(
set_heads(vec![double_validity]), set_heads(vec![double_validity]),
...@@ -1513,6 +1528,42 @@ mod tests { ...@@ -1513,6 +1528,42 @@ mod tests {
}); });
} }
#[test]
fn validators_not_from_group_is_rejected() {
let parachains = vec![
(0u32.into(), vec![], vec![]),
(1u32.into(), vec![], vec![]),
];
with_externalities(&mut new_test_ext(parachains), || {
let mut candidate = AttestedCandidate {
validity_votes: vec![],
validator_indices: BitVec::new(),
candidate: CandidateReceipt {
parachain_index: 0.into(),
collator: Default::default(),
signature: Default::default(),
head_data: HeadData(vec![1, 2, 3]),
egress_queue_roots: vec![],
fees: 0,
block_data_hash: Default::default(),
upward_messages: vec![],
}
};
make_attestations(&mut candidate);
// Change the last vote index to make it not corresponding to the assigned group.
assert!(candidate.validator_indices.pop().is_some());
candidate.validator_indices.append(&mut bitvec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
assert!(Parachains::dispatch(
set_heads(vec![candidate]),
Origin::NONE,
).is_err());
});
}
#[test] #[test]
fn ingress_works() { fn ingress_works() {
use sr_primitives::traits::OnFinalize; use sr_primitives::traits::OnFinalize;
...@@ -1533,6 +1584,7 @@ mod tests { ...@@ -1533,6 +1584,7 @@ mod tests {
let from_a = vec![(1.into(), [i as u8; 32].into())]; let from_a = vec![(1.into(), [i as u8; 32].into())];
let mut candidate_a = AttestedCandidate { let mut candidate_a = AttestedCandidate {
validity_votes: vec![], validity_votes: vec![],
validator_indices: BitVec::new(),
candidate: CandidateReceipt { candidate: CandidateReceipt {
parachain_index: 0.into(), parachain_index: 0.into(),
collator: Default::default(), collator: Default::default(),
...@@ -1548,6 +1600,7 @@ mod tests { ...@@ -1548,6 +1600,7 @@ mod tests {
let from_b = vec![(99.into(), [i as u8; 32].into())]; let from_b = vec![(99.into(), [i as u8; 32].into())];
let mut candidate_b = AttestedCandidate { let mut candidate_b = AttestedCandidate {
validity_votes: vec![], validity_votes: vec![],
validator_indices: BitVec::new(),
candidate: CandidateReceipt { candidate: CandidateReceipt {
parachain_index: 1.into(), parachain_index: 1.into(),
collator: Default::default(), collator: Default::default(),
...@@ -1611,6 +1664,7 @@ mod tests { ...@@ -1611,6 +1664,7 @@ mod tests {
let mut candidate_c = AttestedCandidate { let mut candidate_c = AttestedCandidate {
validity_votes: vec![], validity_votes: vec![],
validator_indices: BitVec::new(),
candidate: CandidateReceipt { candidate: CandidateReceipt {
parachain_index: 99.into(), parachain_index: 99.into(),
collator: Default::default(), collator: Default::default(),
......
...@@ -30,6 +30,7 @@ runtime_aura = { package = "srml-aura", git = "https://github.com/paritytech/sub ...@@ -30,6 +30,7 @@ runtime_aura = { package = "srml-aura", git = "https://github.com/paritytech/sub
client = { package = "substrate-client", git = "https://github.com/paritytech/substrate", branch = "polkadot-master" } client = { package = "substrate-client", git = "https://github.com/paritytech/substrate", branch = "polkadot-master" }
trie = { package = "substrate-trie", git = "https://github.com/paritytech/substrate", branch = "polkadot-master" } trie = { package = "substrate-trie", git = "https://github.com/paritytech/substrate", branch = "polkadot-master" }
runtime_primitives = { package = "sr-primitives", git = "https://github.com/paritytech/substrate", branch = "polkadot-master" } runtime_primitives = { package = "sr-primitives", git = "https://github.com/paritytech/substrate", branch = "polkadot-master" }
bitvec = { version = "0.11", default-features = false, features = ["alloc"] }
[dev-dependencies] [dev-dependencies]
substrate-keyring = { git = "https://github.com/paritytech/substrate", branch = "polkadot-master" } substrate-keyring = { git = "https://github.com/paritytech/substrate", branch = "polkadot-master" }
...@@ -30,6 +30,7 @@ use polkadot_primitives::parachain::{Id as ParaId, Collation, Extrinsic, Candida ...@@ -30,6 +30,7 @@ use polkadot_primitives::parachain::{Id as ParaId, Collation, Extrinsic, Candida
use parking_lot::Mutex; use parking_lot::Mutex;
use futures::prelude::*; use futures::prelude::*;
use log::{warn, debug}; use log::{warn, debug};
use bitvec::bitvec;
use super::{GroupInfo, TableRouter}; use super::{GroupInfo, TableRouter};
use self::includable::IncludabilitySender; use self::includable::IncludabilitySender;
...@@ -519,14 +520,26 @@ impl SharedTable { ...@@ -519,14 +520,26 @@ impl SharedTable {
// aggregation in the future. // aggregation in the future.
let table_attestations = self.inner.lock().table.proposed_candidates(&*self.context); let table_attestations = self.inner.lock().table.proposed_candidates(&*self.context);
table_attestations.into_iter() table_attestations.into_iter()
.map(|attested| AttestedCandidate { .map(|attested| {
candidate: attested.candidate, let mut validity_votes: Vec<_> = attested.validity_votes.into_iter().map(|(id, a)| {
validity_votes: attested.validity_votes.into_iter().map(|(a, v)| match v { (id as usize, match a {
GAttestation::Implicit(s) => (a, ValidityAttestation::Implicit(s)), GAttestation::Implicit(s) => ValidityAttestation::Implicit(s),
GAttestation::Explicit(s) => (a, ValidityAttestation::Explicit(s)), GAttestation::Explicit(s) => ValidityAttestation::Explicit(s),
}).collect(),
}) })
.collect() }).collect();
validity_votes.sort_by(|(id1, _), (id2, _)| id1.cmp(id2));
let mut validator_indices = bitvec![0; validity_votes.last().map(|(i, _)| i + 1).unwrap_or_default()];
for (id, _) in &validity_votes {
validator_indices.set(*id, true);
}
AttestedCandidate {
candidate: attested.candidate,
validity_votes: validity_votes.into_iter().map(|(_, a)| a).collect(),
validator_indices,
}
}).collect()
} }
/// Get the number of total parachains. /// Get the number of total parachains.
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment