2025-01-03 14:00:20 -05:00
|
|
|
use core::time::Duration;
|
2025-01-03 13:04:27 -05:00
|
|
|
|
2025-01-03 14:00:20 -05:00
|
|
|
use blake2::{Digest, Blake2s256};
|
|
|
|
|
|
|
|
|
|
use borsh::{BorshSerialize, BorshDeserialize};
|
|
|
|
|
|
|
|
|
|
use libp2p::gossipsub::{
|
2025-01-09 00:06:51 -05:00
|
|
|
IdentTopic, MessageId, MessageAuthenticity, ValidationMode, ConfigBuilder, IdentityTransform,
|
|
|
|
|
AllowAllSubscriptionFilter, Behaviour,
|
2025-01-03 14:00:20 -05:00
|
|
|
};
|
2025-01-05 01:23:28 -05:00
|
|
|
pub use libp2p::gossipsub::Event;
|
2025-01-03 14:00:20 -05:00
|
|
|
|
|
|
|
|
use serai_cosign::SignedCosign;
|
|
|
|
|
|
|
|
|
|
// Block size limit + 16 KB of space for signatures/metadata
|
2025-01-07 18:09:25 -05:00
|
|
|
pub(crate) const MAX_LIBP2P_GOSSIP_MESSAGE_SIZE: usize = tributary::BLOCK_SIZE_LIMIT + 16384;
|
2025-01-03 14:00:20 -05:00
|
|
|
|
|
|
|
|
const LIBP2P_PROTOCOL: &str = "/serai/coordinator/gossip/1.0.0";
|
|
|
|
|
const BASE_TOPIC: &str = "/";
|
|
|
|
|
|
2025-01-08 19:39:09 -05:00
|
|
|
fn topic_for_tributary(tributary: [u8; 32]) -> IdentTopic {
|
|
|
|
|
IdentTopic::new(format!("/tributary/{}", hex::encode(tributary)))
|
2025-01-03 14:00:20 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, BorshSerialize, BorshDeserialize)]
|
|
|
|
|
pub(crate) enum Message {
|
2025-01-08 19:39:09 -05:00
|
|
|
Tributary { tributary: [u8; 32], message: Vec<u8> },
|
2025-01-03 14:00:20 -05:00
|
|
|
Cosign(SignedCosign),
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-07 16:34:19 -05:00
|
|
|
impl Message {
|
2025-01-08 23:16:04 -05:00
|
|
|
pub(crate) fn topic(&self) -> IdentTopic {
|
2025-01-07 16:34:19 -05:00
|
|
|
match self {
|
2025-01-08 23:16:04 -05:00
|
|
|
Message::Tributary { tributary, .. } => topic_for_tributary(*tributary),
|
|
|
|
|
Message::Cosign(_) => IdentTopic::new(BASE_TOPIC),
|
2025-01-07 16:34:19 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-03 14:00:20 -05:00
|
|
|
pub(crate) type Behavior = Behaviour<IdentityTransform, AllowAllSubscriptionFilter>;
|
|
|
|
|
|
|
|
|
|
pub(crate) fn new_behavior() -> Behavior {
|
|
|
|
|
// The latency used by the Tendermint protocol, used here as the gossip epoch duration
|
|
|
|
|
// libp2p-rs defaults to 1 second, whereas ours will be ~2
|
|
|
|
|
let heartbeat_interval = tributary::tendermint::LATENCY_TIME;
|
|
|
|
|
// The amount of heartbeats which will occur within a single Tributary block
|
|
|
|
|
let heartbeats_per_block = tributary::tendermint::TARGET_BLOCK_TIME.div_ceil(heartbeat_interval);
|
|
|
|
|
// libp2p-rs defaults to 5, whereas ours will be ~8
|
|
|
|
|
let heartbeats_to_keep = 2 * heartbeats_per_block;
|
|
|
|
|
// libp2p-rs defaults to 3 whereas ours will be ~4
|
|
|
|
|
let heartbeats_to_gossip = heartbeats_per_block;
|
|
|
|
|
|
|
|
|
|
let config = ConfigBuilder::default()
|
|
|
|
|
.protocol_id_prefix(LIBP2P_PROTOCOL)
|
|
|
|
|
.history_length(usize::try_from(heartbeats_to_keep).unwrap())
|
|
|
|
|
.history_gossip(usize::try_from(heartbeats_to_gossip).unwrap())
|
|
|
|
|
.heartbeat_interval(Duration::from_millis(heartbeat_interval.into()))
|
|
|
|
|
.max_transmit_size(MAX_LIBP2P_GOSSIP_MESSAGE_SIZE)
|
|
|
|
|
.duplicate_cache_time(Duration::from_millis((heartbeats_to_keep * heartbeat_interval).into()))
|
|
|
|
|
.validation_mode(ValidationMode::Anonymous)
|
|
|
|
|
// Uses a content based message ID to avoid duplicates as much as possible
|
|
|
|
|
.message_id_fn(|msg| {
|
|
|
|
|
MessageId::new(&Blake2s256::digest([msg.topic.as_str().as_bytes(), &msg.data].concat()))
|
|
|
|
|
})
|
|
|
|
|
.build();
|
|
|
|
|
|
2025-01-04 22:21:23 -05:00
|
|
|
let mut gossip = Behavior::new(MessageAuthenticity::Anonymous, config.unwrap()).unwrap();
|
2025-01-03 14:00:20 -05:00
|
|
|
|
|
|
|
|
// Subscribe to the base topic
|
|
|
|
|
let topic = IdentTopic::new(BASE_TOPIC);
|
2025-01-04 22:21:23 -05:00
|
|
|
let _ = gossip.subscribe(&topic);
|
2025-01-03 14:00:20 -05:00
|
|
|
|
2025-01-04 22:21:23 -05:00
|
|
|
gossip
|
2025-01-03 14:00:20 -05:00
|
|
|
}
|