fix(gossipsub): prevent erroneously duplicate message IDs

Previously, we only mutably borrowed the `last_seq_no` in the current scope but did not modify the underlying number. This is because `u64` is copy and calling `wrapping_add` consumes `self` so the compiler just copied it. We introduce a new-type instead that is not `Copy`.

Additionally, `wrapping_add` and initializing with a random u64 might actually warp the number and thus not give us sequential numbers as intended in #3551. To solve this, we initialize with the current unix timestamp in nanoseconds. This allows a node to publish 1000000 messages a second and still not reuse sequence numbers even after a restart / re-initialization of the configuration. This is also what the go implementation does.

Resolves #3714.

Co-authored-by: Thomas Eizinger <thomas@eizinger.io>

Pull-Request: #3716.
This commit is contained in:
Darius Clark
2023-04-04 06:52:16 -04:00
committed by GitHub
parent e350630caf
commit f2a7457fde
4 changed files with 39 additions and 11 deletions

View File

@ -64,6 +64,7 @@ use crate::types::{
use crate::types::{PeerConnections, PeerKind, Rpc};
use crate::{rpc_proto::proto, TopicScoreParams};
use crate::{PublishError, SubscriptionError, ValidationError};
use instant::SystemTime;
use quick_protobuf::{MessageWrite, Writer};
use std::{cmp::Ordering::Equal, fmt::Debug};
use wasm_timer::Interval;
@ -149,20 +150,44 @@ pub enum Event {
/// A data structure for storing configuration for publishing messages. See [`MessageAuthenticity`]
/// for further details.
#[allow(clippy::large_enum_variant)]
#[derive(Clone)]
enum PublishConfig {
Signing {
keypair: Keypair,
author: PeerId,
inline_key: Option<Vec<u8>>,
last_seq_no: u64, // This starts from a random number and increases then overflows (if
// required)
last_seq_no: SequenceNumber,
},
Author(PeerId),
RandomAuthor,
Anonymous,
}
/// A strictly linearly increasing sequence number.
///
/// We start from the current time as unix timestamp in milliseconds.
#[derive(Debug)]
struct SequenceNumber(u64);
impl SequenceNumber {
fn new() -> Self {
let unix_timestamp = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.expect("time to be linear")
.as_nanos();
Self(unix_timestamp as u64)
}
fn next(&mut self) -> u64 {
self.0 = self
.0
.checked_add(1)
.expect("to not exhaust u64 space for sequence numbers");
self.0
}
}
impl PublishConfig {
pub fn get_own_id(&self) -> Option<&PeerId> {
match self {
@ -192,7 +217,7 @@ impl From<MessageAuthenticity> for PublishConfig {
keypair,
author: public_key.to_peer_id(),
inline_key: key,
last_seq_no: rand::random(),
last_seq_no: SequenceNumber::new(),
}
}
MessageAuthenticity::Author(peer_id) => PublishConfig::Author(peer_id),
@ -2757,12 +2782,9 @@ where
ref keypair,
author,
inline_key,
mut last_seq_no,
last_seq_no,
} => {
// Increment the last sequence number
last_seq_no = last_seq_no.wrapping_add(1);
let sequence_number = last_seq_no;
let sequence_number = last_seq_no.next();
let signature = {
let message = proto::Message {