add fallback request for req-response protocols (#2771)

Previously, it was only possible to retry the same request on a
different protocol name that had the exact same binary payloads.

Introduce a way of trying a different request on a different protocol if
the first one fails with Unsupported protocol.

This helps with adding new req-response versions in polkadot while
preserving compatibility with unupgraded nodes.

The way req-response protocols were bumped previously was that they were
bundled with some other notifications protocol upgrade, like for async
backing (but that is more complicated, especially if the feature does
not require any changes to a notifications protocol). Will be needed for
implementing https://github.com/polkadot-fellows/RFCs/pull/47

TODO:
- [x]  add tests
- [x] add guidance docs in polkadot about req-response protocol
versioning
This commit is contained in:
Alin Dima
2024-01-10 15:19:50 +02:00
committed by GitHub
parent af2e30e383
commit f2a750ee86
29 changed files with 802 additions and 304 deletions
@@ -139,6 +139,7 @@ mod tests {
use futures::{executor, future};
use parity_scale_codec::Encode;
use sc_network::ProtocolName;
use sp_core::testing::TaskExecutor;
use polkadot_node_primitives::BlockData;
@@ -231,7 +232,10 @@ mod tests {
Some(Requests::PoVFetchingV1(outgoing)) => {outgoing}
);
req.pending_response
.send(Ok(PoVFetchingResponse::PoV(pov.clone()).encode()))
.send(Ok((
PoVFetchingResponse::PoV(pov.clone()).encode(),
ProtocolName::from(""),
)))
.unwrap();
break
},
@@ -25,7 +25,7 @@ use futures::{
Future, FutureExt, StreamExt,
};
use sc_network as network;
use sc_network::{self as network, ProtocolName};
use sp_keyring::Sr25519Keyring;
use polkadot_node_network_protocol::request_response::{v1, Recipient};
@@ -252,7 +252,7 @@ impl TestRun {
}
}
req.pending_response
.send(response.map(Encode::encode))
.send(response.map(|r| (r.encode(), ProtocolName::from(""))))
.expect("Sending response should succeed");
}
return (valid_responses == 0) && self.valid_chunks.is_empty()
@@ -19,6 +19,7 @@ use std::{
time::Duration,
};
use network::ProtocolName;
use polkadot_node_subsystem_test_helpers::TestSubsystemContextHandle;
use polkadot_node_subsystem_util::TimeoutExt;
@@ -324,7 +325,11 @@ fn to_incoming_req(
let response = rx.await;
let payload = response.expect("Unexpected canceled request").result;
pending_response
.send(payload.map_err(|_| network::RequestFailure::Refused))
.send(
payload
.map_err(|_| network::RequestFailure::Refused)
.map(|r| (r, ProtocolName::from(""))),
)
.expect("Sending response is expected to work");
}
.boxed(),
@@ -22,13 +22,14 @@ use futures_timer::Delay;
use parity_scale_codec::Encode;
use polkadot_node_network_protocol::request_response::{
self as req_res, IncomingRequest, Recipient, ReqProtocolNames, Requests,
self as req_res, v1::AvailableDataFetchingRequest, IncomingRequest, Protocol, Recipient,
ReqProtocolNames, Requests,
};
use polkadot_node_subsystem_test_helpers::derive_erasure_chunks_with_proofs_and_root;
use super::*;
use sc_network::{config::RequestResponseConfig, IfDisconnected, OutboundFailure, RequestFailure};
use sc_network::{IfDisconnected, OutboundFailure, ProtocolName, RequestFailure};
use polkadot_node_primitives::{BlockData, PoV, Proof};
use polkadot_node_subsystem::messages::{
@@ -48,8 +49,18 @@ type VirtualOverseer = TestSubsystemContextHandle<AvailabilityRecoveryMessage>;
// Deterministic genesis hash for protocol names
const GENESIS_HASH: Hash = Hash::repeat_byte(0xff);
fn test_harness_fast_path<T: Future<Output = (VirtualOverseer, RequestResponseConfig)>>(
test: impl FnOnce(VirtualOverseer, RequestResponseConfig) -> T,
fn request_receiver(
req_protocol_names: &ReqProtocolNames,
) -> IncomingRequestReceiver<AvailableDataFetchingRequest> {
let receiver = IncomingRequest::get_config_receiver(req_protocol_names);
// Don't close the sending end of the request protocol. Otherwise, the subsystem will terminate.
std::mem::forget(receiver.1.inbound_queue);
receiver.0
}
fn test_harness<T: Future<Output = VirtualOverseer>>(
subsystem: AvailabilityRecoverySubsystem,
test: impl FnOnce(VirtualOverseer) -> T,
) {
let _ = env_logger::builder()
.is_test(true)
@@ -60,22 +71,18 @@ fn test_harness_fast_path<T: Future<Output = (VirtualOverseer, RequestResponseCo
let (context, virtual_overseer) = make_subsystem_context(pool.clone());
let (collation_req_receiver, req_cfg) =
IncomingRequest::get_config_receiver(&ReqProtocolNames::new(&GENESIS_HASH, None));
let subsystem =
AvailabilityRecoverySubsystem::with_fast_path(collation_req_receiver, Metrics::new_dummy());
let subsystem = async {
subsystem.run(context).await.unwrap();
};
let test_fut = test(virtual_overseer, req_cfg);
let test_fut = test(virtual_overseer);
futures::pin_mut!(test_fut);
futures::pin_mut!(subsystem);
executor::block_on(future::join(
async move {
let (mut overseer, _req_cfg) = test_fut.await;
let mut overseer = test_fut.await;
overseer_signal(&mut overseer, OverseerSignal::Conclude).await;
},
subsystem,
@@ -83,80 +90,6 @@ fn test_harness_fast_path<T: Future<Output = (VirtualOverseer, RequestResponseCo
.1
}
fn test_harness_chunks_only<T: Future<Output = (VirtualOverseer, RequestResponseConfig)>>(
test: impl FnOnce(VirtualOverseer, RequestResponseConfig) -> T,
) {
let _ = env_logger::builder()
.is_test(true)
.filter(Some("polkadot_availability_recovery"), log::LevelFilter::Trace)
.try_init();
let pool = sp_core::testing::TaskExecutor::new();
let (context, virtual_overseer) = make_subsystem_context(pool.clone());
let (collation_req_receiver, req_cfg) =
IncomingRequest::get_config_receiver(&ReqProtocolNames::new(&GENESIS_HASH, None));
let subsystem = AvailabilityRecoverySubsystem::with_chunks_only(
collation_req_receiver,
Metrics::new_dummy(),
);
let subsystem = subsystem.run(context);
let test_fut = test(virtual_overseer, req_cfg);
futures::pin_mut!(test_fut);
futures::pin_mut!(subsystem);
executor::block_on(future::join(
async move {
let (mut overseer, _req_cfg) = test_fut.await;
overseer_signal(&mut overseer, OverseerSignal::Conclude).await;
},
subsystem,
))
.1
.unwrap();
}
fn test_harness_chunks_if_pov_large<
T: Future<Output = (VirtualOverseer, RequestResponseConfig)>,
>(
test: impl FnOnce(VirtualOverseer, RequestResponseConfig) -> T,
) {
let _ = env_logger::builder()
.is_test(true)
.filter(Some("polkadot_availability_recovery"), log::LevelFilter::Trace)
.try_init();
let pool = sp_core::testing::TaskExecutor::new();
let (context, virtual_overseer) = make_subsystem_context(pool.clone());
let (collation_req_receiver, req_cfg) =
IncomingRequest::get_config_receiver(&ReqProtocolNames::new(&GENESIS_HASH, None));
let subsystem = AvailabilityRecoverySubsystem::with_chunks_if_pov_large(
collation_req_receiver,
Metrics::new_dummy(),
);
let subsystem = subsystem.run(context);
let test_fut = test(virtual_overseer, req_cfg);
futures::pin_mut!(test_fut);
futures::pin_mut!(subsystem);
executor::block_on(future::join(
async move {
let (mut overseer, _req_cfg) = test_fut.await;
overseer_signal(&mut overseer, OverseerSignal::Conclude).await;
},
subsystem,
))
.1
.unwrap();
}
const TIMEOUT: Duration = Duration::from_millis(300);
macro_rules! delay {
@@ -342,11 +275,12 @@ impl TestState {
async fn test_chunk_requests(
&self,
req_protocol_names: &ReqProtocolNames,
candidate_hash: CandidateHash,
virtual_overseer: &mut VirtualOverseer,
n: usize,
who_has: impl Fn(usize) -> Has,
) -> Vec<oneshot::Sender<std::result::Result<Vec<u8>, RequestFailure>>> {
) -> Vec<oneshot::Sender<std::result::Result<(Vec<u8>, ProtocolName), RequestFailure>>> {
// arbitrary order.
let mut i = 0;
let mut senders = Vec::new();
@@ -380,7 +314,7 @@ impl TestState {
let _ = req.pending_response.send(
available_data.map(|r|
req_res::v1::ChunkFetchingResponse::from(r).encode()
(req_res::v1::ChunkFetchingResponse::from(r).encode(), req_protocol_names.get_name(Protocol::ChunkFetchingV1))
)
);
}
@@ -394,10 +328,11 @@ impl TestState {
async fn test_full_data_requests(
&self,
req_protocol_names: &ReqProtocolNames,
candidate_hash: CandidateHash,
virtual_overseer: &mut VirtualOverseer,
who_has: impl Fn(usize) -> Has,
) -> Vec<oneshot::Sender<std::result::Result<Vec<u8>, RequestFailure>>> {
) -> Vec<oneshot::Sender<std::result::Result<(Vec<u8>, ProtocolName), RequestFailure>>> {
let mut senders = Vec::new();
for _ in 0..self.validators.len() {
// Receive a request for a chunk.
@@ -433,9 +368,10 @@ impl TestState {
let done = available_data.as_ref().ok().map_or(false, |x| x.is_some());
let _ = req.pending_response.send(
available_data.map(|r|
req_res::v1::AvailableDataFetchingResponse::from(r).encode()
)
available_data.map(|r|(
req_res::v1::AvailableDataFetchingResponse::from(r).encode(),
req_protocol_names.get_name(Protocol::AvailableDataFetchingV1)
))
);
if done { break }
@@ -532,8 +468,13 @@ impl Default for TestState {
#[test]
fn availability_is_recovered_from_chunks_if_no_group_provided() {
let test_state = TestState::default();
let req_protocol_names = ReqProtocolNames::new(&GENESIS_HASH, None);
let subsystem = AvailabilityRecoverySubsystem::with_fast_path(
request_receiver(&req_protocol_names),
Metrics::new_dummy(),
);
test_harness_fast_path(|mut virtual_overseer, req_cfg| async move {
test_harness(subsystem, |mut virtual_overseer| async move {
overseer_signal(
&mut virtual_overseer,
OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(new_leaf(
@@ -565,6 +506,7 @@ fn availability_is_recovered_from_chunks_if_no_group_provided() {
test_state
.test_chunk_requests(
&req_protocol_names,
candidate_hash,
&mut virtual_overseer,
test_state.threshold(),
@@ -600,6 +542,7 @@ fn availability_is_recovered_from_chunks_if_no_group_provided() {
test_state
.test_chunk_requests(
&req_protocol_names,
new_candidate.hash(),
&mut virtual_overseer,
test_state.impossibility_threshold(),
@@ -609,15 +552,20 @@ fn availability_is_recovered_from_chunks_if_no_group_provided() {
// A request times out with `Unavailable` error.
assert_eq!(rx.await.unwrap().unwrap_err(), RecoveryError::Unavailable);
(virtual_overseer, req_cfg)
virtual_overseer
});
}
#[test]
fn availability_is_recovered_from_chunks_even_if_backing_group_supplied_if_chunks_only() {
let test_state = TestState::default();
let req_protocol_names = ReqProtocolNames::new(&GENESIS_HASH, None);
let subsystem = AvailabilityRecoverySubsystem::with_chunks_only(
request_receiver(&req_protocol_names),
Metrics::new_dummy(),
);
test_harness_chunks_only(|mut virtual_overseer, req_cfg| async move {
test_harness(subsystem, |mut virtual_overseer| async move {
overseer_signal(
&mut virtual_overseer,
OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(new_leaf(
@@ -649,6 +597,7 @@ fn availability_is_recovered_from_chunks_even_if_backing_group_supplied_if_chunk
test_state
.test_chunk_requests(
&req_protocol_names,
candidate_hash,
&mut virtual_overseer,
test_state.threshold(),
@@ -684,6 +633,7 @@ fn availability_is_recovered_from_chunks_even_if_backing_group_supplied_if_chunk
test_state
.test_chunk_requests(
&req_protocol_names,
new_candidate.hash(),
&mut virtual_overseer,
test_state.impossibility_threshold(),
@@ -693,15 +643,20 @@ fn availability_is_recovered_from_chunks_even_if_backing_group_supplied_if_chunk
// A request times out with `Unavailable` error.
assert_eq!(rx.await.unwrap().unwrap_err(), RecoveryError::Unavailable);
(virtual_overseer, req_cfg)
virtual_overseer
});
}
#[test]
fn bad_merkle_path_leads_to_recovery_error() {
let mut test_state = TestState::default();
let req_protocol_names = ReqProtocolNames::new(&GENESIS_HASH, None);
let subsystem = AvailabilityRecoverySubsystem::with_fast_path(
request_receiver(&req_protocol_names),
Metrics::new_dummy(),
);
test_harness_fast_path(|mut virtual_overseer, req_cfg| async move {
test_harness(subsystem, |mut virtual_overseer| async move {
overseer_signal(
&mut virtual_overseer,
OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(new_leaf(
@@ -740,6 +695,7 @@ fn bad_merkle_path_leads_to_recovery_error() {
test_state
.test_chunk_requests(
&req_protocol_names,
candidate_hash,
&mut virtual_overseer,
test_state.impossibility_threshold(),
@@ -749,15 +705,20 @@ fn bad_merkle_path_leads_to_recovery_error() {
// A request times out with `Unavailable` error.
assert_eq!(rx.await.unwrap().unwrap_err(), RecoveryError::Unavailable);
(virtual_overseer, req_cfg)
virtual_overseer
});
}
#[test]
fn wrong_chunk_index_leads_to_recovery_error() {
let mut test_state = TestState::default();
let req_protocol_names = ReqProtocolNames::new(&GENESIS_HASH, None);
let subsystem = AvailabilityRecoverySubsystem::with_fast_path(
request_receiver(&req_protocol_names),
Metrics::new_dummy(),
);
test_harness_fast_path(|mut virtual_overseer, req_cfg| async move {
test_harness(subsystem, |mut virtual_overseer| async move {
overseer_signal(
&mut virtual_overseer,
OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(new_leaf(
@@ -796,6 +757,7 @@ fn wrong_chunk_index_leads_to_recovery_error() {
test_state
.test_chunk_requests(
&req_protocol_names,
candidate_hash,
&mut virtual_overseer,
test_state.impossibility_threshold(),
@@ -805,15 +767,20 @@ fn wrong_chunk_index_leads_to_recovery_error() {
// A request times out with `Unavailable` error as there are no good peers.
assert_eq!(rx.await.unwrap().unwrap_err(), RecoveryError::Unavailable);
(virtual_overseer, req_cfg)
virtual_overseer
});
}
#[test]
fn invalid_erasure_coding_leads_to_invalid_error() {
let mut test_state = TestState::default();
let req_protocol_names = ReqProtocolNames::new(&GENESIS_HASH, None);
let subsystem = AvailabilityRecoverySubsystem::with_fast_path(
request_receiver(&req_protocol_names),
Metrics::new_dummy(),
);
test_harness_fast_path(|mut virtual_overseer, req_cfg| async move {
test_harness(subsystem, |mut virtual_overseer| async move {
let pov = PoV { block_data: BlockData(vec![69; 64]) };
let (bad_chunks, bad_erasure_root) = derive_erasure_chunks_with_proofs_and_root(
@@ -859,6 +826,7 @@ fn invalid_erasure_coding_leads_to_invalid_error() {
test_state
.test_chunk_requests(
&req_protocol_names,
candidate_hash,
&mut virtual_overseer,
test_state.threshold(),
@@ -868,15 +836,20 @@ fn invalid_erasure_coding_leads_to_invalid_error() {
// f+1 'valid' chunks can't produce correct data.
assert_eq!(rx.await.unwrap().unwrap_err(), RecoveryError::Invalid);
(virtual_overseer, req_cfg)
virtual_overseer
});
}
#[test]
fn fast_path_backing_group_recovers() {
let test_state = TestState::default();
let req_protocol_names = ReqProtocolNames::new(&GENESIS_HASH, None);
let subsystem = AvailabilityRecoverySubsystem::with_fast_path(
request_receiver(&req_protocol_names),
Metrics::new_dummy(),
);
test_harness_fast_path(|mut virtual_overseer, req_cfg| async move {
test_harness(subsystem, |mut virtual_overseer| async move {
overseer_signal(
&mut virtual_overseer,
OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(new_leaf(
@@ -911,20 +884,30 @@ fn fast_path_backing_group_recovers() {
test_state.respond_to_available_data_query(&mut virtual_overseer, false).await;
test_state
.test_full_data_requests(candidate_hash, &mut virtual_overseer, who_has)
.test_full_data_requests(
&req_protocol_names,
candidate_hash,
&mut virtual_overseer,
who_has,
)
.await;
// Recovered data should match the original one.
assert_eq!(rx.await.unwrap().unwrap(), test_state.available_data);
(virtual_overseer, req_cfg)
virtual_overseer
});
}
#[test]
fn recovers_from_only_chunks_if_pov_large() {
let test_state = TestState::default();
let req_protocol_names = ReqProtocolNames::new(&GENESIS_HASH, None);
let subsystem = AvailabilityRecoverySubsystem::with_chunks_if_pov_large(
request_receiver(&req_protocol_names),
Metrics::new_dummy(),
);
test_harness_chunks_if_pov_large(|mut virtual_overseer, req_cfg| async move {
test_harness(subsystem, |mut virtual_overseer| async move {
overseer_signal(
&mut virtual_overseer,
OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(new_leaf(
@@ -965,6 +948,7 @@ fn recovers_from_only_chunks_if_pov_large() {
test_state
.test_chunk_requests(
&req_protocol_names,
candidate_hash,
&mut virtual_overseer,
test_state.threshold(),
@@ -1009,6 +993,7 @@ fn recovers_from_only_chunks_if_pov_large() {
test_state
.test_chunk_requests(
&req_protocol_names,
new_candidate.hash(),
&mut virtual_overseer,
test_state.impossibility_threshold(),
@@ -1018,15 +1003,20 @@ fn recovers_from_only_chunks_if_pov_large() {
// A request times out with `Unavailable` error.
assert_eq!(rx.await.unwrap().unwrap_err(), RecoveryError::Unavailable);
(virtual_overseer, req_cfg)
virtual_overseer
});
}
#[test]
fn fast_path_backing_group_recovers_if_pov_small() {
let test_state = TestState::default();
let req_protocol_names = ReqProtocolNames::new(&GENESIS_HASH, None);
let subsystem = AvailabilityRecoverySubsystem::with_chunks_if_pov_large(
request_receiver(&req_protocol_names),
Metrics::new_dummy(),
);
test_harness_chunks_if_pov_large(|mut virtual_overseer, req_cfg| async move {
test_harness(subsystem, |mut virtual_overseer| async move {
overseer_signal(
&mut virtual_overseer,
OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(new_leaf(
@@ -1070,20 +1060,30 @@ fn fast_path_backing_group_recovers_if_pov_small() {
test_state.respond_to_available_data_query(&mut virtual_overseer, false).await;
test_state
.test_full_data_requests(candidate_hash, &mut virtual_overseer, who_has)
.test_full_data_requests(
&req_protocol_names,
candidate_hash,
&mut virtual_overseer,
who_has,
)
.await;
// Recovered data should match the original one.
assert_eq!(rx.await.unwrap().unwrap(), test_state.available_data);
(virtual_overseer, req_cfg)
virtual_overseer
});
}
#[test]
fn no_answers_in_fast_path_causes_chunk_requests() {
let test_state = TestState::default();
let req_protocol_names = ReqProtocolNames::new(&GENESIS_HASH, None);
let subsystem = AvailabilityRecoverySubsystem::with_fast_path(
request_receiver(&req_protocol_names),
Metrics::new_dummy(),
);
test_harness_fast_path(|mut virtual_overseer, req_cfg| async move {
test_harness(subsystem, |mut virtual_overseer| async move {
overseer_signal(
&mut virtual_overseer,
OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(new_leaf(
@@ -1119,13 +1119,19 @@ fn no_answers_in_fast_path_causes_chunk_requests() {
test_state.respond_to_available_data_query(&mut virtual_overseer, false).await;
test_state
.test_full_data_requests(candidate_hash, &mut virtual_overseer, who_has)
.test_full_data_requests(
&req_protocol_names,
candidate_hash,
&mut virtual_overseer,
who_has,
)
.await;
test_state.respond_to_query_all_request(&mut virtual_overseer, |_| false).await;
test_state
.test_chunk_requests(
&req_protocol_names,
candidate_hash,
&mut virtual_overseer,
test_state.threshold(),
@@ -1135,15 +1141,20 @@ fn no_answers_in_fast_path_causes_chunk_requests() {
// Recovered data should match the original one.
assert_eq!(rx.await.unwrap().unwrap(), test_state.available_data);
(virtual_overseer, req_cfg)
virtual_overseer
});
}
#[test]
fn task_canceled_when_receivers_dropped() {
let test_state = TestState::default();
let req_protocol_names = ReqProtocolNames::new(&GENESIS_HASH, None);
let subsystem = AvailabilityRecoverySubsystem::with_chunks_only(
request_receiver(&req_protocol_names),
Metrics::new_dummy(),
);
test_harness_chunks_only(|mut virtual_overseer, req_cfg| async move {
test_harness(subsystem, |mut virtual_overseer| async move {
overseer_signal(
&mut virtual_overseer,
OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(new_leaf(
@@ -1170,7 +1181,7 @@ fn task_canceled_when_receivers_dropped() {
for _ in 0..test_state.validators.len() {
match virtual_overseer.recv().timeout(TIMEOUT).await {
None => return (virtual_overseer, req_cfg),
None => return virtual_overseer,
Some(_) => continue,
}
}
@@ -1182,8 +1193,13 @@ fn task_canceled_when_receivers_dropped() {
#[test]
fn chunks_retry_until_all_nodes_respond() {
let test_state = TestState::default();
let req_protocol_names = ReqProtocolNames::new(&GENESIS_HASH, None);
let subsystem = AvailabilityRecoverySubsystem::with_chunks_only(
request_receiver(&req_protocol_names),
Metrics::new_dummy(),
);
test_harness_chunks_only(|mut virtual_overseer, req_cfg| async move {
test_harness(subsystem, |mut virtual_overseer| async move {
overseer_signal(
&mut virtual_overseer,
OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(new_leaf(
@@ -1215,6 +1231,7 @@ fn chunks_retry_until_all_nodes_respond() {
test_state
.test_chunk_requests(
&req_protocol_names,
candidate_hash,
&mut virtual_overseer,
test_state.validators.len() - test_state.threshold(),
@@ -1225,6 +1242,7 @@ fn chunks_retry_until_all_nodes_respond() {
// we get to go another round!
test_state
.test_chunk_requests(
&req_protocol_names,
candidate_hash,
&mut virtual_overseer,
test_state.impossibility_threshold(),
@@ -1234,15 +1252,20 @@ fn chunks_retry_until_all_nodes_respond() {
// Recovered data should match the original one.
assert_eq!(rx.await.unwrap().unwrap_err(), RecoveryError::Unavailable);
(virtual_overseer, req_cfg)
virtual_overseer
});
}
#[test]
fn not_returning_requests_wont_stall_retrieval() {
let test_state = TestState::default();
let req_protocol_names = ReqProtocolNames::new(&GENESIS_HASH, None);
let subsystem = AvailabilityRecoverySubsystem::with_chunks_only(
request_receiver(&req_protocol_names),
Metrics::new_dummy(),
);
test_harness_chunks_only(|mut virtual_overseer, req_cfg| async move {
test_harness(subsystem, |mut virtual_overseer| async move {
overseer_signal(
&mut virtual_overseer,
OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(new_leaf(
@@ -1277,13 +1300,18 @@ fn not_returning_requests_wont_stall_retrieval() {
// Not returning senders won't cause the retrieval to stall:
let _senders = test_state
.test_chunk_requests(candidate_hash, &mut virtual_overseer, not_returning_count, |_| {
Has::DoesNotReturn
})
.test_chunk_requests(
&req_protocol_names,
candidate_hash,
&mut virtual_overseer,
not_returning_count,
|_| Has::DoesNotReturn,
)
.await;
test_state
.test_chunk_requests(
&req_protocol_names,
candidate_hash,
&mut virtual_overseer,
// Should start over:
@@ -1295,6 +1323,7 @@ fn not_returning_requests_wont_stall_retrieval() {
// we get to go another round!
test_state
.test_chunk_requests(
&req_protocol_names,
candidate_hash,
&mut virtual_overseer,
test_state.threshold(),
@@ -1304,15 +1333,20 @@ fn not_returning_requests_wont_stall_retrieval() {
// Recovered data should match the original one:
assert_eq!(rx.await.unwrap().unwrap(), test_state.available_data);
(virtual_overseer, req_cfg)
virtual_overseer
});
}
#[test]
fn all_not_returning_requests_still_recovers_on_return() {
let test_state = TestState::default();
let req_protocol_names = ReqProtocolNames::new(&GENESIS_HASH, None);
let subsystem = AvailabilityRecoverySubsystem::with_chunks_only(
request_receiver(&req_protocol_names),
Metrics::new_dummy(),
);
test_harness_chunks_only(|mut virtual_overseer, req_cfg| async move {
test_harness(subsystem, |mut virtual_overseer| async move {
overseer_signal(
&mut virtual_overseer,
OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(new_leaf(
@@ -1344,6 +1378,7 @@ fn all_not_returning_requests_still_recovers_on_return() {
let senders = test_state
.test_chunk_requests(
&req_protocol_names,
candidate_hash,
&mut virtual_overseer,
test_state.validators.len(),
@@ -1358,6 +1393,7 @@ fn all_not_returning_requests_still_recovers_on_return() {
std::mem::drop(senders);
},
test_state.test_chunk_requests(
&req_protocol_names,
candidate_hash,
&mut virtual_overseer,
// Should start over:
@@ -1370,6 +1406,7 @@ fn all_not_returning_requests_still_recovers_on_return() {
// we get to go another round!
test_state
.test_chunk_requests(
&req_protocol_names,
candidate_hash,
&mut virtual_overseer,
test_state.threshold(),
@@ -1379,15 +1416,20 @@ fn all_not_returning_requests_still_recovers_on_return() {
// Recovered data should match the original one:
assert_eq!(rx.await.unwrap().unwrap(), test_state.available_data);
(virtual_overseer, req_cfg)
virtual_overseer
});
}
#[test]
fn returns_early_if_we_have_the_data() {
let test_state = TestState::default();
let req_protocol_names = ReqProtocolNames::new(&GENESIS_HASH, None);
let subsystem = AvailabilityRecoverySubsystem::with_chunks_only(
request_receiver(&req_protocol_names),
Metrics::new_dummy(),
);
test_harness_chunks_only(|mut virtual_overseer, req_cfg| async move {
test_harness(subsystem, |mut virtual_overseer| async move {
overseer_signal(
&mut virtual_overseer,
OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(new_leaf(
@@ -1414,15 +1456,20 @@ fn returns_early_if_we_have_the_data() {
test_state.respond_to_available_data_query(&mut virtual_overseer, true).await;
assert_eq!(rx.await.unwrap().unwrap(), test_state.available_data);
(virtual_overseer, req_cfg)
virtual_overseer
});
}
#[test]
fn does_not_query_local_validator() {
let test_state = TestState::default();
let req_protocol_names = ReqProtocolNames::new(&GENESIS_HASH, None);
let subsystem = AvailabilityRecoverySubsystem::with_chunks_only(
request_receiver(&req_protocol_names),
Metrics::new_dummy(),
);
test_harness_chunks_only(|mut virtual_overseer, req_cfg| async move {
test_harness(subsystem, |mut virtual_overseer| async move {
overseer_signal(
&mut virtual_overseer,
OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(new_leaf(
@@ -1453,6 +1500,7 @@ fn does_not_query_local_validator() {
test_state
.test_chunk_requests(
&req_protocol_names,
candidate_hash,
&mut virtual_overseer,
test_state.validators.len(),
@@ -1463,6 +1511,7 @@ fn does_not_query_local_validator() {
// second round, make sure it uses the local chunk.
test_state
.test_chunk_requests(
&req_protocol_names,
candidate_hash,
&mut virtual_overseer,
test_state.threshold() - 1,
@@ -1471,15 +1520,20 @@ fn does_not_query_local_validator() {
.await;
assert_eq!(rx.await.unwrap().unwrap(), test_state.available_data);
(virtual_overseer, req_cfg)
virtual_overseer
});
}
#[test]
fn invalid_local_chunk_is_ignored() {
let test_state = TestState::default();
let req_protocol_names = ReqProtocolNames::new(&GENESIS_HASH, None);
let subsystem = AvailabilityRecoverySubsystem::with_chunks_only(
request_receiver(&req_protocol_names),
Metrics::new_dummy(),
);
test_harness_chunks_only(|mut virtual_overseer, req_cfg| async move {
test_harness(subsystem, |mut virtual_overseer| async move {
overseer_signal(
&mut virtual_overseer,
OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(new_leaf(
@@ -1512,6 +1566,7 @@ fn invalid_local_chunk_is_ignored() {
test_state
.test_chunk_requests(
&req_protocol_names,
candidate_hash,
&mut virtual_overseer,
test_state.threshold() - 1,
@@ -1520,6 +1575,6 @@ fn invalid_local_chunk_is_ignored() {
.await;
assert_eq!(rx.await.unwrap().unwrap(), test_state.available_data);
(virtual_overseer, req_cfg)
virtual_overseer
});
}
+4 -1
View File
@@ -264,7 +264,8 @@ impl Network for Arc<NetworkService<Block, Hash>> {
req_protocol_names: &ReqProtocolNames,
if_disconnected: IfDisconnected,
) {
let (protocol, OutgoingRequest { peer, payload, pending_response }) = req.encode_request();
let (protocol, OutgoingRequest { peer, payload, pending_response, fallback_request }) =
req.encode_request();
let peer_id = match peer {
Recipient::Peer(peer_id) => Some(peer_id),
@@ -315,6 +316,7 @@ impl Network for Arc<NetworkService<Block, Hash>> {
target: LOG_TARGET,
%peer_id,
protocol = %req_protocol_names.get_name(protocol),
fallback_protocol = ?fallback_request.as_ref().map(|(_, p)| req_protocol_names.get_name(*p)),
?if_disconnected,
"Starting request",
);
@@ -324,6 +326,7 @@ impl Network for Arc<NetworkService<Block, Hash>> {
peer_id,
req_protocol_names.get_name(protocol),
payload,
fallback_request.map(|(r, p)| (r, req_protocol_names.get_name(p))),
pending_response,
if_disconnected,
);
@@ -17,6 +17,7 @@
use super::*;
use assert_matches::assert_matches;
use futures::{executor, future, Future};
use sc_network::ProtocolName;
use sp_core::{crypto::Pair, Encode};
use sp_keyring::Sr25519Keyring;
use sp_keystore::Keystore;
@@ -559,11 +560,11 @@ fn act_on_advertisement_v2() {
.await;
response_channel
.send(Ok(request_v1::CollationFetchingResponse::Collation(
candidate_a.clone(),
pov.clone(),
)
.encode()))
.send(Ok((
request_v1::CollationFetchingResponse::Collation(candidate_a.clone(), pov.clone())
.encode(),
ProtocolName::from(""),
)))
.expect("Sending response should succeed");
assert_candidate_backing_second(
@@ -761,11 +762,11 @@ fn fetch_one_collation_at_a_time() {
candidate_a.descriptor.relay_parent = test_state.relay_parent;
candidate_a.descriptor.persisted_validation_data_hash = dummy_pvd().hash();
response_channel
.send(Ok(request_v1::CollationFetchingResponse::Collation(
candidate_a.clone(),
pov.clone(),
)
.encode()))
.send(Ok((
request_v1::CollationFetchingResponse::Collation(candidate_a.clone(), pov.clone())
.encode(),
ProtocolName::from(""),
)))
.expect("Sending response should succeed");
assert_candidate_backing_second(
@@ -885,19 +886,19 @@ fn fetches_next_collation() {
// First request finishes now:
response_channel_non_exclusive
.send(Ok(request_v1::CollationFetchingResponse::Collation(
candidate_a.clone(),
pov.clone(),
)
.encode()))
.send(Ok((
request_v1::CollationFetchingResponse::Collation(candidate_a.clone(), pov.clone())
.encode(),
ProtocolName::from(""),
)))
.expect("Sending response should succeed");
response_channel
.send(Ok(request_v1::CollationFetchingResponse::Collation(
candidate_a.clone(),
pov.clone(),
)
.encode()))
.send(Ok((
request_v1::CollationFetchingResponse::Collation(candidate_a.clone(), pov.clone())
.encode(),
ProtocolName::from(""),
)))
.expect("Sending response should succeed");
assert_candidate_backing_second(
@@ -1023,11 +1024,11 @@ fn fetch_next_collation_on_invalid_collation() {
candidate_a.descriptor.relay_parent = test_state.relay_parent;
candidate_a.descriptor.persisted_validation_data_hash = dummy_pvd().hash();
response_channel
.send(Ok(request_v1::CollationFetchingResponse::Collation(
candidate_a.clone(),
pov.clone(),
)
.encode()))
.send(Ok((
request_v1::CollationFetchingResponse::Collation(candidate_a.clone(), pov.clone())
.encode(),
ProtocolName::from(""),
)))
.expect("Sending response should succeed");
let receipt = assert_candidate_backing_second(
@@ -314,11 +314,11 @@ fn v1_advertisement_accepted_and_seconded() {
let pov = PoV { block_data: BlockData(vec![1]) };
response_channel
.send(Ok(request_v2::CollationFetchingResponse::Collation(
candidate.clone(),
pov.clone(),
)
.encode()))
.send(Ok((
request_v2::CollationFetchingResponse::Collation(candidate.clone(), pov.clone())
.encode(),
ProtocolName::from(""),
)))
.expect("Sending response should succeed");
assert_candidate_backing_second(
@@ -565,11 +565,14 @@ fn second_multiple_candidates_per_relay_parent() {
let pov = PoV { block_data: BlockData(vec![1]) };
response_channel
.send(Ok(request_v2::CollationFetchingResponse::Collation(
candidate.clone(),
pov.clone(),
)
.encode()))
.send(Ok((
request_v2::CollationFetchingResponse::Collation(
candidate.clone(),
pov.clone(),
)
.encode(),
ProtocolName::from(""),
)))
.expect("Sending response should succeed");
assert_candidate_backing_second(
@@ -717,11 +720,11 @@ fn fetched_collation_sanity_check() {
let pov = PoV { block_data: BlockData(vec![1]) };
response_channel
.send(Ok(request_v2::CollationFetchingResponse::Collation(
candidate.clone(),
pov.clone(),
)
.encode()))
.send(Ok((
request_v2::CollationFetchingResponse::Collation(candidate.clone(), pov.clone())
.encode(),
ProtocolName::from(""),
)))
.expect("Sending response should succeed");
// PVD request.
@@ -32,7 +32,7 @@ use futures::{
use futures_timer::Delay;
use parity_scale_codec::{Decode, Encode};
use sc_network::config::RequestResponseConfig;
use sc_network::{config::RequestResponseConfig, ProtocolName};
use polkadot_node_network_protocol::{
request_response::{v1::DisputeRequest, IncomingRequest, ReqProtocolNames},
@@ -832,7 +832,7 @@ async fn check_sent_requests(
if confirm_receive {
for req in reqs {
req.pending_response.send(
Ok(DisputeResponse::Confirmed.encode())
Ok((DisputeResponse::Confirmed.encode(), ProtocolName::from("")))
)
.expect("Subsystem should be listening for a response.");
}
@@ -30,7 +30,24 @@
//! `trait IsRequest` .... A trait describing a particular request. It is used for gathering meta
//! data, like what is the corresponding response type.
//!
//! Versioned (v1 module): The actual requests and responses as sent over the network.
//! ## Versioning
//!
//! Versioning for request-response protocols can be done in multiple ways.
//!
//! If you're just changing the protocol name but the binary payloads are the same, just add a new
//! `fallback_name` to the protocol config.
//!
//! One way in which versioning has historically been achieved for req-response protocols is to
//! bundle the new req-resp version with an upgrade of a notifications protocol. The subsystem would
//! then know which request version to use based on stored data about the peer's notifications
//! protocol version.
//!
//! When bumping a notifications protocol version is not needed/desirable, you may add a new
//! req-resp protocol and set the old request as a fallback (see
//! `OutgoingRequest::new_with_fallback`). A request with the new version will be attempted and if
//! the protocol is refused by the peer, the fallback protocol request will be used.
//! Information about the actually used protocol will be returned alongside the raw response, so
//! that you know how to decode it.
use std::{collections::HashMap, time::Duration, u64};
@@ -188,11 +205,11 @@ impl Protocol {
tx: Option<async_channel::Sender<network::IncomingRequest>>,
) -> RequestResponseConfig {
let name = req_protocol_names.get_name(self);
let fallback_names = self.get_fallback_names();
let legacy_names = self.get_legacy_name().into_iter().map(Into::into).collect();
match self {
Protocol::ChunkFetchingV1 => RequestResponseConfig {
name,
fallback_names,
fallback_names: legacy_names,
max_request_size: 1_000,
max_response_size: POV_RESPONSE_SIZE as u64 * 3,
// We are connected to all validators:
@@ -202,7 +219,7 @@ impl Protocol {
Protocol::CollationFetchingV1 | Protocol::CollationFetchingV2 =>
RequestResponseConfig {
name,
fallback_names,
fallback_names: legacy_names,
max_request_size: 1_000,
max_response_size: POV_RESPONSE_SIZE,
// Taken from initial implementation in collator protocol:
@@ -211,7 +228,7 @@ impl Protocol {
},
Protocol::PoVFetchingV1 => RequestResponseConfig {
name,
fallback_names,
fallback_names: legacy_names,
max_request_size: 1_000,
max_response_size: POV_RESPONSE_SIZE,
request_timeout: POV_REQUEST_TIMEOUT_CONNECTED,
@@ -219,7 +236,7 @@ impl Protocol {
},
Protocol::AvailableDataFetchingV1 => RequestResponseConfig {
name,
fallback_names,
fallback_names: legacy_names,
max_request_size: 1_000,
// Available data size is dominated by the PoV size.
max_response_size: POV_RESPONSE_SIZE,
@@ -228,7 +245,7 @@ impl Protocol {
},
Protocol::StatementFetchingV1 => RequestResponseConfig {
name,
fallback_names,
fallback_names: legacy_names,
max_request_size: 1_000,
// Available data size is dominated code size.
max_response_size: STATEMENT_RESPONSE_SIZE,
@@ -246,7 +263,7 @@ impl Protocol {
},
Protocol::DisputeSendingV1 => RequestResponseConfig {
name,
fallback_names,
fallback_names: legacy_names,
max_request_size: 1_000,
// Responses are just confirmation, in essence not even a bit. So 100 seems
// plenty.
@@ -256,7 +273,7 @@ impl Protocol {
},
Protocol::AttestedCandidateV2 => RequestResponseConfig {
name,
fallback_names,
fallback_names: legacy_names,
max_request_size: 1_000,
max_response_size: ATTESTED_CANDIDATE_RESPONSE_SIZE,
request_timeout: ATTESTED_CANDIDATE_TIMEOUT,
@@ -328,12 +345,9 @@ impl Protocol {
}
}
/// Fallback protocol names of this protocol, as understood by substrate networking.
fn get_fallback_names(self) -> Vec<ProtocolName> {
self.get_legacy_name().into_iter().map(Into::into).collect()
}
/// Legacy protocol name associated with each peer set, if any.
/// The request will be tried on this legacy protocol name if the remote refuses to speak the
/// protocol.
const fn get_legacy_name(self) -> Option<&'static str> {
match self {
Protocol::ChunkFetchingV1 => Some("/polkadot/req_chunk/1"),
@@ -360,6 +374,7 @@ pub trait IsRequest {
}
/// Type for getting on the wire [`Protocol`] names using genesis hash & fork id.
#[derive(Clone)]
pub struct ReqProtocolNames {
names: HashMap<Protocol, ProtocolName>,
}
@@ -14,8 +14,9 @@
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use futures::{channel::oneshot, prelude::Future};
use futures::{channel::oneshot, prelude::Future, FutureExt};
use network::ProtocolName;
use parity_scale_codec::{Decode, Encode, Error as DecodingError};
use sc_network as network;
@@ -49,20 +50,6 @@ pub enum Requests {
}
impl Requests {
/// Get the protocol this request conforms to.
pub fn get_protocol(&self) -> Protocol {
match self {
Self::ChunkFetchingV1(_) => Protocol::ChunkFetchingV1,
Self::CollationFetchingV1(_) => Protocol::CollationFetchingV1,
Self::CollationFetchingV2(_) => Protocol::CollationFetchingV2,
Self::PoVFetchingV1(_) => Protocol::PoVFetchingV1,
Self::AvailableDataFetchingV1(_) => Protocol::AvailableDataFetchingV1,
Self::StatementFetchingV1(_) => Protocol::StatementFetchingV1,
Self::DisputeSendingV1(_) => Protocol::DisputeSendingV1,
Self::AttestedCandidateV2(_) => Protocol::AttestedCandidateV2,
}
}
/// Encode the request.
///
/// The corresponding protocol is returned as well, as we are now leaving typed territory.
@@ -85,7 +72,7 @@ impl Requests {
}
/// Used by the network to send us a response to a request.
pub type ResponseSender = oneshot::Sender<Result<Vec<u8>, network::RequestFailure>>;
pub type ResponseSender = oneshot::Sender<Result<(Vec<u8>, ProtocolName), network::RequestFailure>>;
/// Any error that can occur when sending a request.
#[derive(Debug, thiserror::Error)]
@@ -128,11 +115,13 @@ impl RequestError {
/// When using `Recipient::Authority`, the addresses can be found thanks to the authority
/// discovery system.
#[derive(Debug)]
pub struct OutgoingRequest<Req> {
pub struct OutgoingRequest<Req, FallbackReq = Req> {
/// Intended recipient of this request.
pub peer: Recipient,
/// The actual request to send over the wire.
pub payload: Req,
/// Optional fallback request and protocol.
pub fallback_request: Option<(FallbackReq, Protocol)>,
/// Sender which is used by networking to get us back a response.
pub pending_response: ResponseSender,
}
@@ -149,10 +138,12 @@ pub enum Recipient {
/// Responses received for an `OutgoingRequest`.
pub type OutgoingResult<Res> = Result<Res, RequestError>;
impl<Req> OutgoingRequest<Req>
impl<Req, FallbackReq> OutgoingRequest<Req, FallbackReq>
where
Req: IsRequest + Encode,
Req::Response: Decode,
FallbackReq: IsRequest + Encode,
FallbackReq::Response: Decode,
{
/// Create a new `OutgoingRequest`.
///
@@ -163,24 +154,54 @@ where
payload: Req,
) -> (Self, impl Future<Output = OutgoingResult<Req::Response>>) {
let (tx, rx) = oneshot::channel();
let r = Self { peer, payload, pending_response: tx };
(r, receive_response::<Req>(rx))
let r = Self { peer, payload, pending_response: tx, fallback_request: None };
(r, receive_response::<Req>(rx.map(|r| r.map(|r| r.map(|(resp, _)| resp)))))
}
/// Create a new `OutgoingRequest` with a fallback in case the remote does not support this
/// protocol. Useful when adding a new version of a req-response protocol, to achieve
/// compatibility with the older version.
///
/// Returns a raw `Vec<u8>` response over the channel. Use the associated `ProtocolName` to know
/// which request was the successful one and appropriately decode the response.
// WARNING: This is commented for now because it's not used yet.
// If you need it, make sure to test it. You may need to enable the V1 substream upgrade
// protocol, unless libp2p was in the meantime updated to a version that fixes the problem
// described in https://github.com/libp2p/rust-libp2p/issues/5074
// pub fn new_with_fallback(
// peer: Recipient,
// payload: Req,
// fallback_request: FallbackReq,
// ) -> (Self, impl Future<Output = OutgoingResult<(Vec<u8>, ProtocolName)>>) {
// let (tx, rx) = oneshot::channel();
// let r = Self {
// peer,
// payload,
// pending_response: tx,
// fallback_request: Some((fallback_request, FallbackReq::PROTOCOL)),
// };
// (r, async { Ok(rx.await??) })
// }
/// Encode a request into a `Vec<u8>`.
///
/// As this throws away type information, we also return the `Protocol` this encoded request
/// adheres to.
pub fn encode_request(self) -> (Protocol, OutgoingRequest<Vec<u8>>) {
let OutgoingRequest { peer, payload, pending_response } = self;
let encoded = OutgoingRequest { peer, payload: payload.encode(), pending_response };
let OutgoingRequest { peer, payload, pending_response, fallback_request } = self;
let encoded = OutgoingRequest {
peer,
payload: payload.encode(),
fallback_request: fallback_request.map(|(r, p)| (r.encode(), p)),
pending_response,
};
(Req::PROTOCOL, encoded)
}
}
/// Future for actually receiving a typed response for an `OutgoingRequest`.
async fn receive_response<Req>(
rec: oneshot::Receiver<Result<Vec<u8>, network::RequestFailure>>,
rec: impl Future<Output = Result<Result<Vec<u8>, network::RequestFailure>, oneshot::Canceled>>,
) -> OutgoingResult<Req::Response>
where
Req: IsRequest,
@@ -50,6 +50,7 @@ use polkadot_primitives_test_helpers::{
dummy_committed_candidate_receipt, dummy_hash, AlwaysZeroRng,
};
use sc_keystore::LocalKeystore;
use sc_network::ProtocolName;
use sp_application_crypto::{sr25519::Pair, AppCrypto, Pair as TraitPair};
use sp_authority_discovery::AuthorityPair;
use sp_keyring::Sr25519Keyring;
@@ -1330,7 +1331,7 @@ fn receiving_large_statement_from_one_sends_to_another_and_to_candidate_backing(
bad
};
let response = StatementFetchingResponse::Statement(bad_candidate);
outgoing.pending_response.send(Ok(response.encode())).unwrap();
outgoing.pending_response.send(Ok((response.encode(), ProtocolName::from("")))).unwrap();
}
);
@@ -1382,7 +1383,7 @@ fn receiving_large_statement_from_one_sends_to_another_and_to_candidate_backing(
// On retry, we should have reverse order:
assert_eq!(outgoing.peer, Recipient::Peer(peer_c));
let response = StatementFetchingResponse::Statement(candidate.clone());
outgoing.pending_response.send(Ok(response.encode())).unwrap();
outgoing.pending_response.send(Ok((response.encode(), ProtocolName::from("")))).unwrap();
}
);
@@ -1869,7 +1870,7 @@ fn delay_reputation_changes() {
bad
};
let response = StatementFetchingResponse::Statement(bad_candidate);
outgoing.pending_response.send(Ok(response.encode())).unwrap();
outgoing.pending_response.send(Ok((response.encode(), ProtocolName::from("")))).unwrap();
}
);
@@ -1913,7 +1914,7 @@ fn delay_reputation_changes() {
// On retry, we should have reverse order:
assert_eq!(outgoing.peer, Recipient::Peer(peer_c));
let response = StatementFetchingResponse::Statement(candidate.clone());
outgoing.pending_response.send(Ok(response.encode())).unwrap();
outgoing.pending_response.send(Ok((response.encode(), ProtocolName::from("")))).unwrap();
}
);
@@ -38,6 +38,7 @@ use polkadot_primitives::{
SessionIndex, SessionInfo, ValidatorPair,
};
use sc_keystore::LocalKeystore;
use sc_network::ProtocolName;
use sp_application_crypto::Pair as PairT;
use sp_authority_discovery::AuthorityPair as AuthorityDiscoveryPair;
use sp_keyring::Sr25519Keyring;
@@ -684,7 +685,7 @@ async fn handle_sent_request(
persisted_validation_data,
statements,
};
outgoing.pending_response.send(Ok(res.encode())).unwrap();
outgoing.pending_response.send(Ok((res.encode(), ProtocolName::from("")))).unwrap();
}
);
}
@@ -22,8 +22,9 @@ use polkadot_node_network_protocol::{
request_response::v2 as request_v2, v2::BackedCandidateManifest,
};
use polkadot_primitives_test_helpers::make_candidate;
use sc_network::config::{
IncomingRequest as RawIncomingRequest, OutgoingResponse as RawOutgoingResponse,
use sc_network::{
config::{IncomingRequest as RawIncomingRequest, OutgoingResponse as RawOutgoingResponse},
ProtocolName,
};
#[test]
@@ -1342,7 +1343,7 @@ fn when_validator_disabled_after_sending_the_request() {
persisted_validation_data: pvd,
statements,
};
outgoing.pending_response.send(Ok(res.encode())).unwrap();
outgoing.pending_response.send(Ok((res.encode(), ProtocolName::from("")))).unwrap();
}
);
}
@@ -109,7 +109,12 @@ fn prepare_test_inner(
chunks: state.chunks.clone(),
};
let network = NetworkEmulator::new(&config, &dependencies, &test_authorities);
let req_protocol_names = ReqProtocolNames::new(GENESIS_HASH, None);
let (collation_req_receiver, req_cfg) =
IncomingRequest::get_config_receiver(&req_protocol_names);
let network =
NetworkEmulator::new(&config, &dependencies, &test_authorities, req_protocol_names);
let network_bridge_tx = network_bridge::MockNetworkBridgeTx::new(
config.clone(),
@@ -122,9 +127,6 @@ fn prepare_test_inner(
_ => panic!("Unexpected objective"),
};
let (collation_req_receiver, req_cfg) =
IncomingRequest::get_config_receiver(&ReqProtocolNames::new(GENESIS_HASH, None));
let subsystem = if use_fast_path {
AvailabilityRecoverySubsystem::with_fast_path(
collation_req_receiver,
@@ -33,7 +33,9 @@ use polkadot_node_subsystem::{
};
use polkadot_node_network_protocol::request_response::{
self as req_res, v1::ChunkResponse, Requests,
self as req_res,
v1::{AvailableDataFetchingRequest, ChunkFetchingRequest, ChunkResponse},
IsRequest, Requests,
};
use polkadot_primitives::AuthorityDiscoveryId;
@@ -144,7 +146,10 @@ impl MockNetworkBridgeTx {
size = 0;
Err(RequestFailure::Network(OutboundFailure::ConnectionClosed))
} else {
Ok(req_res::v1::ChunkFetchingResponse::from(Some(chunk)).encode())
Ok((
req_res::v1::ChunkFetchingResponse::from(Some(chunk)).encode(),
self.network.req_protocol_names().get_name(ChunkFetchingRequest::PROTOCOL),
))
};
let authority_discovery_id_clone = authority_discovery_id.clone();
@@ -212,8 +217,13 @@ impl MockNetworkBridgeTx {
let response = if random_error(self.config.error) {
Err(RequestFailure::Network(OutboundFailure::ConnectionClosed))
} else {
Ok(req_res::v1::AvailableDataFetchingResponse::from(Some(available_data))
.encode())
Ok((
req_res::v1::AvailableDataFetchingResponse::from(Some(available_data))
.encode(),
self.network
.req_protocol_names()
.get_name(AvailableDataFetchingRequest::PROTOCOL),
))
};
let future = async move {
@@ -19,6 +19,7 @@ use super::{
*,
};
use colored::Colorize;
use polkadot_node_network_protocol::request_response::ReqProtocolNames;
use polkadot_primitives::AuthorityDiscoveryId;
use prometheus_endpoint::U64;
use rand::{seq::SliceRandom, thread_rng};
@@ -311,6 +312,8 @@ pub struct NetworkEmulator {
stats: Vec<Arc<PeerEmulatorStats>>,
/// Each emulated peer is a validator.
validator_authority_ids: HashMap<AuthorityDiscoveryId, usize>,
/// Request protocol names
req_protocol_names: ReqProtocolNames,
}
impl NetworkEmulator {
@@ -318,6 +321,7 @@ impl NetworkEmulator {
config: &TestConfiguration,
dependencies: &TestEnvironmentDependencies,
authorities: &TestAuthorities,
req_protocol_names: ReqProtocolNames,
) -> Self {
let n_peers = config.n_validators;
gum::info!(target: LOG_TARGET, "{}",format!("Initializing emulation for a {} peer network.", n_peers).bright_blue());
@@ -355,7 +359,12 @@ impl NetworkEmulator {
gum::info!(target: LOG_TARGET, "{}",format!("Network created, connected validator count {}", connected_count).bright_black());
Self { peers, stats, validator_authority_ids: validator_authority_id_mapping }
Self {
peers,
stats,
validator_authority_ids: validator_authority_id_mapping,
req_protocol_names,
}
}
pub fn is_peer_connected(&self, peer: &AuthorityDiscoveryId) -> bool {
@@ -428,6 +437,11 @@ impl NetworkEmulator {
// Our node always is peer 0.
self.peer_stats(0).inc_received(bytes);
}
// Get the request protocol names
pub fn req_protocol_names(&self) -> &ReqProtocolNames {
&self.req_protocol_names
}
}
use polkadot_node_subsystem_util::metrics::prometheus::{