feat/view: assure heads in a view are sorted (#2493)

* feat/view: assure heads in a view are sorted

Allows O(n) comparisons, adds an alternate equiv relation
which takes O(n^2) for integrity verification.

Ref #2133

* revert: remove custom PartialEq impl, there are no duplicates

* fix: do not sort the live_heads, that alters the local view

* refactor/view: heads should not be public

* chore/spellcheck: add unfinalized

* fix/view: add missing len() and is_empty() fns

* quirk

* vec is not view

* Update node/network/approval-distribution/src/tests.rs

Co-authored-by: Andronik Ordian <write@reusable.software>

* Update node/network/bridge/src/lib.rs

Co-authored-by: Andronik Ordian <write@reusable.software>

* Update node/network/protocol/src/lib.rs

Co-authored-by: Andronik Ordian <write@reusable.software>

* fixup comment

* fix botched test

Co-authored-by: Andronik Ordian <write@reusable.software>
This commit is contained in:
Bernhard Schuster
2021-02-23 16:39:57 +01:00
committed by GitHub
parent 571651e326
commit e3f776abed
11 changed files with 85 additions and 38 deletions
+12 -14
View File
@@ -409,10 +409,10 @@ where
}
fn construct_view(live_heads: impl DoubleEndedIterator<Item = Hash>, finalized_number: BlockNumber) -> View {
View {
heads: live_heads.rev().take(MAX_VIEW_HEADS).collect(),
View::new(
live_heads.rev().take(MAX_VIEW_HEADS),
finalized_number
}
)
}
#[tracing::instrument(level = "trace", skip(net, ctx, validation_peers, collation_peers), fields(subsystem = LOG_TARGET))]
@@ -427,8 +427,9 @@ async fn update_our_view(
) -> SubsystemResult<()> {
let new_view = construct_view(live_heads.iter().map(|v| v.0), finalized_number);
// We only want to send a view update when the heads changed, not when only the finalized block changed.
if local_view.heads == new_view.heads {
// We only want to send a view update when the heads changed.
// A change in finalized block number only is _not_ sufficient.
if local_view.check_heads_eq(&new_view) {
return Ok(())
}
@@ -477,7 +478,7 @@ async fn handle_peer_messages<M>(
for message in messages {
outgoing_messages.push(match message {
WireMessage::ViewUpdate(new_view) => {
if new_view.heads.len() > MAX_VIEW_HEADS ||
if new_view.len() > MAX_VIEW_HEADS ||
new_view.finalized_number < peer_data.view.finalized_number
{
net.report_peer(
@@ -486,7 +487,7 @@ async fn handle_peer_messages<M>(
).await?;
continue
} else if new_view.heads.is_empty() {
} else if new_view.is_empty() {
net.report_peer(
peer.clone(),
EMPTY_VIEW_COST,
@@ -996,7 +997,7 @@ mod tests {
let actions = network_handle.next_network_actions(4).await;
let wire_message = WireMessage::<protocol_v1::ValidationProtocol>::ViewUpdate(
View { heads: vec![hash_a], finalized_number: 5 }
View::new(vec![hash_a], 5)
).encode();
assert_network_actions_contains(
@@ -1378,10 +1379,7 @@ mod tests {
let actions = network_handle.next_network_actions(2).await;
let wire_message = WireMessage::<protocol_v1::ValidationProtocol>::ViewUpdate(
View {
heads: vec![hash_b],
finalized_number: 1,
}
View::new(vec![hash_b], 1)
).encode();
assert_network_actions_contains(
@@ -1412,7 +1410,7 @@ mod tests {
peer_a.clone(),
PeerSet::Validation,
WireMessage::<protocol_v1::ValidationProtocol>::ViewUpdate(
View { heads: vec![Hash::repeat_byte(0x01)], finalized_number: 1 },
View::new(vec![Hash::repeat_byte(0x01)], 1),
).encode(),
).await;
@@ -1420,7 +1418,7 @@ mod tests {
peer_a.clone(),
PeerSet::Validation,
WireMessage::<protocol_v1::ValidationProtocol>::ViewUpdate(
View { heads: vec![], finalized_number: 0 },
View::new(vec![], 0),
).encode(),
).await;