Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
backing: reorder votes to match bitfield in backed candidate (#2006)
Browse files Browse the repository at this point in the history
  • Loading branch information
rphmeier authored Nov 24, 2020
1 parent 9de263f commit ede1959
Showing 1 changed file with 96 additions and 4 deletions.
100 changes: 96 additions & 4 deletions node/core/backing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ use polkadot_primitives::v1::{
ValidatorIndex, SigningContext, PoV, CandidateHash,
CandidateDescriptor, AvailableData, ValidatorSignature, Hash, CandidateReceipt,
CandidateCommitments, CoreState, CoreIndex, CollatorId, ValidationOutputs,
ValidityAttestation,
};
use polkadot_node_primitives::{
FromTableMisbehavior, Statement, SignedFullStatement, MisbehaviorReport, ValidationResult,
Expand Down Expand Up @@ -256,7 +257,7 @@ fn table_attested_to_backed(
) -> Option<BackedCandidate> {
let TableAttestedCandidate { candidate, validity_votes, group_id: para_id } = attested;

let (ids, validity_votes): (Vec<_>, Vec<_>) = validity_votes
let (ids, validity_votes): (Vec<_>, Vec<ValidityAttestation>) = validity_votes
.into_iter()
.map(|(id, vote)| (id, vote.into()))
.unzip();
Expand All @@ -267,15 +268,30 @@ fn table_attested_to_backed(

validator_indices.resize(group.len(), false);

for id in ids.iter() {
// The order of the validity votes in the backed candidate must match
// the order of bits set in the bitfield, which is not necessarily
// the order of the `validity_votes` we got from the table.
let mut vote_positions = Vec::with_capacity(validity_votes.len());
for (orig_idx, id) in ids.iter().enumerate() {
if let Some(position) = group.iter().position(|x| x == id) {
validator_indices.set(position, true);
vote_positions.push((orig_idx, position));
} else {
tracing::warn!(
target: LOG_TARGET,
"Logic error: Validity vote from table does not correspond to group",
);

return None;
}
}
vote_positions.sort_by_key(|(_orig, pos_in_group)| *pos_in_group);

Some(BackedCandidate {
candidate,
validity_votes,
validity_votes: vote_positions.into_iter()
.map(|(pos_in_votes, _pos_in_group)| validity_votes[pos_in_votes].clone())
.collect(),
validator_indices,
})
}
Expand Down Expand Up @@ -1003,7 +1019,7 @@ mod tests {
use polkadot_primitives::v1::{
ScheduledCore, BlockData, CandidateCommitments,
PersistedValidationData, ValidationData, TransientValidationData, HeadData,
ValidityAttestation, GroupRotationInfo,
GroupRotationInfo,
};
use polkadot_subsystem::{
messages::RuntimeApiRequest,
Expand Down Expand Up @@ -2123,4 +2139,80 @@ mod tests {
).await;
});
}

#[test]
fn candidate_backing_reorders_votes() {
use sp_core::Encode;

let relay_parent = [1; 32].into();
let para_id = ParaId::from(10);
let session_index = 5;
let signing_context = SigningContext { parent_hash: relay_parent, session_index };
let validators = vec![
Sr25519Keyring::Alice,
Sr25519Keyring::Bob,
Sr25519Keyring::Charlie,
Sr25519Keyring::Dave,
Sr25519Keyring::Ferdie,
Sr25519Keyring::One,
];

let validator_public = validator_pubkeys(&validators);
let validator_groups = {
let mut validator_groups = HashMap::new();
validator_groups.insert(para_id, vec![0, 1, 2, 3, 4, 5]);
validator_groups
};

let table_context = TableContext {
signing_context,
validator: None,
groups: validator_groups,
validators: validator_public.clone(),
};

let fake_attestation = |idx: u32| {
let candidate: CommittedCandidateReceipt = Default::default();
let hash = candidate.hash();
let mut data = vec![0; 64];
data[0..32].copy_from_slice(hash.0.as_bytes());
data[32..36].copy_from_slice(idx.encode().as_slice());

let sig = ValidatorSignature::try_from(data).unwrap();
statement_table::generic::ValidityAttestation::Implicit(sig)
};

let attested = TableAttestedCandidate {
candidate: Default::default(),
validity_votes: vec![
(5, fake_attestation(5)),
(3, fake_attestation(3)),
(1, fake_attestation(1)),
],
group_id: para_id,
};

let backed = table_attested_to_backed(attested, &table_context).unwrap();

let expected_bitvec = {
let mut validator_indices = BitVec::<bitvec::order::Lsb0, u8>::with_capacity(6);
validator_indices.resize(6, false);

validator_indices.set(1, true);
validator_indices.set(3, true);
validator_indices.set(5, true);

validator_indices
};

// Should be in bitfield order, which is opposite to the order provided to the function.
let expected_attestations = vec![
fake_attestation(1).into(),
fake_attestation(3).into(),
fake_attestation(5).into(),
];

assert_eq!(backed.validator_indices, expected_bitvec);
assert_eq!(backed.validity_votes, expected_attestations);
}
}

0 comments on commit ede1959

Please sign in to comment.