node: Use timestamps instead of sequence numbers

Signed-off-by: Alexis Sellier <self@cloudhead.io>
This commit is contained in:
Alexis Sellier 2022-08-27 12:43:27 +02:00
parent 32591e6aa4
commit 9ca982ee28
No known key found for this signature in database
2 changed files with 47 additions and 36 deletions

View File

@ -25,6 +25,8 @@ use crate::storage::{Inventory, ReadStorage, Remotes, Unverified, WriteStorage};
pub type PeerId = IpAddr; pub type PeerId = IpAddr;
/// Network routing table. Keeps track of where projects are hosted. /// Network routing table. Keeps track of where projects are hosted.
pub type Routing = HashMap<ProjId, HashSet<PeerId>>; pub type Routing = HashMap<ProjId, HashSet<PeerId>>;
/// Seconds since epoch.
pub type Timestamp = u64;
pub const NETWORK_MAGIC: u32 = 0x819b43d9; pub const NETWORK_MAGIC: u32 = 0x819b43d9;
pub const DEFAULT_PORT: u16 = 8776; pub const DEFAULT_PORT: u16 = 8776;
@ -67,8 +69,8 @@ pub enum Message {
/// Send our inventory to a peer. Sent in response to [`Message::GetInventory`]. /// Send our inventory to a peer. Sent in response to [`Message::GetInventory`].
/// Nb. This should be the whole inventory, not a partial update. /// Nb. This should be the whole inventory, not a partial update.
Inventory { Inventory {
seq: u64,
inv: Inventory, inv: Inventory,
timestamp: Timestamp,
/// Original peer this inventory came from. We don't set this when we /// Original peer this inventory came from. We don't set this when we
/// are the originator, only when relaying. /// are the originator, only when relaying.
origin: Option<PeerId>, origin: Option<PeerId>,
@ -96,13 +98,11 @@ impl Message {
where where
T: storage::ReadStorage, T: storage::ReadStorage,
{ {
ctx.seq += 1; let timestamp = ctx.timestamp();
let seq = ctx.seq;
let inv = ctx.storage.inventory()?; let inv = ctx.storage.inventory()?;
Ok(Self::Inventory { Ok(Self::Inventory {
seq, timestamp,
inv, inv,
origin: None, origin: None,
}) })
@ -656,10 +656,8 @@ pub struct Context<S, T> {
io: VecDeque<Io<(), DisconnectReason>>, io: VecDeque<Io<(), DisconnectReason>>,
/// Clock. Tells the time. /// Clock. Tells the time.
clock: RefClock, clock: RefClock,
/// Sequence number of known peers. /// Timestamps of known peers.
seqs: HashMap<PeerId, u64>, timestamps: HashMap<PeerId, u64>,
/// Our own sequence number.
seq: u64,
/// Project storage. /// Project storage.
storage: T, storage: T,
/// Peer address manager. /// Peer address manager.
@ -683,9 +681,8 @@ where
config, config,
clock, clock,
routing: HashMap::with_hasher(rng.clone().into()), routing: HashMap::with_hasher(rng.clone().into()),
seqs: HashMap::with_hasher(rng.clone().into()), timestamps: HashMap::with_hasher(rng.clone().into()),
io: VecDeque::new(), io: VecDeque::new(),
seq: 0,
storage, storage,
addrmgr, addrmgr,
rng, rng,
@ -707,6 +704,11 @@ where
} }
} }
/// Get current local timestamp.
fn timestamp(&self) -> Timestamp {
self.clock.local_time().as_secs()
}
/// Disconnect a peer. /// Disconnect a peer.
fn disconnect(&mut self, addr: net::SocketAddr, reason: DisconnectReason) { fn disconnect(&mut self, addr: net::SocketAddr, reason: DisconnectReason) {
self.io.push_back(Io::Disconnect(addr, reason)); self.io.push_back(Io::Disconnect(addr, reason));
@ -795,8 +797,8 @@ pub enum PeerError {
WrongMagic(u32), WrongMagic(u32),
#[error("wrong protocol version in message: {0}")] #[error("wrong protocol version in message: {0}")]
WrongVersion(u32), WrongVersion(u32),
#[error("invalid inventory sequence number: {0}")] #[error("invalid inventory timestamp: {0}")]
InvalidSequenceNumber(u64), InvalidTimestamp(u64),
} }
#[derive(Debug)] #[derive(Debug)]
@ -876,15 +878,22 @@ impl Peer {
let inventory = Message::inventory(ctx).unwrap(); let inventory = Message::inventory(ctx).unwrap();
ctx.write(self.addr, inventory); ctx.write(self.addr, inventory);
} }
Message::Inventory { seq: 0, .. } => { Message::Inventory { timestamp: 0, .. } => {
return Err(PeerError::InvalidSequenceNumber(0)); return Err(PeerError::InvalidTimestamp(0));
} }
Message::Inventory { seq, inv, origin } => { Message::Inventory {
let sequence = ctx.seqs.entry(self.id()).or_insert(0); timestamp,
inv,
origin,
} => {
let last = ctx
.timestamps
.entry(self.id())
.or_insert_with(Timestamp::default);
// Discard inventory messages from sequence numbers we've already seen. // Discard inventory messages from timestamps in the past.
if seq > *sequence { if timestamp > *last {
*sequence = seq; *last = timestamp;
} else { } else {
return Ok(None); return Ok(None);
} }
@ -892,7 +901,7 @@ impl Peer {
if ctx.config.relay { if ctx.config.relay {
return Ok(Some(Message::Inventory { return Ok(Some(Message::Inventory {
seq, timestamp,
inv, inv,
origin: origin.or_else(|| Some(self.id())), origin: origin.or_else(|| Some(self.id())),
})); }));

View File

@ -105,12 +105,13 @@ fn test_wrong_peer_magic() {
fn test_inventory_fetch() { fn test_inventory_fetch() {
let mut alice = Peer::new("alice", [7, 7, 7, 7], MockStorage::empty()); let mut alice = Peer::new("alice", [7, 7, 7, 7], MockStorage::empty());
let bob = Peer::new("bob", [8, 8, 8, 8], MockStorage::empty()); let bob = Peer::new("bob", [8, 8, 8, 8], MockStorage::empty());
let now = LocalTime::now().as_secs();
alice.connect_to(&bob.addr()); alice.connect_to(&bob.addr());
alice.receive( alice.receive(
&bob.addr(), &bob.addr(),
Message::Inventory { Message::Inventory {
seq: 1, timestamp: now,
inv: vec![], inv: vec![],
origin: None, origin: None,
}, },
@ -118,7 +119,7 @@ fn test_inventory_fetch() {
} }
#[test] #[test]
fn test_inventory_relay_bad_seq() { fn test_inventory_relay_bad_timestamp() {
let mut alice = Peer::new("alice", [7, 7, 7, 7], MockStorage::empty()); let mut alice = Peer::new("alice", [7, 7, 7, 7], MockStorage::empty());
let bob = Peer::new("bob", [8, 8, 8, 8], MockStorage::empty()); let bob = Peer::new("bob", [8, 8, 8, 8], MockStorage::empty());
@ -126,15 +127,15 @@ fn test_inventory_relay_bad_seq() {
alice.receive( alice.receive(
&bob.addr(), &bob.addr(),
Message::Inventory { Message::Inventory {
seq: 0, timestamp: 0,
inv: vec![], inv: vec![],
origin: None, origin: None,
}, },
); );
assert_matches!( assert_matches!(
alice.outbox().next(), alice.outbox().next(),
Some(Io::Disconnect(addr, DisconnectReason::Error(PeerError::InvalidSequenceNumber(seq)))) Some(Io::Disconnect(addr, DisconnectReason::Error(PeerError::InvalidTimestamp(t))))
if addr == bob.addr() && seq == 0 if addr == bob.addr() && t == 0
); );
} }
@ -145,6 +146,7 @@ fn test_inventory_relay() {
let bob = Peer::new("bob", [8, 8, 8, 8], MockStorage::empty()); let bob = Peer::new("bob", [8, 8, 8, 8], MockStorage::empty());
let eve = Peer::new("eve", [9, 9, 9, 9], MockStorage::empty()); let eve = Peer::new("eve", [9, 9, 9, 9], MockStorage::empty());
let inv = vec![]; let inv = vec![];
let now = LocalTime::now().as_secs();
// Inventory from Bob relayed to Eve. // Inventory from Bob relayed to Eve.
alice.connect_to(&bob.addr()); alice.connect_to(&bob.addr());
@ -152,15 +154,15 @@ fn test_inventory_relay() {
alice.receive( alice.receive(
&bob.addr(), &bob.addr(),
Message::Inventory { Message::Inventory {
seq: 1, timestamp: now,
inv: inv.clone(), inv: inv.clone(),
origin: None, origin: None,
}, },
); );
assert_matches!( assert_matches!(
alice.messages(&eve.addr()).next(), alice.messages(&eve.addr()).next(),
Some(Message::Inventory { seq, origin, .. }) Some(Message::Inventory { timestamp, origin, .. })
if origin == Some(bob.ip) && seq == 1 if origin == Some(bob.ip) && timestamp == now
); );
assert_matches!( assert_matches!(
alice.messages(&bob.addr()).next(), alice.messages(&bob.addr()).next(),
@ -171,7 +173,7 @@ fn test_inventory_relay() {
alice.receive( alice.receive(
&bob.addr(), &bob.addr(),
Message::Inventory { Message::Inventory {
seq: 1, timestamp: now,
inv: inv.clone(), inv: inv.clone(),
origin: None, origin: None,
}, },
@ -185,15 +187,15 @@ fn test_inventory_relay() {
alice.receive( alice.receive(
&bob.addr(), &bob.addr(),
Message::Inventory { Message::Inventory {
seq: 2, timestamp: now + 1,
inv: inv.clone(), inv: inv.clone(),
origin: None, origin: None,
}, },
); );
assert_matches!( assert_matches!(
alice.messages(&eve.addr()).next(), alice.messages(&eve.addr()).next(),
Some(Message::Inventory { seq, origin, .. }) Some(Message::Inventory { timestamp, origin, .. })
if origin == Some(bob.ip) && seq == 2, if origin == Some(bob.ip) && timestamp == now + 1,
"Sending a new inventory does trigger the relay" "Sending a new inventory does trigger the relay"
); );
@ -201,15 +203,15 @@ fn test_inventory_relay() {
alice.receive( alice.receive(
&eve.addr(), &eve.addr(),
Message::Inventory { Message::Inventory {
seq: 4, timestamp: now,
inv, inv,
origin: None, origin: None,
}, },
); );
assert_matches!( assert_matches!(
alice.messages(&bob.addr()).next(), alice.messages(&bob.addr()).next(),
Some(Message::Inventory { seq, origin, .. }) Some(Message::Inventory { timestamp, origin, .. })
if origin == Some(eve.ip) && seq == 4 if origin == Some(eve.ip) && timestamp == now
); );
} }