Dispute distribution implementation (#3282)

* Dispute protocol.

* Dispute distribution protocol.

* Get network requests routed.

* WIP: Basic dispute sender logic.

* Basic validator determination logic.

* WIP: Getting things to typecheck.

* Slightly larger timeout.

* More typechecking stuff.

* Cleanup.

* Finished most of the sending logic.

* Handle active leaves updates

- Cleanup dead disputes
- Update sends for new sessions
- Retry on errors

* Pass sessions in already.

* Startup dispute sending.

* Provide incoming decoding facilities

and use them in statement-distribution.

* Relaxed runtime util requirements.

We only need a `SubsystemSender` not a full `SubsystemContext`.

* Better usability of incoming requests.

Make it possible to consume stuff without clones.

* Add basic receiver functionality.

* Cleanup + fixes for sender.

* One more sender fix.

* Start receiver.

* Make sure to send responses back.

* WIP: Exposed authority discovery

* Make tests pass.

* Fully featured receiver.

* Decrease cost of `NotAValidator`.

* Make `RuntimeInfo` LRU cache size configurable.

* Cache more sessions.

* Fix collator protocol.

* Disable metrics for now.

* Make dispute-distribution a proper subsystem.

* Fix naming.

* Code style fixes.

* Factored out 4x copied mock function.

* WIP: Tests.

* Whitespace cleanup.

* Accessor functions.

* More testing.

* More Debug instances.

* Fix busy loop.

* Working tests.

* More tests.

* Cleanup.

* Fix build.

* Basic receiving test.

* Non validator message gets dropped.

* More receiving tests.

* Test nested and subsequent imports.

* Fix spaces.

* Better formatted imports.

* Import cleanup.

* Metrics.

* Message -> MuxedMessage

* Message -> MuxedMessage

* More review remarks.

* Add missing metrics.rs.

* Fix flaky test.

* Dispute coordinator - deliver confirmations.

* Send out `DisputeMessage` on issue local statement.

* Unwire dispute distribution.

* Review remarks.

* Review remarks.

* Better docs.
This commit is contained in:
Robert Klotzner
2021-07-09 04:29:53 +02:00
committed by GitHub
parent 20993b32b1
commit b5257b2407
52 changed files with 4040 additions and 407 deletions
@@ -83,6 +83,9 @@ pub enum Error {
#[error(transparent)]
Oneshot(#[from] oneshot::Canceled),
#[error("Oneshot receiver died")]
OneshotSendFailed,
#[error(transparent)]
Participation(#[from] ParticipationError),
}
@@ -159,6 +162,7 @@ async fn handle_incoming(
candidate_receipt,
session,
n_validators,
report_availability,
} => {
if let Some((_, block_hash)) = state.recent_block {
participate(
@@ -168,6 +172,7 @@ async fn handle_incoming(
candidate_receipt,
session,
n_validators,
report_availability,
)
.await
} else {
@@ -184,6 +189,7 @@ async fn participate(
candidate_receipt: CandidateReceipt,
session: SessionIndex,
n_validators: u32,
report_availability: oneshot::Sender<bool>,
) -> Result<(), Error> {
let (recover_available_data_tx, recover_available_data_rx) = oneshot::channel();
let (code_tx, code_rx) = oneshot::channel();
@@ -203,14 +209,21 @@ async fn participate(
.await;
let available_data = match recover_available_data_rx.await? {
Ok(data) => data,
Ok(data) => {
report_availability.send(true).map_err(|_| Error::OneshotSendFailed)?;
data
}
Err(RecoveryError::Invalid) => {
report_availability.send(true).map_err(|_| Error::OneshotSendFailed)?;
// the available data was recovered but it is invalid, therefore we'll
// vote negatively for the candidate dispute
cast_invalid_vote(ctx, candidate_hash, candidate_receipt, session).await;
return Ok(());
}
Err(RecoveryError::Unavailable) => {
report_availability.send(false).map_err(|_| Error::OneshotSendFailed)?;
return Err(ParticipationError::MissingAvailableData(candidate_hash).into());
}
};
@@ -80,7 +80,7 @@ async fn activate_leaf(virtual_overseer: &mut VirtualOverseer, block_number: Blo
.await;
}
async fn participate(virtual_overseer: &mut VirtualOverseer) {
async fn participate(virtual_overseer: &mut VirtualOverseer) -> oneshot::Receiver<bool> {
let commitments = CandidateCommitments::default();
let candidate_receipt = {
let mut receipt = CandidateReceipt::default();
@@ -91,6 +91,8 @@ async fn participate(virtual_overseer: &mut VirtualOverseer) {
let session = 1;
let n_validators = 10;
let (report_availability, receive_availability) = oneshot::channel();
virtual_overseer
.send(FromOverseer::Communication {
msg: DisputeParticipationMessage::Participate {
@@ -98,12 +100,14 @@ async fn participate(virtual_overseer: &mut VirtualOverseer) {
candidate_receipt: candidate_receipt.clone(),
session,
n_validators,
report_availability,
},
})
.await;
})
.await;
receive_availability
}
async fn recover_available_data(virtual_overseer: &mut VirtualOverseer) {
async fn recover_available_data(virtual_overseer: &mut VirtualOverseer, receive_availability: oneshot::Receiver<bool>) {
let pov_block = PoV {
block_data: BlockData(Vec::new()),
};
@@ -122,6 +126,8 @@ async fn recover_available_data(virtual_overseer: &mut VirtualOverseer) {
},
"overseer did not receive recover available data message",
);
assert_eq!(receive_availability.await.expect("Availability should get reported"), true);
}
async fn fetch_validation_code(virtual_overseer: &mut VirtualOverseer) {
@@ -166,7 +172,7 @@ async fn store_available_data(virtual_overseer: &mut VirtualOverseer, success: b
fn cannot_participate_when_recent_block_state_is_missing() {
test_harness(|mut virtual_overseer| {
Box::pin(async move {
participate(&mut virtual_overseer).await;
let _ = participate(&mut virtual_overseer).await;
virtual_overseer
})
@@ -175,7 +181,7 @@ fn cannot_participate_when_recent_block_state_is_missing() {
test_harness(|mut virtual_overseer| {
Box::pin(async move {
activate_leaf(&mut virtual_overseer, 10).await;
participate(&mut virtual_overseer).await;
let _ = participate(&mut virtual_overseer).await;
// after activating at least one leaf the recent block
// state should be available which should lead to trying
@@ -199,7 +205,7 @@ fn cannot_participate_if_cannot_recover_available_data() {
test_harness(|mut virtual_overseer| {
Box::pin(async move {
activate_leaf(&mut virtual_overseer, 10).await;
participate(&mut virtual_overseer).await;
let receive_availability = participate(&mut virtual_overseer).await;
assert_matches!(
virtual_overseer.recv().await,
@@ -211,6 +217,8 @@ fn cannot_participate_if_cannot_recover_available_data() {
"overseer did not receive recover available data message",
);
assert_eq!(receive_availability.await.expect("Availability should get reported"), false);
virtual_overseer
})
});
@@ -221,8 +229,8 @@ fn cannot_participate_if_cannot_recover_validation_code() {
test_harness(|mut virtual_overseer| {
Box::pin(async move {
activate_leaf(&mut virtual_overseer, 10).await;
participate(&mut virtual_overseer).await;
recover_available_data(&mut virtual_overseer).await;
let receive_availability = participate(&mut virtual_overseer).await;
recover_available_data(&mut virtual_overseer, receive_availability).await;
assert_matches!(
virtual_overseer.recv().await,
@@ -248,7 +256,7 @@ fn cast_invalid_vote_if_available_data_is_invalid() {
test_harness(|mut virtual_overseer| {
Box::pin(async move {
activate_leaf(&mut virtual_overseer, 10).await;
participate(&mut virtual_overseer).await;
let receive_availability = participate(&mut virtual_overseer).await;
assert_matches!(
virtual_overseer.recv().await,
@@ -260,6 +268,8 @@ fn cast_invalid_vote_if_available_data_is_invalid() {
"overseer did not receive recover available data message",
);
assert_eq!(receive_availability.await.expect("Availability should get reported"), true);
assert_matches!(
virtual_overseer.recv().await,
AllMessages::DisputeCoordinator(DisputeCoordinatorMessage::IssueLocalStatement(
@@ -281,8 +291,8 @@ fn cast_invalid_vote_if_validation_fails_or_is_invalid() {
test_harness(|mut virtual_overseer| {
Box::pin(async move {
activate_leaf(&mut virtual_overseer, 10).await;
participate(&mut virtual_overseer).await;
recover_available_data(&mut virtual_overseer).await;
let receive_availability = participate(&mut virtual_overseer).await;
recover_available_data(&mut virtual_overseer, receive_availability).await;
fetch_validation_code(&mut virtual_overseer).await;
store_available_data(&mut virtual_overseer, true).await;
@@ -317,8 +327,8 @@ fn cast_invalid_vote_if_validation_passes_but_commitments_dont_match() {
test_harness(|mut virtual_overseer| {
Box::pin(async move {
activate_leaf(&mut virtual_overseer, 10).await;
participate(&mut virtual_overseer).await;
recover_available_data(&mut virtual_overseer).await;
let receive_availability = participate(&mut virtual_overseer).await;
recover_available_data(&mut virtual_overseer, receive_availability).await;
fetch_validation_code(&mut virtual_overseer).await;
store_available_data(&mut virtual_overseer, true).await;
@@ -357,8 +367,8 @@ fn cast_valid_vote_if_validation_passes() {
test_harness(|mut virtual_overseer| {
Box::pin(async move {
activate_leaf(&mut virtual_overseer, 10).await;
participate(&mut virtual_overseer).await;
recover_available_data(&mut virtual_overseer).await;
let receive_availability = participate(&mut virtual_overseer).await;
recover_available_data(&mut virtual_overseer, receive_availability).await;
fetch_validation_code(&mut virtual_overseer).await;
store_available_data(&mut virtual_overseer, true).await;
@@ -393,8 +403,8 @@ fn failure_to_store_available_data_does_not_preclude_participation() {
test_harness(|mut virtual_overseer| {
Box::pin(async move {
activate_leaf(&mut virtual_overseer, 10).await;
participate(&mut virtual_overseer).await;
recover_available_data(&mut virtual_overseer).await;
let receive_availability = participate(&mut virtual_overseer).await;
recover_available_data(&mut virtual_overseer, receive_availability).await;
fetch_validation_code(&mut virtual_overseer).await;
// the store available data request should fail
store_available_data(&mut virtual_overseer, false).await;