Remove peer timestamp

Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
Alexis Sellier 2022-09-22 18:17:51 +02:00
parent 1fa85f0da3
commit 7169881849
No known key found for this signature in database
7 changed files with 47 additions and 70 deletions

View File

@ -4,8 +4,9 @@ pub mod filter;
pub mod message; pub mod message;
pub mod peer; pub mod peer;
use std::collections::{BTreeMap, VecDeque};
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::{collections::VecDeque, fmt, net, net::IpAddr}; use std::{fmt, net, net::IpAddr};
use crossbeam_channel as chan; use crossbeam_channel as chan;
use fastrand::Rng; use fastrand::Rng;
@ -236,7 +237,7 @@ impl<'r, T: WriteStorage<'r>, S: address_book::Store, G: crypto::Signer> Service
} }
/// Get the connected peers. /// Get the connected peers.
pub fn peers(&self) -> &Sessions { pub fn sessions(&self) -> &Sessions {
&self.sessions &self.sessions
} }
@ -651,6 +652,13 @@ pub struct Lookup {
pub remote: Vec<NodeId>, pub remote: Vec<NodeId>,
} }
/// Information on a peer, that we may or may not be connected to.
#[derive(Default, Debug)]
pub struct Peer {
/// Timestamp of the last message received from peer.
pub last_message: Timestamp,
}
/// Global service state used across peers. /// Global service state used across peers.
#[derive(Debug)] #[derive(Debug)]
pub struct Context<S, T, G> { pub struct Context<S, T, G> {
@ -660,6 +668,8 @@ pub struct Context<S, T, G> {
signer: G, signer: G,
/// Tracks the location of projects. /// Tracks the location of projects.
routing: Routing, routing: Routing,
/// Keeps track of peer states.
peers: BTreeMap<NodeId, Peer>,
/// Outgoing I/O queue. /// Outgoing I/O queue.
io: VecDeque<Io>, io: VecDeque<Io>,
/// Clock. Tells the time. /// Clock. Tells the time.
@ -700,6 +710,7 @@ where
signer, signer,
clock, clock,
routing: HashMap::with_hasher(rng.clone().into()), routing: HashMap::with_hasher(rng.clone().into()),
peers: BTreeMap::new(),
io: VecDeque::new(), io: VecDeque::new(),
storage, storage,
addrmgr, addrmgr,
@ -741,12 +752,7 @@ where
fn handshake_messages(&self) -> [Message; 4] { fn handshake_messages(&self) -> [Message; 4] {
let git = self.config.git_url.clone(); let git = self.config.git_url.clone();
[ [
Message::init( Message::init(self.node_id(), self.config.listen.clone(), git),
self.node_id(),
self.timestamp(),
self.config.listen.clone(),
git,
),
Message::node(self.node_announcement(), &self.signer), Message::node(self.node_announcement(), &self.signer),
Message::inventory(self.inventory_announcement().unwrap(), &self.signer), Message::inventory(self.inventory_announcement().unwrap(), &self.signer),
Message::subscribe(self.filter(), self.timestamp(), Timestamp::MAX), Message::subscribe(self.filter(), self.timestamp(), Timestamp::MAX),

View File

@ -156,13 +156,14 @@ impl InventoryAnnouncement {
/// Message payload. /// Message payload.
/// These are the messages peers send to each other. /// These are the messages peers send to each other.
///
/// "Announcement" messages are messages that are relayed between peers.
#[derive(Clone, PartialEq, Eq)] #[derive(Clone, PartialEq, Eq)]
pub enum Message { pub enum Message {
/// The first message sent to a peer after connection. /// The first message sent to a peer after connection.
Initialize { Initialize {
// TODO: This is currently untrusted. // TODO: This is currently untrusted.
id: NodeId, id: NodeId,
timestamp: Timestamp,
version: u32, version: u32,
addrs: Vec<Address>, addrs: Vec<Address>,
git: git::Url, git: git::Url,
@ -205,13 +206,12 @@ pub enum Message {
} }
impl Message { impl Message {
pub fn init(id: NodeId, timestamp: Timestamp, addrs: Vec<Address>, git: git::Url) -> Self { pub fn init(id: NodeId, addrs: Vec<Address>, git: git::Url) -> Self {
Self::Initialize { Self::Initialize {
id, id,
timestamp,
version: PROTOCOL_VERSION, version: PROTOCOL_VERSION,
addrs,
git, git,
addrs,
} }
} }

View File

@ -29,7 +29,7 @@ pub enum SessionError {
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 timestamp: {0}")] #[error("invalid announcement timestamp: {0}")]
InvalidTimestamp(u64), InvalidTimestamp(u64),
#[error("peer misbehaved")] #[error("peer misbehaved")]
Misbehavior, Misbehavior,
@ -47,8 +47,6 @@ pub struct Session {
pub persistent: bool, pub persistent: bool,
/// Peer connection state. /// Peer connection state.
pub state: SessionState, pub state: SessionState,
/// Last known peer time.
pub timestamp: Timestamp,
/// Peer subscription. /// Peer subscription.
pub subscribe: Option<Subscribe>, pub subscribe: Option<Subscribe>,
@ -64,7 +62,6 @@ impl Session {
addr, addr,
state: SessionState::default(), state: SessionState::default(),
link, link,
timestamp: Timestamp::default(),
subscribe: None, subscribe: None,
persistent, persistent,
attempts: 0, attempts: 0,
@ -110,17 +107,11 @@ impl Session {
SessionState::Initial, SessionState::Initial,
Message::Initialize { Message::Initialize {
id, id,
timestamp,
version, version,
addrs, addrs,
git, git,
}, },
) => { ) => {
let now = ctx.timestamp();
if timestamp.abs_diff(now) > MAX_TIME_DELTA.as_secs() {
return Err(SessionError::InvalidTimestamp(timestamp));
}
if version != PROTOCOL_VERSION { if version != PROTOCOL_VERSION {
return Err(SessionError::WrongVersion(version)); return Err(SessionError::WrongVersion(version));
} }
@ -154,18 +145,17 @@ impl Session {
signature, signature,
}, },
) => { ) => {
// FIXME: This is wrong, we are comparing timestamps of different peers.
let now = ctx.clock.local_time(); let now = ctx.clock.local_time();
let last = self.timestamp; let peer = ctx.peers.entry(node).or_insert_with(Peer::default);
// Don't allow messages from too far in the past or future. // Don't allow messages from too far in the future.
if message.timestamp.abs_diff(now.as_secs()) > MAX_TIME_DELTA.as_secs() { if message.timestamp.saturating_sub(now.as_secs()) > MAX_TIME_DELTA.as_secs() {
return Err(SessionError::InvalidTimestamp(message.timestamp)); return Err(SessionError::InvalidTimestamp(message.timestamp));
} }
// Discard inventory messages we've already seen, otherwise update // Discard inventory messages we've already seen, otherwise update
// out last seen time. // out last seen time.
if message.timestamp > last { if message.timestamp > peer.last_message {
self.timestamp = message.timestamp; peer.last_message = message.timestamp;
} else { } else {
return Ok(None); return Ok(None);
} }
@ -188,6 +178,8 @@ impl Session {
signature, signature,
}, },
) => { ) => {
// FIXME: Check message timestamp.
if message.verify(&node, &signature) { if message.verify(&node, &signature) {
// TODO: Buffer/throttle fetches. // TODO: Buffer/throttle fetches.
// TODO: Check that we're tracking this user as well. // TODO: Check that we're tracking this user as well.
@ -222,6 +214,8 @@ impl Session {
signature, signature,
}, },
) => { ) => {
// FIXME: Check message timestamp.
if !message.verify(&node, &signature) { if !message.verify(&node, &signature) {
return Err(SessionError::Misbehavior); return Err(SessionError::Misbehavior);
} }

View File

@ -150,12 +150,7 @@ where
self.service.connected(remote, &local, Link::Inbound); self.service.connected(remote, &local, Link::Inbound);
self.receive( self.receive(
&remote, &remote,
Message::init( Message::init(peer.node_id(), vec![Address::from(remote)], git),
peer.node_id(),
self.local_time().as_secs(),
vec![Address::from(remote)],
git,
),
); );
let mut msgs = self.messages(&remote); let mut msgs = self.messages(&remote);
@ -182,12 +177,7 @@ where
let git = peer.config().git_url.clone(); let git = peer.config().git_url.clone();
self.receive( self.receive(
&remote, &remote,
Message::init( Message::init(peer.node_id(), peer.config().listen.clone(), git),
peer.node_id(),
self.local_time().as_secs(),
peer.config().listen.clone(),
git,
),
); );
} }

View File

@ -20,8 +20,8 @@ use crate::test::signer::MockSigner;
use crate::test::simulator; use crate::test::simulator;
use crate::test::simulator::{Peer as _, Simulation}; use crate::test::simulator::{Peer as _, Simulation};
use crate::test::storage::MockStorage; use crate::test::storage::MockStorage;
use crate::LocalTime;
use crate::{client, identity, rad, service, storage, test}; use crate::{client, identity, rad, service, storage, test};
use crate::{Link, LocalTime};
// NOTE // NOTE
// //
@ -42,7 +42,7 @@ fn test_outbound_connection() {
let peers = alice let peers = alice
.service .service
.peers() .sessions()
.negotiated() .negotiated()
.map(|(ip, _)| *ip) .map(|(ip, _)| *ip)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -62,7 +62,7 @@ fn test_inbound_connection() {
let peers = alice let peers = alice
.service .service
.peers() .sessions()
.negotiated() .negotiated()
.map(|(ip, _)| *ip) .map(|(ip, _)| *ip)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -103,27 +103,6 @@ fn test_wrong_peer_version() {
// TODO // TODO
} }
#[test]
fn test_handshake_invalid_timestamp() {
let mut alice = Peer::new("alice", [7, 7, 7, 7], MockStorage::empty());
let bob = Peer::new("bob", [8, 8, 8, 8], MockStorage::empty());
let time_delta = MAX_TIME_DELTA.as_secs() + 1;
let local = std::net::SocketAddr::new(bob.ip, bob.rng.u16(..));
alice.initialize();
alice.connected(bob.addr(), &local, Link::Inbound);
alice.receive(
&bob.addr(),
Message::init(
bob.node_id(),
alice.timestamp() - time_delta,
vec![],
bob.git_url(),
),
);
assert_matches!(alice.outbox().next(), Some(Io::Disconnect(addr, _)) if addr == bob.addr());
}
#[test] #[test]
#[ignore] #[ignore]
fn test_wrong_peer_magic() { fn test_wrong_peer_magic() {
@ -211,7 +190,7 @@ 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());
let two_hours = 3600 * 2; let two_hours = 3600 * 2;
let timestamp = alice.local_time.as_secs() - two_hours; let timestamp = alice.local_time.as_secs() + two_hours;
alice.connect_to(&bob); alice.connect_to(&bob);
alice.receive( alice.receive(
@ -341,7 +320,7 @@ fn test_persistent_peer_reconnect() {
sim.run_while([&mut alice, &mut bob, &mut eve], |s| !s.is_settled()); sim.run_while([&mut alice, &mut bob, &mut eve], |s| !s.is_settled());
let ips = alice let ips = alice
.peers() .sessions()
.negotiated() .negotiated()
.map(|(ip, _)| *ip) .map(|(ip, _)| *ip)
.collect::<Vec<_>>(); .collect::<Vec<_>>();

View File

@ -132,13 +132,11 @@ impl wire::Encode for Message {
match self { match self {
Self::Initialize { Self::Initialize {
id, id,
timestamp,
version, version,
addrs, addrs,
git, git,
} => { } => {
n += id.encode(writer)?; n += id.encode(writer)?;
n += timestamp.encode(writer)?;
n += version.encode(writer)?; n += version.encode(writer)?;
n += addrs.as_slice().encode(writer)?; n += addrs.as_slice().encode(writer)?;
n += git.encode(writer)?; n += git.encode(writer)?;
@ -191,14 +189,12 @@ impl wire::Decode for Message {
match MessageType::try_from(type_id) { match MessageType::try_from(type_id) {
Ok(MessageType::Initialize) => { Ok(MessageType::Initialize) => {
let id = NodeId::decode(reader)?; let id = NodeId::decode(reader)?;
let timestamp = Timestamp::decode(reader)?;
let version = u32::decode(reader)?; let version = u32::decode(reader)?;
let addrs = Vec::<Address>::decode(reader)?; let addrs = Vec::<Address>::decode(reader)?;
let git = git::Url::decode(reader)?; let git = git::Url::decode(reader)?;
Ok(Self::Initialize { Ok(Self::Initialize {
id, id,
timestamp,
version, version,
addrs, addrs,
git, git,

View File

@ -135,6 +135,18 @@ impl std::hash::Hash for PublicKey {
} }
} }
impl PartialOrd for PublicKey {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.0.as_ref().partial_cmp(other.as_ref())
}
}
impl Ord for PublicKey {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.as_ref().cmp(other.as_ref())
}
}
impl fmt::Display for PublicKey { impl fmt::Display for PublicKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_human()) write!(f, "{}", self.to_human())