use crate::{gossip::AbstractEventRef, observation::ObservationKey};
use itertools::Itertools;
use std::cmp::Ordering;
use std::usize;
pub(crate) fn find_interesting_content_for_event<'a, E>(
builder_event: E,
unconsensused_events: impl Iterator<Item = E>,
consistent_cmp: impl Fn(&ObservationKey, &ObservationKey) -> Ordering,
is_descendant: impl Fn(E, E) -> bool,
is_already_interesting_content: impl Fn(&ObservationKey) -> bool,
is_interesting_payload: impl Fn(&ObservationKey) -> bool,
) -> Vec<ObservationKey>
where
E: AbstractEventRef<'a>,
{
let has_builder_creator = |event: E| event.creator() == builder_event.creator();
let mut events_to_process = unconsensused_events
.filter(|event| is_descendant(builder_event, *event))
.filter_map(|event| {
event
.payload_key()
.filter(|payload_key| !is_already_interesting_content(payload_key))
.map(|payload_key| {
(
event,
(payload_key, if has_builder_creator(event) { 0 } else { 1 }),
)
})
})
.collect_vec();
events_to_process.sort_by(|(_, l_key), (_, r_key)| l_key.cmp(&r_key));
let payload_keys_with_events = events_to_process
.into_iter()
.group_by(|(_, (&payload_key, _))| payload_key)
.into_iter()
.map(|(payload_key, events)| {
let events = events.map(|(event, _)| event);
(payload_key, events.collect_vec())
})
.collect_vec();
let mut interesting_payload_keys = payload_keys_with_events
.iter()
.filter_map(|(payload_key, events)| {
if is_interesting_payload(&payload_key) {
events.iter().next().map(|event| (event, payload_key))
} else {
None
}
})
.map(|(event, payload_key)| {
(
if has_builder_creator(*event) {
event.index_by_creator()
} else {
usize::MAX
},
payload_key,
)
})
.collect_vec();
interesting_payload_keys.sort_by(|(l_index, l_key), (r_index, r_key)| {
l_index
.cmp(r_index)
.then_with(|| consistent_cmp(l_key, r_key))
});
interesting_payload_keys
.into_iter()
.map(|(_, key)| key)
.cloned()
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
hash::Hash,
observation::{ConsensusMode, ObservationHash},
peer_list::PeerIndex,
};
lazy_static! {
static ref OPAQUE_HASHES: Vec<ObservationHash> =
(0..9).map(observation_hash_from_u8).collect();
static ref PEER_IDS: Vec<PeerIndex> =
(0..9).map(PeerIndex::new_test_peer_index).collect();
}
#[derive(Debug, Clone)]
struct TestEvent {
peer_index: PeerIndex,
creator_index: usize,
payload_key: Option<ObservationKey>,
has_ancestors: bool,
}
impl TestEvent {
fn new(
peer_index: PeerIndex,
creator_index: usize,
payload_hash: Option<ObservationHash>,
consensus_mode: ConsensusMode,
) -> Self {
Self {
peer_index,
creator_index,
payload_key: payload_hash
.map(|hash| ObservationKey::new(hash, peer_index, consensus_mode)),
has_ancestors: false,
}
}
fn with_ancestors(self) -> Self {
Self {
has_ancestors: true,
..self
}
}
}
impl<'a> AbstractEventRef<'a> for &'a TestEvent {
fn payload_key(self) -> Option<&'a ObservationKey> {
self.payload_key.as_ref()
}
fn creator(self) -> PeerIndex {
self.peer_index
}
fn index_by_creator(self) -> usize {
self.creator_index
}
}
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug)]
pub(crate) enum AssertObservationKey {
Single(u8, PeerIndex),
Supermajority(u8),
}
impl AssertObservationKey {
pub fn new_from_key(key: &ObservationKey, hashes: &[ObservationHash]) -> Self {
match *key {
ObservationKey::Single(ref hash, peer) => {
AssertObservationKey::Single(find_observation_hash_index(hash, hashes), peer)
}
ObservationKey::Supermajority(ref hash) => {
AssertObservationKey::Supermajority(find_observation_hash_index(hash, hashes))
}
}
}
}
fn observation_hash_from_u8(value: u8) -> ObservationHash {
ObservationHash(Hash::from(vec![value].as_slice()))
}
fn find_observation_hash_index(hash: &ObservationHash, hashes: &[ObservationHash]) -> u8 {
unwrap!(hashes.iter().position(|obs_hash| obs_hash == hash)) as u8
}
mod find_interesting_content_for_event {
use super::*;
use AssertObservationKey::*;
struct TestSimpleData {
events: Events,
payload_properties: PayloadProperties,
expected_payloads: Vec<AssertObservationKey>,
is_reverse: bool,
}
struct PayloadProperties {
is_already_interesting_content: bool,
is_interesting_payload: bool,
}
struct Events {
builder_event: TestEvent,
unconsensused_events: Vec<TestEvent>,
}
impl Events {
fn with_builder_event_sees_other(self) -> Self {
Self {
builder_event: self.builder_event.with_ancestors(),
..self
}
}
fn new_basic_setup(consensus_mode: ConsensusMode) -> Self {
let opaque_1 = Some(OPAQUE_HASHES[1]);
let opaque_2 = Some(OPAQUE_HASHES[2]);
let last_event_peer = PEER_IDS[6];
Self {
builder_event: TestEvent::new(last_event_peer, 100, None, consensus_mode),
unconsensused_events: vec![
TestEvent::new(PEER_IDS[2], 2, opaque_1, consensus_mode),
TestEvent::new(PEER_IDS[8], 11, opaque_1, consensus_mode),
TestEvent::new(last_event_peer, 6, opaque_1, consensus_mode),
TestEvent::new(last_event_peer, 10, opaque_2, consensus_mode),
],
}
}
}
fn fake_consistent_cmp(
lhs_key: &ObservationKey,
rhs_key: &ObservationKey,
is_reverse: bool,
) -> Ordering {
if is_reverse {
lhs_key.peer_index().cmp(&rhs_key.peer_index()).reverse()
} else {
lhs_key.peer_index().cmp(&rhs_key.peer_index())
}
}
fn test_find_interesting_content_for_event(data: TestSimpleData) {
let TestSimpleData {
events,
payload_properties,
expected_payloads,
is_reverse,
} = data;
let payloads = find_interesting_content_for_event(
&events.builder_event,
events.unconsensused_events.iter(),
|lhs_key, rhs_key| fake_consistent_cmp(lhs_key, rhs_key, is_reverse),
|event_x, _event_y| event_x.has_ancestors,
|_payload_key| payload_properties.is_already_interesting_content,
|_payload_key| payload_properties.is_interesting_payload,
);
assert_eq!(
expected_payloads,
payloads
.iter()
.map(|key| AssertObservationKey::new_from_key(key, &OPAQUE_HASHES))
.collect_vec()
);
}
#[test]
fn all_payloads_interesting() {
test_find_interesting_content_for_event(TestSimpleData {
events: Events::new_basic_setup(ConsensusMode::Supermajority)
.with_builder_event_sees_other(),
payload_properties: PayloadProperties {
is_already_interesting_content: false,
is_interesting_payload: true,
},
expected_payloads: vec![Supermajority(1), Supermajority(2)],
is_reverse: false,
});
}
#[test]
fn all_payloads_interesting_single() {
test_find_interesting_content_for_event(TestSimpleData {
events: Events::new_basic_setup(ConsensusMode::Single)
.with_builder_event_sees_other(),
payload_properties: PayloadProperties {
is_already_interesting_content: false,
is_interesting_payload: true,
},
expected_payloads: vec![
Single(1, PEER_IDS[6]),
Single(2, PEER_IDS[6]),
Single(1, PEER_IDS[8]),
Single(1, PEER_IDS[2]),
],
is_reverse: true,
});
}
#[test]
fn all_payloads_already_interesting() {
test_find_interesting_content_for_event(TestSimpleData {
events: Events::new_basic_setup(ConsensusMode::Supermajority)
.with_builder_event_sees_other(),
payload_properties: PayloadProperties {
is_already_interesting_content: true,
is_interesting_payload: true,
},
expected_payloads: vec![],
is_reverse: false,
});
}
#[test]
fn no_payloads_interesting() {
test_find_interesting_content_for_event(TestSimpleData {
events: Events::new_basic_setup(ConsensusMode::Supermajority)
.with_builder_event_sees_other(),
payload_properties: PayloadProperties {
is_already_interesting_content: false,
is_interesting_payload: false,
},
expected_payloads: vec![],
is_reverse: false,
});
}
}
}