node: Add `NodeId`
Signed-off-by: Alexis Sellier <self@cloudhead.io>
This commit is contained in:
parent
66d580019b
commit
ad1f25b3f5
|
|
@ -4,10 +4,19 @@ use ed25519_consensus as ed25519;
|
|||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Serialize, Deserialize, Eq, Debug, Clone)]
|
||||
pub trait Signer {
|
||||
/// Return this signer's public/verification key.
|
||||
fn public_key(&self) -> &PublicKey;
|
||||
}
|
||||
|
||||
/// The public/verification key.
|
||||
#[derive(Serialize, Deserialize, Eq, Debug, Copy, Clone)]
|
||||
#[serde(transparent)]
|
||||
pub struct PublicKey(pub ed25519::VerificationKey);
|
||||
|
||||
/// The private/signing key.
|
||||
pub type SecretKey = ed25519::SigningKey;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum PublicKeyError {
|
||||
#[error("invalid length {0}")]
|
||||
|
|
@ -36,6 +45,12 @@ impl PartialEq for PublicKey {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<ed25519::VerificationKey> for PublicKey {
|
||||
fn from(other: ed25519::VerificationKey) -> Self {
|
||||
Self(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PublicKey {
|
||||
pub fn encode(&self) -> String {
|
||||
multibase::encode(multibase::Base::Base58Btc, &self.0)
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ pub fn list_remotes(url: &Url) -> Result<Remotes<Unverified>, ListRefsError> {
|
|||
for r in refs {
|
||||
let (id, refname) = parse_ref::<UserId>(r.name())?;
|
||||
let entry = remotes
|
||||
.entry(id.clone())
|
||||
.entry(id)
|
||||
.or_insert_with(|| Remote::new(id, HashMap::default()));
|
||||
|
||||
entry.refs.insert(refname.to_string(), r.oid().into());
|
||||
|
|
|
|||
|
|
@ -123,8 +123,8 @@ mod test {
|
|||
|
||||
let mut hm = HashSet::new();
|
||||
|
||||
assert!(hm.insert(a.clone()));
|
||||
assert!(hm.insert(b.clone()));
|
||||
assert!(hm.insert(a));
|
||||
assert!(hm.insert(b));
|
||||
assert!(!hm.insert(a));
|
||||
assert!(!hm.insert(b));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,15 +16,16 @@ use crate::address_book::AddressBook;
|
|||
use crate::address_manager::AddressManager;
|
||||
use crate::clock::RefClock;
|
||||
use crate::collections::{HashMap, HashSet};
|
||||
use crate::crypto;
|
||||
use crate::decoder::Decoder;
|
||||
use crate::identity::{ProjId, UserId};
|
||||
use crate::storage::{self, WriteRepository};
|
||||
use crate::storage::{Inventory, ReadStorage, Remotes, Unverified, WriteStorage};
|
||||
|
||||
/// Network peer identifier.
|
||||
pub type PeerId = IpAddr;
|
||||
/// Network node identifier.
|
||||
pub type NodeId = crypto::PublicKey;
|
||||
/// Network routing table. Keeps track of where projects are hosted.
|
||||
pub type Routing = HashMap<ProjId, HashSet<PeerId>>;
|
||||
pub type Routing = HashMap<ProjId, HashSet<NodeId>>;
|
||||
/// Seconds since epoch.
|
||||
pub type Timestamp = u64;
|
||||
|
||||
|
|
@ -69,6 +70,8 @@ pub struct Envelope {
|
|||
pub enum Message {
|
||||
/// Say hello to a peer. This is the first message sent to a peer after connection.
|
||||
Hello {
|
||||
// TODO: This is currently untrusted.
|
||||
id: NodeId,
|
||||
timestamp: Timestamp,
|
||||
version: u32,
|
||||
addrs: Vec<Address>,
|
||||
|
|
@ -87,7 +90,7 @@ pub enum Message {
|
|||
timestamp: Timestamp,
|
||||
/// Original peer this inventory came from. We don't set this when we
|
||||
/// are the originator, only when relaying.
|
||||
origin: Option<PeerId>,
|
||||
origin: Option<NodeId>,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -101,8 +104,9 @@ impl From<Message> for Envelope {
|
|||
}
|
||||
|
||||
impl Message {
|
||||
pub fn hello(timestamp: Timestamp, addrs: Vec<Address>, git: Url) -> Self {
|
||||
pub fn hello(id: NodeId, timestamp: Timestamp, addrs: Vec<Address>, git: Url) -> Self {
|
||||
Self::Hello {
|
||||
id,
|
||||
timestamp,
|
||||
version: PROTOCOL_VERSION,
|
||||
addrs,
|
||||
|
|
@ -110,7 +114,7 @@ impl Message {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn inventory<S, T>(ctx: &mut Context<S, T>) -> Result<Self, storage::Error>
|
||||
pub fn inventory<S, T, G>(ctx: &mut Context<S, T, G>) -> Result<Self, storage::Error>
|
||||
where
|
||||
T: storage::ReadStorage,
|
||||
{
|
||||
|
|
@ -219,11 +223,11 @@ impl Config {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Protocol<S, T> {
|
||||
pub struct Protocol<S, T, G> {
|
||||
/// Peers currently or recently connected.
|
||||
peers: Peers,
|
||||
/// Protocol state that isn't peer-specific.
|
||||
context: Context<S, T>,
|
||||
context: Context<S, T, G>,
|
||||
/// Whether our local inventory no long represents what we have announced to the network.
|
||||
out_of_sync: bool,
|
||||
/// Last time the protocol was idle.
|
||||
|
|
@ -238,12 +242,19 @@ pub struct Protocol<S, T> {
|
|||
start_time: LocalTime,
|
||||
}
|
||||
|
||||
impl<T: ReadStorage + WriteStorage, S: address_book::Store> Protocol<S, T> {
|
||||
pub fn new(config: Config, clock: RefClock, storage: T, addresses: S, rng: Rng) -> Self {
|
||||
impl<T: ReadStorage + WriteStorage, S: address_book::Store, G: crypto::Signer> Protocol<S, T, G> {
|
||||
pub fn new(
|
||||
config: Config,
|
||||
clock: RefClock,
|
||||
storage: T,
|
||||
addresses: S,
|
||||
signer: G,
|
||||
rng: Rng,
|
||||
) -> Self {
|
||||
let addrmgr = AddressManager::new(addresses);
|
||||
|
||||
Self {
|
||||
context: Context::new(config, clock, storage, addrmgr, rng.clone()),
|
||||
context: Context::new(config, clock, storage, addrmgr, signer, rng.clone()),
|
||||
peers: Peers::new(rng),
|
||||
out_of_sync: false,
|
||||
last_idle: LocalTime::default(),
|
||||
|
|
@ -254,15 +265,15 @@ impl<T: ReadStorage + WriteStorage, S: address_book::Store> Protocol<S, T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn disconnect(&mut self, peer: &PeerId, reason: DisconnectReason) {
|
||||
if let Some(addr) = self.peers.get(peer).map(|p| p.addr) {
|
||||
pub fn disconnect(&mut self, remote: &IpAddr, reason: DisconnectReason) {
|
||||
if let Some(addr) = self.peers.get(remote).map(|p| p.addr) {
|
||||
self.context.disconnect(addr, reason);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn providers(&self, proj: &ProjId) -> Box<dyn Iterator<Item = &Peer> + '_> {
|
||||
if let Some(peers) = self.routing.get(proj) {
|
||||
Box::new(peers.iter().filter_map(|id| self.peers.get(id)))
|
||||
Box::new(peers.iter().filter_map(|id| self.peers.by_id(id)))
|
||||
} else {
|
||||
Box::new(std::iter::empty())
|
||||
}
|
||||
|
|
@ -302,7 +313,7 @@ impl<T: ReadStorage + WriteStorage, S: address_book::Store> Protocol<S, T> {
|
|||
/// Returns a sorted list from the closest peer to the furthest.
|
||||
/// Peers with more trackings in common score score higher.
|
||||
#[allow(unused)]
|
||||
pub fn closest_peers(&self, n: usize) -> Vec<PeerId> {
|
||||
pub fn closest_peers(&self, n: usize) -> Vec<NodeId> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
|
|
@ -348,7 +359,7 @@ impl<T: ReadStorage + WriteStorage, S: address_book::Store> Protocol<S, T> {
|
|||
.context
|
||||
.routing
|
||||
.get(proj)
|
||||
.map_or(vec![], |r| r.iter().copied().collect()),
|
||||
.map_or(vec![], |r| r.iter().cloned().collect()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -403,10 +414,11 @@ impl<T: ReadStorage + WriteStorage, S: address_book::Store> Protocol<S, T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<S, T> nakamoto::Protocol for Protocol<S, T>
|
||||
impl<S, T, G> nakamoto::Protocol for Protocol<S, T, G>
|
||||
where
|
||||
T: ReadStorage + WriteStorage + 'static,
|
||||
S: address_book::Store,
|
||||
G: crypto::Signer,
|
||||
{
|
||||
type Event = ();
|
||||
type Command = Command;
|
||||
|
|
@ -488,11 +500,11 @@ where
|
|||
}
|
||||
|
||||
fn attempted(&mut self, addr: &std::net::SocketAddr) {
|
||||
let id = addr.ip();
|
||||
let ip = addr.ip();
|
||||
let persistent = self.context.config.is_persistent(addr);
|
||||
let mut peer = self
|
||||
.peers
|
||||
.entry(id)
|
||||
.entry(ip)
|
||||
.or_insert_with(|| Peer::new(*addr, Link::Outbound, persistent));
|
||||
|
||||
peer.attempts += 1;
|
||||
|
|
@ -504,9 +516,9 @@ where
|
|||
_local_addr: &std::net::SocketAddr,
|
||||
link: Link,
|
||||
) {
|
||||
let id = addr.ip();
|
||||
let ip = addr.ip();
|
||||
|
||||
debug!("Connected to {} ({:?})", id, link);
|
||||
debug!("Connected to {} ({:?})", ip, link);
|
||||
|
||||
// For outbound connections, we are the first to say "Hello".
|
||||
// For inbound connections, we wait for the remote to say "Hello" first.
|
||||
|
|
@ -514,11 +526,12 @@ where
|
|||
if link.is_outbound() {
|
||||
let git = self.config.git_url.clone();
|
||||
|
||||
if let Some(peer) = self.peers.get_mut(&id) {
|
||||
if let Some(peer) = self.peers.get_mut(&ip) {
|
||||
self.context.write_all(
|
||||
peer.addr,
|
||||
[
|
||||
Message::hello(
|
||||
self.context.id(),
|
||||
self.context.timestamp(),
|
||||
self.context.config.listen.clone(),
|
||||
git,
|
||||
|
|
@ -531,7 +544,7 @@ where
|
|||
}
|
||||
} else {
|
||||
self.peers.insert(
|
||||
id,
|
||||
ip,
|
||||
Peer::new(
|
||||
addr,
|
||||
Link::Inbound,
|
||||
|
|
@ -547,11 +560,11 @@ where
|
|||
reason: nakamoto::DisconnectReason<Self::DisconnectReason>,
|
||||
) {
|
||||
let since = self.local_time();
|
||||
let id = addr.ip();
|
||||
let ip = addr.ip();
|
||||
|
||||
debug!("Disconnected from {} ({})", id, reason);
|
||||
debug!("Disconnected from {} ({})", ip, reason);
|
||||
|
||||
if let Some(peer) = self.peers.get_mut(&id) {
|
||||
if let Some(peer) = self.peers.get_mut(&ip) {
|
||||
peer.state = PeerState::Disconnected { since };
|
||||
|
||||
// Attempt to re-connect to persistent peers.
|
||||
|
|
@ -566,7 +579,7 @@ where
|
|||
}
|
||||
// TODO: Eventually we want a delay before attempting a reconnection,
|
||||
// with exponential back-off.
|
||||
debug!("Reconnecting to {} (attempts={})...", id, peer.attempts);
|
||||
debug!("Reconnecting to {} (attempts={})...", ip, peer.attempts);
|
||||
|
||||
// TODO: Try to reconnect only if the peer was attempted. A disconnect without
|
||||
// even a successful attempt means that we're unlikely to be able to reconnect.
|
||||
|
|
@ -614,7 +627,7 @@ where
|
|||
Ok(Some(msg)) => {
|
||||
let peers = negotiated
|
||||
.iter()
|
||||
.filter(|(id, _)| *id != peer.id())
|
||||
.filter(|(ip, _)| *ip != peer.ip())
|
||||
.map(|(_, addr)| *addr)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
|
|
@ -629,15 +642,15 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<S, T> Deref for Protocol<S, T> {
|
||||
type Target = Context<S, T>;
|
||||
impl<S, T, G> Deref for Protocol<S, T, G> {
|
||||
type Target = Context<S, T, G>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.context
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, T> DerefMut for Protocol<S, T> {
|
||||
impl<S, T, G> DerefMut for Protocol<S, T, G> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.context
|
||||
}
|
||||
|
|
@ -673,7 +686,7 @@ impl fmt::Display for DisconnectReason {
|
|||
}
|
||||
}
|
||||
|
||||
impl<S, T> Iterator for Protocol<S, T> {
|
||||
impl<S, T, G> Iterator for Protocol<S, T, G> {
|
||||
type Item = Io<(), DisconnectReason>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
|
|
@ -687,14 +700,16 @@ pub struct Lookup {
|
|||
/// Whether the project was found locally or not.
|
||||
pub local: Option<Remotes<Unverified>>,
|
||||
/// A list of remote peers on which the project is known to exist.
|
||||
pub remote: Vec<PeerId>,
|
||||
pub remote: Vec<NodeId>,
|
||||
}
|
||||
|
||||
/// Global protocol state used across peers.
|
||||
#[derive(Debug)]
|
||||
pub struct Context<S, T> {
|
||||
pub struct Context<S, T, G> {
|
||||
/// Protocol configuration.
|
||||
config: Config,
|
||||
/// Our cryptographic signer and key.
|
||||
signer: G,
|
||||
/// Tracks the location of projects.
|
||||
routing: Routing,
|
||||
/// Outgoing I/O queue.
|
||||
|
|
@ -709,19 +724,22 @@ pub struct Context<S, T> {
|
|||
rng: Rng,
|
||||
}
|
||||
|
||||
impl<S, T> Context<S, T>
|
||||
impl<S, T, G> Context<S, T, G>
|
||||
where
|
||||
T: storage::ReadStorage + storage::WriteStorage,
|
||||
G: crypto::Signer,
|
||||
{
|
||||
pub(crate) fn new(
|
||||
config: Config,
|
||||
clock: RefClock,
|
||||
storage: T,
|
||||
addrmgr: AddressManager<S>,
|
||||
signer: G,
|
||||
rng: Rng,
|
||||
) -> Self {
|
||||
Self {
|
||||
config,
|
||||
signer,
|
||||
clock,
|
||||
routing: HashMap::with_hasher(rng.clone().into()),
|
||||
io: VecDeque::new(),
|
||||
|
|
@ -731,8 +749,12 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn id(&self) -> NodeId {
|
||||
*self.signer.public_key()
|
||||
}
|
||||
|
||||
/// Process a peer inventory announcement by updating our routing table.
|
||||
fn process_inventory(&mut self, inventory: &Inventory, from: PeerId, remote: &Url) {
|
||||
fn process_inventory(&mut self, inventory: &Inventory, from: NodeId, remote: &Url) {
|
||||
for proj_id in inventory {
|
||||
let inventory = self
|
||||
.routing
|
||||
|
|
@ -760,7 +782,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<S, T> Context<S, T> {
|
||||
impl<S, T, G> Context<S, T, G> {
|
||||
/// Get current local timestamp.
|
||||
pub(crate) fn timestamp(&self) -> Timestamp {
|
||||
self.clock.local_time().as_secs()
|
||||
|
|
@ -800,13 +822,24 @@ impl<S, T> Context<S, T> {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Peers(AddressBook<PeerId, Peer>);
|
||||
/// Holds currently (or recently) connected peers.
|
||||
pub struct Peers(AddressBook<IpAddr, Peer>);
|
||||
|
||||
impl Peers {
|
||||
pub fn new(rng: Rng) -> Self {
|
||||
Self(AddressBook::new(rng))
|
||||
}
|
||||
|
||||
pub fn by_id(&self, id: &NodeId) -> Option<&Peer> {
|
||||
self.0.values().find(|p| {
|
||||
if let PeerState::Negotiated { id: _id, .. } = &p.state {
|
||||
_id == id
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Iterator over fully negotiated peers.
|
||||
pub fn negotiated(&self) -> impl Iterator<Item = (&IpAddr, &Peer)> + Clone {
|
||||
self.0.iter().filter(move |(_, p)| p.is_negotiated())
|
||||
|
|
@ -814,7 +847,7 @@ impl Peers {
|
|||
}
|
||||
|
||||
impl Deref for Peers {
|
||||
type Target = AddressBook<PeerId, Peer>;
|
||||
type Target = AddressBook<IpAddr, Peer>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
|
|
@ -828,6 +861,7 @@ impl DerefMut for Peers {
|
|||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
enum PeerState {
|
||||
/// Initial peer state. For outgoing peers this
|
||||
/// means we've attempted a connection. For incoming
|
||||
|
|
@ -837,6 +871,8 @@ enum PeerState {
|
|||
Initial,
|
||||
/// State after successful handshake.
|
||||
Negotiated {
|
||||
/// The peer's unique identifier.
|
||||
id: NodeId,
|
||||
since: LocalTime,
|
||||
/// Addresses this peer is reachable on.
|
||||
addrs: Vec<Address>,
|
||||
|
|
@ -892,7 +928,7 @@ impl Peer {
|
|||
}
|
||||
}
|
||||
|
||||
fn id(&self) -> PeerId {
|
||||
fn ip(&self) -> IpAddr {
|
||||
self.addr.ip()
|
||||
}
|
||||
|
||||
|
|
@ -900,23 +936,25 @@ impl Peer {
|
|||
matches!(self.state, PeerState::Negotiated { .. })
|
||||
}
|
||||
|
||||
fn received<S, T>(
|
||||
fn received<S, T, G>(
|
||||
&mut self,
|
||||
envelope: Envelope,
|
||||
ctx: &mut Context<S, T>,
|
||||
ctx: &mut Context<S, T, G>,
|
||||
) -> Result<Option<Message>, PeerError>
|
||||
where
|
||||
T: storage::ReadStorage + storage::WriteStorage,
|
||||
G: crypto::Signer,
|
||||
{
|
||||
if envelope.magic != NETWORK_MAGIC {
|
||||
return Err(PeerError::WrongMagic(envelope.magic));
|
||||
}
|
||||
debug!("Received {:?} from {}", &envelope.msg, self.id());
|
||||
debug!("Received {:?} from {}", &envelope.msg, self.ip());
|
||||
|
||||
match (&self.state, envelope.msg) {
|
||||
(
|
||||
PeerState::Initial,
|
||||
Message::Hello {
|
||||
id,
|
||||
timestamp,
|
||||
version,
|
||||
addrs,
|
||||
|
|
@ -938,7 +976,7 @@ impl Peer {
|
|||
ctx.write_all(
|
||||
self.addr,
|
||||
[
|
||||
Message::hello(now, ctx.config.listen.clone(), git),
|
||||
Message::hello(ctx.id(), now, ctx.config.listen.clone(), git),
|
||||
Message::get_inventory([]),
|
||||
],
|
||||
);
|
||||
|
|
@ -947,6 +985,7 @@ impl Peer {
|
|||
// set after the first message is received only. Setting it here would
|
||||
// mean that messages received right after the handshake could be ignored.
|
||||
self.state = PeerState::Negotiated {
|
||||
id,
|
||||
since: ctx.clock.local_time(),
|
||||
addrs,
|
||||
git,
|
||||
|
|
@ -955,7 +994,7 @@ impl Peer {
|
|||
(PeerState::Initial, _) => {
|
||||
debug!(
|
||||
"Disconnecting peer {} for sending us a message before handshake",
|
||||
self.id()
|
||||
self.ip()
|
||||
);
|
||||
return Err(PeerError::Misbehavior);
|
||||
}
|
||||
|
|
@ -965,7 +1004,7 @@ impl Peer {
|
|||
ctx.write(self.addr, inventory);
|
||||
}
|
||||
(
|
||||
PeerState::Negotiated { git, .. },
|
||||
PeerState::Negotiated { id, git, .. },
|
||||
Message::Inventory {
|
||||
timestamp,
|
||||
inv,
|
||||
|
|
@ -986,13 +1025,13 @@ impl Peer {
|
|||
} else {
|
||||
return Ok(None);
|
||||
}
|
||||
ctx.process_inventory(&inv, origin.unwrap_or_else(|| self.id()), git);
|
||||
ctx.process_inventory(&inv, origin.unwrap_or(*id), git);
|
||||
|
||||
if ctx.config.relay {
|
||||
return Ok(Some(Message::Inventory {
|
||||
timestamp,
|
||||
inv,
|
||||
origin: origin.or_else(|| Some(self.id())),
|
||||
origin: origin.or(Some(*id)),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
@ -1007,12 +1046,12 @@ impl Peer {
|
|||
(PeerState::Negotiated { .. }, Message::Hello { .. }) => {
|
||||
debug!(
|
||||
"Disconnecting peer {} for sending us a redundant handshake message",
|
||||
self.id()
|
||||
self.ip()
|
||||
);
|
||||
return Err(PeerError::Misbehavior);
|
||||
}
|
||||
(PeerState::Disconnected { .. }, msg) => {
|
||||
debug!("Ignoring {:?} from disconnected peer {}", msg, self.id());
|
||||
debug!("Ignoring {:?} from disconnected peer {}", msg, self.ip());
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ impl ReadRepository for Repository {
|
|||
let name = r.name().ok_or(Error::InvalidRef)?;
|
||||
let (id, refname) = git::parse_ref::<UserId>(name)?;
|
||||
let entry = remotes
|
||||
.entry(id.clone())
|
||||
.entry(id)
|
||||
.or_insert_with(|| Remote::new(id, HashMap::default()));
|
||||
let oid = r.target().ok_or(Error::InvalidRef)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
pub(crate) mod arbitrary;
|
||||
pub(crate) mod assert;
|
||||
pub(crate) mod crypto;
|
||||
pub(crate) mod fixtures;
|
||||
pub(crate) mod logger;
|
||||
pub(crate) mod peer;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
use crate::crypto::{PublicKey, SecretKey, Signer};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MockSigner {
|
||||
key: PublicKey,
|
||||
}
|
||||
|
||||
impl MockSigner {
|
||||
pub fn new(rng: &mut fastrand::Rng) -> Self {
|
||||
let mut bytes: [u8; 32] = [0; 32];
|
||||
|
||||
for byte in &mut bytes {
|
||||
*byte = rng.u8(..);
|
||||
}
|
||||
let sk = SecretKey::from(bytes);
|
||||
|
||||
Self {
|
||||
key: sk.verification_key().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MockSigner {
|
||||
fn default() -> Self {
|
||||
let bytes: [u8; 32] = [0; 32];
|
||||
let sk = SecretKey::from(bytes);
|
||||
|
||||
Self {
|
||||
key: sk.verification_key().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Signer for MockSigner {
|
||||
fn public_key(&self) -> &PublicKey {
|
||||
&self.key
|
||||
}
|
||||
}
|
||||
|
|
@ -12,10 +12,11 @@ use crate::collections::HashMap;
|
|||
use crate::decoder::Decoder;
|
||||
use crate::protocol::*;
|
||||
use crate::storage::{ReadStorage, WriteStorage};
|
||||
use crate::test::crypto::MockSigner;
|
||||
use crate::*;
|
||||
|
||||
/// Protocol instantiation used for testing.
|
||||
pub type Protocol<S> = crate::protocol::Protocol<HashMap<net::IpAddr, KnownAddress>, S>;
|
||||
pub type Protocol<S> = crate::protocol::Protocol<HashMap<net::IpAddr, KnownAddress>, S, MockSigner>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Peer<S> {
|
||||
|
|
@ -80,7 +81,7 @@ where
|
|||
ip: impl Into<net::IpAddr>,
|
||||
addrs: Vec<(net::SocketAddr, Source)>,
|
||||
storage: S,
|
||||
rng: fastrand::Rng,
|
||||
mut rng: fastrand::Rng,
|
||||
) -> Self {
|
||||
let addrs = addrs
|
||||
.into_iter()
|
||||
|
|
@ -88,7 +89,8 @@ where
|
|||
.collect();
|
||||
let local_time = LocalTime::now();
|
||||
let clock = RefClock::from(local_time);
|
||||
let protocol = Protocol::new(config, clock, storage, addrs, rng.clone());
|
||||
let signer = MockSigner::new(&mut rng);
|
||||
let protocol = Protocol::new(config, clock, storage, addrs, signer, rng.clone());
|
||||
let ip = ip.into();
|
||||
let local_addr = net::SocketAddr::new(ip, rng.u16(..));
|
||||
|
||||
|
|
@ -120,6 +122,10 @@ where
|
|||
self.config().git_url.clone()
|
||||
}
|
||||
|
||||
pub fn id(&self) -> NodeId {
|
||||
self.protocol.id()
|
||||
}
|
||||
|
||||
pub fn receive(&mut self, peer: &net::SocketAddr, msg: Message) {
|
||||
let bytes = serde_json::to_vec(&Envelope {
|
||||
magic: NETWORK_MAGIC,
|
||||
|
|
@ -130,23 +136,25 @@ where
|
|||
self.protocol.received_bytes(peer, &bytes);
|
||||
}
|
||||
|
||||
pub fn connect_from(&mut self, remote: &net::SocketAddr) {
|
||||
pub fn connect_from(&mut self, peer: &Self) {
|
||||
let remote = simulator::Peer::<Protocol<S>>::addr(peer);
|
||||
let local = net::SocketAddr::new(self.ip, self.rng.u16(..));
|
||||
let git = format!("file://{}.git", remote.ip());
|
||||
let git = Url::from_bytes(git.as_bytes()).unwrap();
|
||||
|
||||
self.initialize();
|
||||
self.protocol.connected(*remote, &local, Link::Inbound);
|
||||
self.protocol.connected(remote, &local, Link::Inbound);
|
||||
self.receive(
|
||||
remote,
|
||||
&remote,
|
||||
Message::hello(
|
||||
peer.id(),
|
||||
self.local_time().as_secs(),
|
||||
vec![Address::Tcp { addr: *remote }],
|
||||
vec![Address::Tcp { addr: remote }],
|
||||
git,
|
||||
),
|
||||
);
|
||||
|
||||
let mut msgs = self.messages(remote);
|
||||
let mut msgs = self.messages(&remote);
|
||||
msgs.find(|m| matches!(m, Message::Hello { .. }))
|
||||
.expect("`hello` is sent");
|
||||
msgs.find(|m| matches!(m, Message::GetInventory { .. }))
|
||||
|
|
@ -171,6 +179,7 @@ where
|
|||
self.receive(
|
||||
&remote,
|
||||
Message::hello(
|
||||
peer.id(),
|
||||
self.local_time().as_secs(),
|
||||
peer.config().listen.clone(),
|
||||
git,
|
||||
|
|
|
|||
|
|
@ -51,8 +51,8 @@ fn test_inbound_connection() {
|
|||
let bob = Peer::new("bob", [9, 9, 9, 9], MockStorage::empty());
|
||||
let eve = Peer::new("eve", [7, 7, 7, 7], MockStorage::empty());
|
||||
|
||||
alice.connect_from(&bob.addr());
|
||||
alice.connect_from(&eve.addr());
|
||||
alice.connect_from(&bob);
|
||||
alice.connect_from(&eve);
|
||||
|
||||
let peers = alice
|
||||
.protocol
|
||||
|
|
@ -108,7 +108,12 @@ fn test_handshake_invalid_timestamp() {
|
|||
alice.connected(bob.addr(), &local, Link::Inbound);
|
||||
alice.receive(
|
||||
&bob.addr(),
|
||||
Message::hello(alice.timestamp() - time_delta, vec![], bob.git_url()),
|
||||
Message::hello(
|
||||
bob.id(),
|
||||
alice.timestamp() - time_delta,
|
||||
vec![],
|
||||
bob.git_url(),
|
||||
),
|
||||
);
|
||||
assert_matches!(alice.outbox().next(), Some(Io::Disconnect(addr, _)) if addr == bob.addr());
|
||||
}
|
||||
|
|
@ -144,7 +149,7 @@ fn test_inventory_sync() {
|
|||
|
||||
for proj in &projs {
|
||||
let providers = alice.routing().get(proj).unwrap();
|
||||
assert!(providers.contains(&bob.ip));
|
||||
assert!(providers.contains(&bob.id()));
|
||||
}
|
||||
|
||||
let a = alice
|
||||
|
|
@ -192,7 +197,7 @@ fn test_inventory_relay() {
|
|||
|
||||
// Inventory from Bob relayed to Eve.
|
||||
alice.connect_to(&bob);
|
||||
alice.connect_from(&eve.addr());
|
||||
alice.connect_from(&eve);
|
||||
alice.receive(
|
||||
&bob.addr(),
|
||||
Message::Inventory {
|
||||
|
|
@ -204,7 +209,7 @@ fn test_inventory_relay() {
|
|||
assert_matches!(
|
||||
alice.messages(&eve.addr()).next(),
|
||||
Some(Message::Inventory { timestamp, origin, .. })
|
||||
if origin == Some(bob.ip) && timestamp == now
|
||||
if origin == Some(bob.id()) && timestamp == now
|
||||
);
|
||||
assert_matches!(
|
||||
alice.messages(&bob.addr()).next(),
|
||||
|
|
@ -237,7 +242,7 @@ fn test_inventory_relay() {
|
|||
assert_matches!(
|
||||
alice.messages(&eve.addr()).next(),
|
||||
Some(Message::Inventory { timestamp, origin, .. })
|
||||
if origin == Some(bob.ip) && timestamp == now + 1,
|
||||
if origin == Some(bob.id()) && timestamp == now + 1,
|
||||
"Sending a new inventory does trigger the relay"
|
||||
);
|
||||
|
||||
|
|
@ -253,7 +258,7 @@ fn test_inventory_relay() {
|
|||
assert_matches!(
|
||||
alice.messages(&bob.addr()).next(),
|
||||
Some(Message::Inventory { timestamp, origin, .. })
|
||||
if origin == Some(eve.ip) && timestamp == now
|
||||
if origin == Some(eve.id()) && timestamp == now
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -334,9 +339,9 @@ fn prop_inventory_exchange_dense() {
|
|||
let mut routing = Routing::with_hasher(rng.clone().into());
|
||||
|
||||
for (inv, peer) in &[
|
||||
(alice_inv.inventory, alice.addr().ip()),
|
||||
(bob_inv.inventory, bob.addr().ip()),
|
||||
(eve_inv.inventory, eve.addr().ip()),
|
||||
(alice_inv.inventory, alice.id()),
|
||||
(bob_inv.inventory, bob.id()),
|
||||
(eve_inv.inventory, eve.id()),
|
||||
] {
|
||||
for (proj, _) in inv {
|
||||
routing
|
||||
|
|
@ -352,7 +357,7 @@ fn prop_inventory_exchange_dense() {
|
|||
eve.command(Command::Connect(alice.addr()));
|
||||
eve.command(Command::Connect(bob.addr()));
|
||||
|
||||
let mut peers: HashMap<_, _> = [(alice.ip, alice), (bob.ip, bob), (eve.ip, eve)]
|
||||
let mut peers: HashMap<_, _> = [(alice.id(), alice), (bob.id(), bob), (eve.id(), eve)]
|
||||
.into_iter()
|
||||
.collect();
|
||||
let mut simulator = Simulation::new(LocalTime::now(), rng, simulator::Options::default())
|
||||
|
|
|
|||
Loading…
Reference in New Issue