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"
[dependencies]
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" }
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" }
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" }
polkadot-parachain = { path = "../parachain", default-features = false }
bitvec = { version = "0.11", default-features = false, features = ["alloc"] }
[dev-dependencies]
substrate-serializer = { git = "https://github.com/paritytech/substrate", branch = "polkadot-master" }
......@@ -29,4 +30,5 @@ std = [
"runtime_primitives/std",
"serde",
"polkadot-parachain/std",
"bitvec/std"
]
......@@ -19,6 +19,7 @@
use rstd::prelude::*;
use rstd::cmp::Ordering;
use parity_codec::{Encode, Decode};
use bitvec::vec::BitVec;
use super::{Hash, Balance, BlockNumber};
#[cfg(feature = "std")]
......@@ -305,7 +306,9 @@ pub struct AttestedCandidate {
/// The candidate data.
pub candidate: CandidateReceipt,
/// 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 {
......
......@@ -6,7 +6,7 @@ edition = "2018"
build = "build.rs"
[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 }
log = { version = "0.3", optional = true }
serde = { version = "1.0", default-features = false }
......
......@@ -21,7 +21,6 @@ use rstd::collections::btree_map::BTreeMap;
use parity_codec::{Decode, HasCompact};
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::weights::SimpleDispatchInfo;
use primitives::{Hash, Balance, parachain::{
......@@ -726,6 +725,11 @@ impl<T: Trait> Module<T> {
"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;
T::ParachainCurrency::deduct(para_id, fees)?;
......@@ -733,19 +737,15 @@ impl<T: Trait> Module<T> {
let mut encoded_implicit = None;
let mut encoded_explicit = None;
// track which voters have voted already, 1 bit per authority.
let mut track_voters = bitvec![0; authorities.len()];
for (auth_index, validity_attestation) in &candidate.validity_votes {
let auth_index = *auth_index as usize;
// protect against double-votes.
match validator_group.iter().find(|&(idx, _)| *idx == auth_index) {
None => return Err("Attesting validator not on this chain's validation duty."),
Some(&(idx, _)) => {
if track_voters.get(idx) {
return Err("Voter already attested validity once")
}
track_voters.set(idx, true)
}
for ((auth_index, _), validity_attestation) in candidate.validator_indices
.iter()
.enumerate()
.filter(|(_, bit)| *bit)
.zip(candidate.validity_votes.iter())
{
if validator_group.iter().find(|&(idx, _)| *idx == auth_index).is_none() {
return Err("Attesting validator not on this chain's validation duty.");
}
let (payload, sig) = match validity_attestation {
......@@ -818,6 +818,7 @@ impl<T: Trait> ProvideInherent for Module<T> {
mod tests {
use super::*;
use super::Call as ParachainsCall;
use bitvec::{bitvec, vec::BitVec};
use sr_io::{TestExternalities, with_externalities};
use substrate_primitives::{H256, Blake2Hasher};
use substrate_trie::NodeCodec;
......@@ -827,7 +828,7 @@ mod tests {
testing::{UintAuthorityId, Header},
};
use primitives::{
parachain::{CandidateReceipt, HeadData, ValidityAttestation, ValidatorIndex}, SessionKey,
parachain::{CandidateReceipt, HeadData, ValidityAttestation}, SessionKey,
BlockNumber, AuraId,
};
use keyring::Ed25519Keyring;
......@@ -1004,6 +1005,7 @@ mod tests {
let validation_entries = duty_roster.validator_duty.iter()
.enumerate();
let mut validator_indices = BitVec::new();
for (idx, &duty) in validation_entries {
if duty != Chain::Parachain(candidate.parachain_index()) { continue }
vote_implicit = !vote_implicit;
......@@ -1019,17 +1021,24 @@ mod tests {
let payload = localized_payload(statement, parent_hash);
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)
} else {
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 {
AttestedCandidate {
validity_votes: vec![],
validator_indices: BitVec::new(),
candidate: CandidateReceipt {
parachain_index: 0.into(),
collator: Default::default(),
......@@ -1049,6 +1058,7 @@ mod tests {
) -> AttestedCandidate {
AttestedCandidate {
validity_votes: vec![],
validator_indices: BitVec::new(),
candidate: CandidateReceipt {
parachain_index: id.into(),
collator: Default::default(),
......@@ -1411,6 +1421,7 @@ mod tests {
with_externalities(&mut new_test_ext(parachains), || {
let candidate = AttestedCandidate {
validity_votes: vec![],
validator_indices: BitVec::new(),
candidate: CandidateReceipt {
parachain_index: 0.into(),
collator: Default::default(),
......@@ -1438,6 +1449,7 @@ mod tests {
with_externalities(&mut new_test_ext(parachains), || {
let mut candidate_a = AttestedCandidate {
validity_votes: vec![],
validator_indices: BitVec::new(),
candidate: CandidateReceipt {
parachain_index: 0.into(),
collator: Default::default(),
......@@ -1452,6 +1464,7 @@ mod tests {
let mut candidate_b = AttestedCandidate {
validity_votes: vec![],
validator_indices: BitVec::new(),
candidate: CandidateReceipt {
parachain_index: 1.into(),
collator: Default::default(),
......@@ -1489,6 +1502,7 @@ mod tests {
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(),
......@@ -1505,6 +1519,7 @@ mod tests {
let mut double_validity = candidate.clone();
double_validity.validity_votes.push(candidate.validity_votes[0].clone());
double_validity.validator_indices.push(true);
assert!(Parachains::dispatch(
set_heads(vec![double_validity]),
......@@ -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]
fn ingress_works() {
use sr_primitives::traits::OnFinalize;
......@@ -1533,6 +1584,7 @@ mod tests {
let from_a = vec![(1.into(), [i as u8; 32].into())];
let mut candidate_a = AttestedCandidate {
validity_votes: vec![],
validator_indices: BitVec::new(),
candidate: CandidateReceipt {
parachain_index: 0.into(),
collator: Default::default(),
......@@ -1548,6 +1600,7 @@ mod tests {
let from_b = vec![(99.into(), [i as u8; 32].into())];
let mut candidate_b = AttestedCandidate {
validity_votes: vec![],
validator_indices: BitVec::new(),
candidate: CandidateReceipt {
parachain_index: 1.into(),
collator: Default::default(),
......@@ -1611,6 +1664,7 @@ mod tests {
let mut candidate_c = AttestedCandidate {
validity_votes: vec![],
validator_indices: BitVec::new(),
candidate: CandidateReceipt {
parachain_index: 99.into(),
collator: Default::default(),
......
......@@ -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" }
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" }
bitvec = { version = "0.11", default-features = false, features = ["alloc"] }
[dev-dependencies]
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
use parking_lot::Mutex;
use futures::prelude::*;
use log::{warn, debug};
use bitvec::bitvec;
use super::{GroupInfo, TableRouter};
use self::includable::IncludabilitySender;
......@@ -519,14 +520,26 @@ impl SharedTable {
// aggregation in the future.
let table_attestations = self.inner.lock().table.proposed_candidates(&*self.context);
table_attestations.into_iter()
.map(|attested| AttestedCandidate {
candidate: attested.candidate,
validity_votes: attested.validity_votes.into_iter().map(|(a, v)| match v {
GAttestation::Implicit(s) => (a, ValidityAttestation::Implicit(s)),
GAttestation::Explicit(s) => (a, ValidityAttestation::Explicit(s)),
}).collect(),
.map(|attested| {
let mut validity_votes: Vec<_> = attested.validity_votes.into_iter().map(|(id, a)| {
(id as usize, match a {
GAttestation::Implicit(s) => ValidityAttestation::Implicit(s),
GAttestation::Explicit(s) => ValidityAttestation::Explicit(s),
})
.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.
......
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