node: Send all remotes in inventory

Signed-off-by: Alexis Sellier <self@cloudhead.io>
This commit is contained in:
Alexis Sellier 2022-08-22 17:02:16 +02:00
parent 789c50ee35
commit 15eed71588
No known key found for this signature in database
7 changed files with 143 additions and 88 deletions

View File

@ -65,6 +65,7 @@ impl fmt::Display for Did {
}
#[derive(Serialize, Deserialize, Eq, Debug, Clone)]
#[serde(transparent)]
pub struct UserId(pub VerificationKey);
impl std::hash::Hash for UserId {
@ -73,6 +74,12 @@ impl std::hash::Hash for UserId {
}
}
impl fmt::Display for UserId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.encode())
}
}
impl PartialEq for UserId {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0

View File

@ -17,7 +17,7 @@ use crate::collections::{HashMap, HashSet};
use crate::decoder::Decoder;
use crate::identity::{ProjId, UserId};
use crate::storage;
use crate::storage::{Inventory, ReadStorage, Refs, WriteStorage};
use crate::storage::{Inventory, ReadStorage, Remotes, Unverified, WriteStorage};
/// Network peer identifier.
pub type PeerId = IpAddr;
@ -586,7 +586,7 @@ impl<S, T> Iterator for Protocol<S, T> {
#[derive(Debug)]
pub struct Lookup {
/// Whether the project was found locally or not.
pub local: Option<Refs>,
pub local: Option<Remotes<Unverified>>,
/// A list of remote peers on which the project is known to exist.
pub remote: Vec<PeerId>,
}

View File

@ -20,7 +20,7 @@ pub static RAD_ID_GLOB: Lazy<refspec::PatternString> =
pub static IDENTITY_PATH: Lazy<&Path> = Lazy::new(|| Path::new(".rad/identity.toml"));
pub type BranchName = String;
pub type Inventory = Vec<(ProjId, Refs)>;
pub type Inventory = Vec<(ProjId, HashMap<String, Remote<Unverified>>)>;
/// Storage error.
#[derive(Error, Debug)]
@ -39,29 +39,39 @@ pub enum Error {
InvalidHead,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
pub struct Refs {
heads: HashMap<BranchName, Oid>,
}
impl From<HashMap<BranchName, Oid>> for Refs {
fn from(heads: HashMap<BranchName, Oid>) -> Self {
Self { heads }
}
}
pub type Refs = HashMap<BranchName, Oid>;
pub type RemoteId = UserId;
pub type RefName = String;
/// Verified (used as type witness).
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Verified;
/// Unverified (used as type witness).
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Unverified;
/// Project remotes. Tracks the git state of a project.
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct Remotes<V>(HashMap<RemoteId, Remote<V>>);
impl Remotes<Unverified> {
pub fn new(remotes: HashMap<RemoteId, Remote<Unverified>>) -> Self {
Self(remotes)
}
}
#[allow(clippy::from_over_into)]
impl Into<HashMap<String, Remote<Unverified>>> for Remotes<Unverified> {
fn into(self) -> HashMap<String, Remote<Unverified>> {
let mut remotes = HashMap::with_hasher(fastrand::Rng::new().into());
for (k, v) in self.0 {
remotes.insert(k.to_string(), v);
}
remotes
}
}
/// A project remote.
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
pub struct Remote<V> {
@ -70,11 +80,22 @@ pub struct Remote<V> {
/// Whether this remote is of a project delegate.
delegate: bool,
/// Whether the remote is verified or not, ie. whether its signed refs were checked.
#[serde(skip)]
verified: PhantomData<V>,
}
impl Remote<Unverified> {
pub fn new(refs: HashMap<RefName, Oid>) -> Self {
Self {
refs,
delegate: false,
verified: PhantomData,
}
}
}
pub trait ReadStorage {
fn get(&self, proj: &ProjId) -> Result<Option<Refs>, Error>;
fn get(&self, proj: &ProjId) -> Result<Option<Remotes<Unverified>>, Error>;
fn inventory(&self) -> Result<Inventory, Error>;
}
@ -92,7 +113,7 @@ where
self.deref().inventory()
}
fn get(&self, proj: &ProjId) -> Result<Option<Refs>, Error> {
fn get(&self, proj: &ProjId) -> Result<Option<Remotes<Unverified>>, Error> {
self.deref().get(proj)
}
}
@ -118,23 +139,22 @@ impl fmt::Debug for Storage {
}
impl ReadStorage for Storage {
fn get(&self, _id: &ProjId) -> Result<Option<Refs>, Error> {
fn get(&self, _id: &ProjId) -> Result<Option<Remotes<Unverified>>, Error> {
todo!()
}
fn inventory(&self) -> Result<Inventory, Error> {
let glob: String = RAD_ID_GLOB.clone().into();
let refs = self.backend.references_glob(glob.as_str())?;
let mut projs = Vec::new();
for r in refs {
let r = r?;
let name = r.name().ok_or(Error::InvalidRef)?;
let id = ProjId::from_ref(name)?;
let _id = ProjId::from_ref(name)?;
projs.push((id, Refs::default()));
todo!();
}
Ok(projs)
Ok(vec![])
}
}

View File

@ -1,6 +1,6 @@
mod arbitrary;
mod assert;
mod logger;
mod peer;
mod storage;
mod tests;
pub(crate) mod arbitrary;
pub(crate) mod assert;
pub(crate) mod logger;
pub(crate) mod peer;
pub(crate) mod storage;
pub(crate) mod tests;

View File

@ -2,8 +2,25 @@ use crate::collections::HashMap;
use crate::hash;
use crate::identity::{ProjId, UserId};
use crate::storage;
use crate::test::storage::MockStorage;
impl quickcheck::Arbitrary for storage::Refs {
impl quickcheck::Arbitrary for storage::Remotes<storage::Unverified> {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
let remotes: HashMap<storage::RemoteId, storage::Remote<storage::Unverified>> =
quickcheck::Arbitrary::arbitrary(g);
storage::Remotes::new(remotes)
}
}
impl quickcheck::Arbitrary for MockStorage {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
let inventory = quickcheck::Arbitrary::arbitrary(g);
MockStorage::new(inventory)
}
}
impl quickcheck::Arbitrary for storage::Remote<storage::Unverified> {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
let rng = fastrand::Rng::with_seed(u64::arbitrary(g));
let mut refs: HashMap<storage::BranchName, storage::Oid> = HashMap::with_hasher(rng.into());
@ -19,7 +36,7 @@ impl quickcheck::Arbitrary for storage::Refs {
refs.insert(name.to_string(), oid);
}
}
storage::Refs::from(refs)
storage::Remote::new(refs)
}
}

View File

@ -1,14 +1,15 @@
use std::net;
use crate::identity::ProjId;
use crate::storage::{Error, Inventory, ReadStorage, Refs, WriteStorage};
use crate::storage::{Error, Inventory, ReadStorage, Remotes, Unverified, WriteStorage};
#[derive(Clone, Debug)]
pub struct MockStorage {
pub inventory: Inventory,
pub inventory: Vec<(ProjId, Remotes<Unverified>)>,
}
impl MockStorage {
pub fn new(inventory: Inventory) -> Self {
pub fn new(inventory: Vec<(ProjId, Remotes<Unverified>)>) -> Self {
Self { inventory }
}
@ -20,7 +21,7 @@ impl MockStorage {
}
impl ReadStorage for MockStorage {
fn get(&self, proj: &ProjId) -> Result<Option<Refs>, Error> {
fn get(&self, proj: &ProjId) -> Result<Option<Remotes<Unverified>>, Error> {
if let Some((_, refs)) = self.inventory.iter().find(|(id, _)| id == proj) {
return Ok(Some(refs.clone()));
}
@ -28,7 +29,13 @@ impl ReadStorage for MockStorage {
}
fn inventory(&self) -> Result<Inventory, Error> {
Ok(self.inventory.clone())
let inventory = self
.inventory
.iter()
.map(|(id, remotes)| (id.clone(), remotes.clone().into()))
.collect::<Vec<_>>();
Ok(inventory)
}
}

View File

@ -5,11 +5,10 @@ use nakamoto_net as nakamoto;
use nakamoto_net::simulator;
use nakamoto_net::simulator::{Peer as _, Simulation};
use nakamoto_net::Protocol as _;
use quickcheck_macros::quickcheck;
use crate::collections::{HashMap, HashSet};
use crate::protocol::*;
use crate::storage::{Inventory, ReadStorage};
use crate::storage::ReadStorage;
#[allow(unused)]
use crate::test::logger;
use crate::test::peer::Peer;
@ -157,18 +156,19 @@ fn test_persistent_peer_reconnect() {
assert_matches!(alice.outbox().next(), None);
}
#[quickcheck]
fn prop_inventory_exchange_dense(alice_inv: Inventory, bob_inv: Inventory, eve_inv: Inventory) {
#[test]
fn prop_inventory_exchange_dense() {
fn property(alice_inv: MockStorage, bob_inv: MockStorage, eve_inv: MockStorage) {
let rng = fastrand::Rng::new();
let alice = Peer::new("alice", [7, 7, 7, 7], MockStorage::new(alice_inv.clone()));
let mut bob = Peer::new("bob", [8, 8, 8, 8], MockStorage::new(bob_inv.clone()));
let mut eve = Peer::new("eve", [9, 9, 9, 9], MockStorage::new(eve_inv.clone()));
let alice = Peer::new("alice", [7, 7, 7, 7], alice_inv.clone());
let mut bob = Peer::new("bob", [8, 8, 8, 8], bob_inv.clone());
let mut eve = Peer::new("eve", [9, 9, 9, 9], eve_inv.clone());
let mut routing = Routing::with_hasher(rng.clone().into());
for (inv, peer) in &[
(alice_inv, alice.addr().ip()),
(bob_inv, bob.addr().ip()),
(eve_inv, eve.addr().ip()),
(alice_inv.inventory, alice.addr().ip()),
(bob_inv.inventory, bob.addr().ip()),
(eve_inv.inventory, eve.addr().ip()),
] {
for (proj, _) in inv {
routing
@ -222,3 +222,7 @@ fn prop_inventory_exchange_dense(alice_inv: Inventory, bob_inv: Inventory, eve_i
}
}
}
quickcheck::QuickCheck::new()
.gen(quickcheck::Gen::new(8))
.quickcheck(property as fn(MockStorage, MockStorage, MockStorage));
}