Allow at most one candidate with a code upgrade in a block (#2456)

* guide: max one candidate with upgrade per block

* max one candidate with upgrade per block

* ignore on runtime level too
This commit is contained in:
Robert Habermeier
2021-02-17 10:42:06 -06:00
committed by GitHub
parent 1e2161258b
commit 9e525e296d
4 changed files with 107 additions and 3 deletions
+15 -1
View File
@@ -448,7 +448,7 @@ async fn select_candidates(
selected_candidates.clone(),
tx,
)).into()).await.map_err(|err| Error::GetBackedCandidatesSend(err))?;
let candidates = rx.await.map_err(|err| Error::CanceledBackedCandidates(err))?;
let mut candidates = rx.await.map_err(|err| Error::CanceledBackedCandidates(err))?;
// `selected_candidates` is generated in ascending order by core index, and `GetBackedCandidates`
// _should_ preserve that property, but let's just make sure.
@@ -466,6 +466,20 @@ async fn select_candidates(
Err(Error::BackedCandidateOrderingProblem)?;
}
// keep only one candidate with validation code.
let mut with_validation_code = false;
candidates.retain(|c| {
if c.candidate.commitments.new_validation_code.is_some() {
if with_validation_code {
return false
}
with_validation_code = true;
}
true
});
Ok(candidates)
}
@@ -426,4 +426,63 @@ mod select_candidates {
);
})
}
#[test]
fn selects_max_one_code_upgrade() {
let mock_cores = mock_availability_cores();
let n_cores = mock_cores.len();
let empty_hash = PersistedValidationData::<BlockNumber>::default().hash();
// why those particular indices? see the comments on mock_availability_cores()
// the first candidate with code is included out of [1, 4, 7, 8, 10].
let cores = [1, 7, 10];
let cores_with_code = [1, 4, 8];
let committed_receipts: Vec<_> = (0..mock_cores.len())
.map(|i| CommittedCandidateReceipt {
descriptor: CandidateDescriptor {
para_id: i.into(),
persisted_validation_data_hash: empty_hash,
..Default::default()
},
commitments: CandidateCommitments {
new_validation_code: if cores_with_code.contains(&i) { Some(vec![].into()) } else { None },
..Default::default()
},
..Default::default()
})
.collect();
let candidates: Vec<_> = committed_receipts.iter().map(|r| r.to_plain()).collect();
let expected_candidates: Vec<_> = cores
.iter()
.map(|&idx| candidates[idx].clone())
.collect();
let expected_backed: Vec<_> = cores
.iter()
.map(|&idx| BackedCandidate {
candidate: committed_receipts[idx].clone(),
validity_votes: Vec::new(),
validator_indices: default_bitvec(n_cores),
})
.collect();
test_harness(|r| mock_overseer(r, expected_backed), |mut tx: mpsc::Sender<FromJobCommand>| async move {
let result =
select_candidates(&mock_cores, &[], &candidates, Default::default(), &mut tx)
.await.unwrap();
result.into_iter()
.for_each(|c|
assert!(
expected_candidates.iter().any(|c2| c.candidate.corresponds_to(c2)),
"Failed to find candidate: {:?}",
c,
)
);
})
}
}