diff --git a/ng-app/src/lib/Login.svelte b/ng-app/src/lib/Login.svelte
index 5f61864..74cdd7b 100644
--- a/ng-app/src/lib/Login.svelte
+++ b/ng-app/src/lib/Login.svelte
@@ -221,7 +221,7 @@
For each category of images, you will be presented with the 15 possible
- choices. The categories are shuffled at every login. They will not
+ image choices. The categories are shuffled at every login. They will not
always appear in the same order.
@@ -229,7 +229,8 @@
image that belongs to your pazzle. Find it and tap or click on that one.
The 15 images are shuffled too, they will not appear at the same
position at each login. On a computer, you can also use the tab key on
- your keyboard to move to the desired item on the screen.
+ your keyboard to move to the desired item on the screen, then press the
+ space bar.
Once you completed the last category, you will be presented with all the
diff --git a/ng-sdk-js/src/lib.rs b/ng-sdk-js/src/lib.rs
index 5299cec..6c956a6 100644
--- a/ng-sdk-js/src/lib.rs
+++ b/ng-sdk-js/src/lib.rs
@@ -23,7 +23,7 @@ use p2p_net::broker::*;
use p2p_net::connection::{ClientConfig, StartConfig};
use p2p_net::types::{
BootstrapContent, BootstrapContentV0, ClientId, ClientInfo, ClientInfoV0, ClientType,
- CreateAccountBSP, DirectPeerId, Identity, UserId, IP,
+ CreateAccountBSP, DirectPeerId, IP,
};
use p2p_net::utils::{decode_invitation_string, spawn_and_log_error, Receiver, ResultSend, Sender};
#[cfg(target_arch = "wasm32")]
diff --git a/ng-wallet/src/lib.rs b/ng-wallet/src/lib.rs
index fb702dd..af7d5bc 100644
--- a/ng-wallet/src/lib.rs
+++ b/ng-wallet/src/lib.rs
@@ -38,11 +38,8 @@ use p2p_net::{
broker::BROKER,
connection::{ClientConfig, StartConfig},
};
-use p2p_net::{
- connection::IConnect,
- types::{ClientInfo, Identity, SiteType, SiteV0},
-};
-use p2p_repo::types::{PubKey, Sig, Timestamp};
+use p2p_net::{connection::IConnect, types::ClientInfo};
+use p2p_repo::types::{Identity, PubKey, Sig, SiteType, SiteV0, Timestamp};
use p2p_repo::utils::{generate_keypair, now_timestamp, sign, verify};
use p2p_repo::{log::*, types::PrivKey};
use rand::prelude::*;
diff --git a/ngcli/src/main.rs b/ngcli/src/main.rs
index c4b9b43..77d230b 100644
--- a/ngcli/src/main.rs
+++ b/ngcli/src/main.rs
@@ -12,7 +12,6 @@ use ed25519_dalek::*;
use duration_str::parse;
use futures::{future, pin_mut, stream, SinkExt, StreamExt};
use p2p_net::actors::*;
-use p2p_repo::object::Object;
use p2p_repo::store::{store_max_value_size, store_valid_value_size, HashMapRepoStore, RepoStore};
use rand::rngs::OsRng;
use serde::{Deserialize, Serialize};
diff --git a/p2p-broker/src/broker_store/account.rs b/p2p-broker/src/broker_store/account.rs
index c668433..4b49f1a 100644
--- a/p2p-broker/src/broker_store/account.rs
+++ b/p2p-broker/src/broker_store/account.rs
@@ -18,7 +18,7 @@ use p2p_net::types::*;
use p2p_repo::kcv_store::KCVStore;
use p2p_repo::log::*;
use p2p_repo::store::*;
-use p2p_repo::types::Timestamp;
+use p2p_repo::types::UserId;
use serde_bare::{from_slice, to_vec};
pub struct Account<'a> {
diff --git a/p2p-net/src/actors/start.rs b/p2p-net/src/actors/start.rs
index b61e537..f39835b 100644
--- a/p2p-net/src/actors/start.rs
+++ b/p2p-net/src/actors/start.rs
@@ -11,9 +11,13 @@
use crate::actors::noise::Noise;
use crate::connection::NoiseFSM;
-use crate::types::{AdminRequest, ExtResponse};
+use crate::types::{
+ AdminRequest, CoreBrokerConnect, CoreBrokerConnectResponse, CoreBrokerConnectResponseV0,
+ CoreMessage, CoreMessageV0, CoreResponseContentV0, CoreResponseV0, ExtResponse,
+};
use crate::{actor::*, errors::ProtocolError, types::ProtocolMessage};
use async_std::sync::Mutex;
+use p2p_repo::log::*;
use serde::{Deserialize, Serialize};
use std::any::{Any, TypeId};
use std::sync::Arc;
@@ -21,12 +25,12 @@ use std::sync::Arc;
// pub struct Noise3(Noise);
/// Start chosen protocol
-/// First message sent by the client
+/// First message sent by the connecting peer
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum StartProtocol {
Client(ClientHello),
Ext(ExtHello),
- //Core(CoreHello),
+ Core(CoreHello),
Admin(AdminRequest),
}
@@ -34,6 +38,7 @@ impl StartProtocol {
pub fn type_id(&self) -> TypeId {
match self {
StartProtocol::Client(a) => a.type_id(),
+ StartProtocol::Core(a) => a.type_id(),
StartProtocol::Ext(a) => a.type_id(),
StartProtocol::Admin(a) => a.type_id(),
}
@@ -41,6 +46,7 @@ impl StartProtocol {
pub fn get_actor(&self) -> Box {
match self {
StartProtocol::Client(a) => a.get_actor(),
+ StartProtocol::Core(a) => a.get_actor(),
StartProtocol::Ext(a) => a.get_actor(),
StartProtocol::Admin(a) => a.get_actor(),
}
@@ -53,7 +59,72 @@ impl From for ProtocolMessage {
}
}
-/// External Hello (finalizes the Noise handshake and send first ExtRequest)
+/// Core Hello (finalizes the Noise handshake and sends CoreConnect)
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct CoreHello {
+ // contains the 3rd Noise handshake message "s,se"
+ pub noise: Noise,
+
+ /// Noise encrypted payload (a CoreMessage::CoreRequest::BrokerConnect)
+ pub payload: Vec,
+}
+
+impl CoreHello {
+ pub fn get_actor(&self) -> Box {
+ Actor::::new_responder()
+ }
+}
+
+impl TryFrom for CoreBrokerConnectResponse {
+ type Error = ProtocolError;
+ fn try_from(msg: ProtocolMessage) -> Result {
+ if let ProtocolMessage::CoreMessage(CoreMessage::V0(CoreMessageV0::Response(
+ CoreResponseV0 {
+ content: CoreResponseContentV0::BrokerConnectResponse(a),
+ ..
+ },
+ ))) = msg
+ {
+ Ok(CoreBrokerConnectResponse::V0(a))
+ } else {
+ log_debug!("INVALID {:?}", msg);
+ Err(ProtocolError::InvalidValue)
+ }
+ }
+}
+
+impl From for ProtocolMessage {
+ fn from(msg: CoreHello) -> ProtocolMessage {
+ ProtocolMessage::Start(StartProtocol::Core(msg))
+ }
+}
+
+impl From for ProtocolMessage {
+ fn from(msg: CoreBrokerConnect) -> ProtocolMessage {
+ unimplemented!();
+ }
+}
+
+impl Actor<'_, CoreBrokerConnect, CoreBrokerConnectResponse> {}
+
+#[async_trait::async_trait]
+impl EActor for Actor<'_, CoreBrokerConnect, CoreBrokerConnectResponse> {
+ async fn respond(
+ &mut self,
+ msg: ProtocolMessage,
+ fsm: Arc>,
+ ) -> Result<(), ProtocolError> {
+ //let req = CoreBrokerConnect::try_from(msg)?;
+ // let res = CoreBrokerConnectResponse::V0(CoreBrokerConnectResponseV0 {
+ // successes: vec![],
+ // errors: vec![],
+ // });
+ // fsm.lock().await.send(res.into()).await?;
+ Ok(())
+ }
+}
+
+/// External Hello (finalizes the Noise handshake and sends first ExtRequest)
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ExtHello {
// contains the 3rd Noise handshake message "s,se"
diff --git a/p2p-net/src/broker.rs b/p2p-net/src/broker.rs
index 89b517c..727849f 100644
--- a/p2p-net/src/broker.rs
+++ b/p2p-net/src/broker.rs
@@ -320,7 +320,7 @@ impl<'a> Broker<'a> {
let blockstream = self
.get_block_from_store_with_block_id(nuri, obj_ref.id, true)
.await?;
- let store = HashMapRepoStore::from_block_stream(blockstream).await;
+ let store = Box::new(HashMapRepoStore::from_block_stream(blockstream).await);
Object::load(obj_ref.id, Some(obj_ref.key), &store)
.map_err(|e| match e {
@@ -346,7 +346,6 @@ impl<'a> Broker<'a> {
};
let refs = vec![obj_ref.clone()];
let metadata = vec![5u8; 55];
- let expiry = None;
let (member_privkey, member_pubkey) = generate_keypair();
@@ -354,13 +353,16 @@ impl<'a> Broker<'a> {
member_privkey,
member_pubkey,
1,
- obj_ref.clone(),
+ PubKey::nil(),
+ QuorumType::NoSigning,
+ vec![],
+ vec![],
vec![],
vec![],
refs,
+ vec![],
metadata,
obj_ref.clone(),
- expiry,
)
.unwrap();
async fn send(mut tx: Sender, commit: Commit) -> ResultSend<()> {
@@ -782,6 +784,7 @@ impl<'a> Broker<'a> {
}
};
}
+ //TODO, if Core, check that IP is not in self.direct_connections
}
let mut connection = cnx
@@ -844,6 +847,8 @@ impl<'a> Broker<'a> {
let mut broker = BROKER.write().await;
broker.reconnecting(remote_peer_id, config.get_user());
// TODO: deal with cycle error https://users.rust-lang.org/t/recursive-async-method-causes-cycle-error/84628/5
+ // use a channel and send the reconnect job to it.
+ // create a spawned loop to read the channel and process the reconnection requests.
// let result = broker
// .connect(cnx, ip, core, peer_pubk, peer_privk, remote_peer_id)
// .await;
diff --git a/p2p-net/src/connection.rs b/p2p-net/src/connection.rs
index 6a80316..aeedb76 100644
--- a/p2p-net/src/connection.rs
+++ b/p2p-net/src/connection.rs
@@ -163,6 +163,7 @@ pub struct ClientConfig {
pub user_priv: PrivKey,
pub client: PubKey,
pub info: ClientInfo,
+ //pub peer_advert: PeerAdvert,
pub registration: Option>,
}
@@ -172,7 +173,8 @@ pub struct ExtConfig {}
#[derive(Debug, Clone)]
pub struct CoreConfig {
pub addr: BindAddress,
- pub interface: String,
+ //pub interface: String,
+ pub overlays_config: CoreBrokerConnect,
}
#[derive(Debug, Clone)]
diff --git a/p2p-net/src/lib.rs b/p2p-net/src/lib.rs
index e9cd6dd..6c10e68 100644
--- a/p2p-net/src/lib.rs
+++ b/p2p-net/src/lib.rs
@@ -31,8 +31,6 @@ pub mod utils;
pub mod tests;
-pub mod site;
-
pub static NG_BOOTSTRAP_LOCAL_PATH: &str = "/.ng_bootstrap";
#[cfg(debug_assertions)]
diff --git a/p2p-net/src/types.rs b/p2p-net/src/types.rs
index fabea42..909bcb3 100644
--- a/p2p-net/src/types.rs
+++ b/p2p-net/src/types.rs
@@ -31,7 +31,7 @@ use std::{
use web_time::SystemTime;
//
-// Broker common types
+// Network common types
//
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
@@ -85,88 +85,6 @@ pub struct Interface {
pub ipv6: Vec,
}
-/// List of Identity types
-#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
-pub enum Identity {
- OrgSite(PubKey),
- IndividualSite(PubKey),
- OrgPublic(PubKey),
- OrgProtected(PubKey),
- OrgPrivate(PubKey),
- IndividualPublic(PubKey),
- IndividualProtected(PubKey),
- IndividualPrivate(PubKey),
- Group(RepoId),
- Dialog(RepoId),
- Document(RepoId),
- DialogOverlay(Digest),
-}
-
-/// Site type
-#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
-pub enum SiteType {
- Org,
- Individual, // formerly Personal
-}
-
-/// Site Store
-#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
-pub struct SiteStore {
- // pub identity: Identity,
- pub key: PrivKey,
- // signature with site_key
- // pub sig: Sig,
- pub root_branch_def_ref: ObjectRef,
-
- pub repo_secret: SymKey,
-}
-
-/// Site Store type
-#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
-pub enum SiteStoreType {
- Public,
- Protected,
- Private,
-}
-
-/// Site V0
-#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
-pub struct SiteV0 {
- pub site_type: SiteType,
- // Identity::OrgSite or Identity::IndividualSite
- // pub site_identity: Identity,
- pub site_key: PrivKey,
-
- // Identity::OrgPublic or Identity::IndividualPublic
- pub public: SiteStore,
-
- // Identity::OrgProtected or Identity::IndividualProtected
- pub protected: SiteStore,
-
- // Identity::OrgPrivate or Identity::IndividualPrivate
- pub private: SiteStore,
-
- pub cores: Vec<(PubKey, Option<[u8; 32]>)>,
-
- pub bootstraps: Vec,
-}
-
-/// Reduced Site (for QRcode)
-#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
-pub struct ReducedSiteV0 {
- pub site_key: PrivKey,
-
- pub private_site_key: PrivKey,
-
- pub private_site_root_branch_def_ref: ObjectRef,
-
- pub private_site_repo_secret: SymKey,
-
- pub core: PubKey,
-
- pub bootstraps: Vec,
-}
-
/// Bind address
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct BindAddress {
@@ -194,6 +112,8 @@ impl From<&SocketAddr> for BindAddress {
}
}
+/// BROKER common types
+
/// Core Broker connection details Version 0
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct BrokerCoreV0 {
@@ -1239,33 +1159,119 @@ pub enum RegistrationConfig {
pub type DirectPeerId = PubKey;
+pub type ForwardedPeerId = PubKey;
+
/// Peer ID: public key of the node, or an encrypted version of it
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
pub enum PeerId {
- DIRECT(DirectPeerId),
- FORWARDED([u8; 32]),
+ Direct(DirectPeerId),
+ Forwarded(ForwardedPeerId),
}
+pub type OuterOverlayId = Digest;
+
+pub type InnerOverlayId = Digest;
+
/// Overlay ID
///
-/// - for read overlays that need to be discovered by public key:
-/// BLAKE3 hash over the repository public key (of root doc)
-/// - for write overlays:
-/// BLAKE3 keyed hash over the repository public key
-/// - key: BLAKE3 derive_key ("NextGraph OverlayId BLAKE3 key", repo_secret, root_secret)
-pub type OverlayId = Digest;
+/// - for outer overlays that need to be discovered by public key:
+/// BLAKE3 hash over the repository public key (of root repo)
+/// - for inner overlays:
+/// BLAKE3 keyed hash over the repository public key (of root repo)
+/// - key: BLAKE3 derive_key ("NextGraph Overlay Secret BLAKE3 key", root_secret)
+#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
+pub enum OverlayId {
+ Outer(OuterOverlayId),
+ Inner(InnerOverlayId),
+ Global,
+}
-/// Overlay session ID
+impl fmt::Display for OverlayId {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let overlay_ser = serde_bare::to_vec(&self).unwrap();
+ write!(f, "{}", base64_url::encode(&overlay_ser))
+ }
+}
+
+/// Overlay Access
///
-/// Used as a component for key derivation.
-/// Each peer generates it randomly when (re)joining the overlay network.
-pub type SessionId = u64;
+/// Used by the Client when opening or pinning a repo.
+#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
+pub enum OverlayAccess {
+ /// The repo will be accessed on the Outer Overlay in Read Only mode
+ /// This can be used for Public, Protected or Group overlays
+ /// Value should be an OverlayId::Outer
+ ReadOnly(OverlayId),
+ /// The repo will be accessed on the Inner Overlay in Write mode, and the associated Outer overlay is also given
+ /// This is used for Public, Protected and Group overlays
+ /// First value in tuple should be the OverlayId::Inner, second the OverlayId::Outer
+ ReadWrite((OverlayId, OverlayId)),
+ /// The repo will be accessed on the Inner Overlay in Write mode, and it doesn't have an Outer overlay
+ /// This is used for Private and Dialog overlays
+ /// Value should be an OverlayId::Inner
+ WriteOnly(OverlayId),
+}
+
+impl OverlayAccess {
+ pub fn new_ro(outer_overlay: OverlayId) -> Result {
+ if let OverlayId::Outer(_digest) = outer_overlay {
+ Ok(OverlayAccess::ReadOnly(outer_overlay))
+ } else {
+ Err(NgError::InvalidArgument)
+ }
+ }
+ pub fn new_rw(inner_overlay: OverlayId, outer_overlay: OverlayId) -> Result {
+ if let OverlayId::Inner(_digest) = inner_overlay {
+ if let OverlayId::Outer(_digest) = outer_overlay {
+ Ok(OverlayAccess::ReadWrite((inner_overlay, outer_overlay)))
+ } else {
+ Err(NgError::InvalidArgument)
+ }
+ } else {
+ Err(NgError::InvalidArgument)
+ }
+ }
+ pub fn new_wo(inner_overlay: OverlayId) -> Result {
+ if let OverlayId::Inner(_digest) = inner_overlay {
+ Ok(OverlayAccess::WriteOnly(inner_overlay))
+ } else {
+ Err(NgError::InvalidArgument)
+ }
+ }
+}
+
+/// Inner Overlay Link
+///
+/// Details of the inner overlay of an NgLink
+#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
+pub struct InnerOverlayLink {
+ /// overlay public key ID
+ pub id: Identity,
-/// Topic ID: public key of the topic
-pub type TopicId = PubKey;
+ /// current root branch definition commit
+ /// The ref is split in two: id and key.
+ /// The ID can be omitted if reading the overlay members should not be allowed.
+ /// In this case, the pinning broker will not be able to subscribe to the overlay root topic
+ /// and will therefor lose access if the overlay is refreshed.
+ pub root_branch_def_id: Option,
+ pub root_branch_def_key: ObjectKey,
+}
+
+/// Overlay Link
+///
+/// Details of the overlay of an NgLink
+#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
+pub enum OverlayLink {
+ Outer(Identity),
+ Inner(InnerOverlayLink),
+ Inherit,
+}
-/// User ID: user account for broker
-pub type UserId = PubKey;
+/// Overlay session ID
+///
+/// It is a pubkey used for signing all OverlayMessage sent by the peer.
+/// Each peer generates it randomly when (re)joining the overlay network.
+pub type SessionId = PubKey;
/// Client ID: client of a user
pub type ClientId = PubKey;
@@ -1426,30 +1432,24 @@ impl ClientInfo {
// OVERLAY MESSAGES
//
-/// Overlay connection request
-///
-/// Sent to an existing overlay member to initiate a session
-#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
-pub enum OverlayConnect {
- V0(),
-}
-
-/// Overlay disconnection request
+/// Overlay leave request
///
-/// Sent to a connected overlay member to terminate a session
+/// In outerOverlay: informs the broker that the overlay is not need anymore
+/// In innerOverlay: Sent to all connected overlay participants to terminate a session
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
-pub enum OverlayDisconnect {
+pub enum OverlayLeave {
V0(),
}
-/// Content of TopicAdvertV0
+/// Content of PublisherAdvertV0
+/// the peer is matched with the InnerOverlayMessageV0.Session -> peerId.
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
-pub struct TopicAdvertContentV0 {
+pub struct PublisherAdvertContentV0 {
/// Topic public key
pub topic: TopicId,
/// Peer public key
- pub peer: PeerId,
+ pub peer: DirectPeerId,
}
/// Topic advertisement by a publisher
@@ -1457,8 +1457,8 @@ pub struct TopicAdvertContentV0 {
/// Flooded to all peers in overlay
/// Creates subscription routing table entries
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
-pub struct TopicAdvertV0 {
- pub content: TopicAdvertContentV0,
+pub struct PublisherAdvertV0 {
+ pub content: PublisherAdvertContentV0,
/// Signature over content by topic key
pub sig: Sig,
@@ -1466,22 +1466,25 @@ pub struct TopicAdvertV0 {
/// Topic advertisement by a publisher
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
-pub enum TopicAdvert {
- V0(TopicAdvertV0),
+pub enum PublisherAdvert {
+ V0(PublisherAdvertV0),
}
/// Topic subscription request by a peer
///
/// Forwarded towards all publishers along subscription routing table entries
-/// that are created by TopicAdverts
+/// that are created by PublisherAdverts
/// Creates event routing table entries along the path
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct SubReqV0 {
- /// Random ID generated by the subscriber
- pub id: u64,
-
/// Topic public key
pub topic: TopicId,
+
+ /// For initial subscription, should be None,
+ /// When updating a subscription after a new publisher has joined (with a PublisherAdvert),
+ /// then the target publisher should be entered here.
+ /// The brokers will only forward the SubscriptionRequest to that publisher (on all available paths)
+ pub publisher: Option,
}
/// Topic subscription request by a peer
@@ -1490,19 +1493,29 @@ pub enum SubReq {
V0(SubReqV0),
}
-/// Topic subscription acknowledgement by a publisher
+/// Topic subscription marker sent by all publishers, back to subscriber
///
-/// Sent to all subscribers in an Event.
-#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
-pub struct SubAckV0 {
- /// SubReq ID to acknowledge
- pub id: u64,
+/// Forwarded to all subscribers.
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct SubMarkerV0 {
+ /// The publisher broker that marks its starting cut
+ /// TODO: that could be omitted, because we can retrieve it with the SessionId
+ pub publisher: DirectPeerId,
+
+ /// The subscribed topic
+ pub topic: TopicId,
+
+ /// The subscriber
+ pub subscriber: DirectPeerId,
+
+ /// Current heads at the broker when receiving the SubReq. Can be used to safely do a CoreTopicSyncReq
+ pub known_heads: Vec,
}
/// Topic subscription acknowledgement by a publisher
-#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
-pub enum SubAck {
- V0(SubAckV0),
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum SubMarker {
+ V0(SubMarkerV0),
}
/// Topic unsubscription request by a subscriber
@@ -1521,64 +1534,37 @@ pub enum UnsubReq {
V0(UnsubReqV0),
}
-/// Topic unsubscription acknowledgement
-/// Sent to the requestor in response to an UnsubReq
-#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
-pub struct UnsubAckV0 {
- /// Topic public key
- pub topic: TopicId,
-}
-#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
-pub enum UnsubAck {
- V0(UnsubAckV0),
-}
-
-/// Branch change notification
-/// Contains a chunk of a newly added Commit or File referenced by a commit.
-#[derive(Clone, Debug, Serialize, Deserialize)]
-pub struct ChangeV0 {
- /// Block with encrypted content
- pub content: Block,
-
- /// Encrypted key for the Commit object in content
- /// Only set for the root block of the object
- /// The key is encrypted using ChaCha20:
- /// - key: BLAKE3 derive_key ("NextGraph Event ObjectRef ChaCha20 key",
- /// branch_pubkey + branch_secret + publisher_pubkey)
- /// - nonce: commit_seq
- pub key: Option,
-}
-
-/// Body of EventContentV0
-#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
-pub enum EventBodyV0 {
- SubAck,
- Change,
-}
-
/// Content of EventV0
-#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
+/// Contains the object of newly published Commit, its optional blocks, and optional refs and their blocks.
+/// If a block is not present in the Event, its ID should be present in block_ids and the block should be put on the emitting broker beforehand with BlocksPut.
+#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct EventContentV0 {
/// Pub/sub topic
pub topic: TopicId,
- /// Publisher pubkey encrypted with ChaCha20:
- /// - key: BLAKE3 derive_key ("NextGraph Event Publisher ChaCha20 key",
- /// repo_pubkey + repo_secret +
- /// branch_pubkey + branch_secret)
- pub publisher: [u8; 32], // PubKey
+ pub publisher: ForwardedPeerId,
/// Commit sequence number of publisher
- pub seq: u32,
+ pub seq: u64,
- /// Event body
- pub body: EventBodyV0,
+ /// Blocks with encrypted content. First in the list is always the commit block, the others are optional.
+ pub blocks: Vec,
+
+ /// Ids of additional Blocks with encrypted content that are not to be pushed in the pub/sub
+ pub block_ids: Vec,
+
+ /// Encrypted key for the Commit object (the first Block in blocks)
+ /// The key is encrypted using ChaCha20:
+ /// - key: BLAKE3 derive_key ("NextGraph Event ObjectRef ChaCha20 key",
+ /// repo_pubkey + branch_pubkey + branch_secret + publisher)
+ /// - nonce: commit_seq
+ pub key: Option,
}
/// Pub/sub event published in a topic
///
/// Forwarded along event routing table entries
-#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
+#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct EventV0 {
pub content: EventContentV0,
@@ -1587,7 +1573,7 @@ pub struct EventV0 {
}
/// Pub/sub event published in a topic
-#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
+#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum Event {
V0(EventV0),
}
@@ -1595,12 +1581,18 @@ pub enum Event {
/// Object search in a pub/sub topic
///
/// Sent along the reverse path of a pub/sub topic
-/// from a subscriber to all publishers.
+/// from a subscriber to one publisher at a time.
+/// fanout is always 1
+/// if result is none, tries another path if several paths available locally
+/// answered with a BlockResult
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BlockSearchTopicV0 {
/// Topic to forward the request in
pub topic: TopicId,
+ /// Also search in subscribers
+ pub search_in_subs: bool,
+
/// List of Object IDs to request
pub ids: Vec,
@@ -1611,13 +1603,16 @@ pub struct BlockSearchTopicV0 {
pub path: Vec,
}
-/// Object request by ID
+/// Object request by ID to publishers
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum BlockSearchTopic {
V0(BlockSearchTopicV0),
}
-/// Block search along a random walk
+/// Block search along a random walk in the overlay
+/// fanout is always 1
+/// if result is none, tries another path if several paths available locally
+/// answered with a BlockResult
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BlockSearchRandomV0 {
/// List of Block IDs to request
@@ -1627,13 +1622,14 @@ pub struct BlockSearchRandomV0 {
pub include_children: bool,
/// Number of random nodes to forward the request to at each step
- pub fanout: u8,
+ // pub fanout: u8,
+ // for now fanout is always 1
- /// List of Peer IDs the request traversed so far
- pub path: Vec,
+ /// List of Broker Peer IDs the request traversed so far
+ pub path: Vec,
}
-/// Block request by ID using a random walk
+/// Block request by ID using a random walk in the overlay
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum BlockSearchRandom {
V0(BlockSearchRandomV0),
@@ -1641,13 +1637,9 @@ pub enum BlockSearchRandom {
/// Response to a BlockSearch* request
///
-/// Follows request path with possible shortcuts.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BlockResultV0 {
- /// Response path
- pub path: Vec,
-
- /// Resulting Object(s)
+ /// Resulting Blocks(s)
pub payload: Vec,
}
@@ -1657,173 +1649,98 @@ pub enum BlockResult {
V0(BlockResultV0),
}
-/// Request latest events corresponding to the branch heads in a pub/sub topic
-///
-/// In response an Event is sent for each commit chunk that belong to branch heads
-/// that are not present in the requestor's known heads
-#[derive(Clone, Debug, Serialize, Deserialize)]
-pub struct BranchHeadsReqV0 {
- /// Topic public key of the branch
- pub topic: TopicId,
-
- /// Known heads
- pub known_heads: Vec,
-}
-
-/// Request latest events corresponding to the branch heads in a pub/sub topic
-#[derive(Clone, Debug, Serialize, Deserialize)]
-pub enum BranchHeadsReq {
- V0(BranchHeadsReqV0),
-}
-
-/// Branch synchronization request
+/// Topic synchronization request
///
/// In response a stream of `Block`s of the requested Objects are sent
/// that are not present in the requestor's known heads and commits
#[derive(Clone, Debug, Serialize, Deserialize)]
-pub struct BranchSyncReqV0 {
- /// Heads to request, including all their dependencies
- pub heads: Vec,
+pub struct TopicSyncReqV0 {
+ /// Topic public key
+ pub topic: TopicId,
/// Fully synchronized until these commits
pub known_heads: Vec,
/// Known commit IDs since known_heads
+ // TODO: is this going to be used?
pub known_commits: BloomFilter,
}
-/// Branch synchronization request
+/// Topic synchronization request
#[derive(Clone, Debug, Serialize, Deserialize)]
-pub enum BranchSyncReq {
- V0(BranchSyncReqV0),
+pub enum TopicSyncReq {
+ V0(TopicSyncReqV0),
}
-impl BranchSyncReq {
- pub fn heads(&self) -> &Vec {
+impl TopicSyncReq {
+ pub fn topic(&self) -> &TopicId {
match self {
- BranchSyncReq::V0(o) => &o.heads,
+ TopicSyncReq::V0(o) => &o.topic,
}
}
pub fn known_heads(&self) -> &Vec {
match self {
- BranchSyncReq::V0(o) => &o.known_heads,
+ TopicSyncReq::V0(o) => &o.known_heads,
}
}
pub fn known_commits(&self) -> &BloomFilter {
match self {
- BranchSyncReq::V0(o) => &o.known_commits,
+ TopicSyncReq::V0(o) => &o.known_commits,
}
}
}
-/// Events the requestor needs, see EventReqV0
-#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
-pub struct NeedEventsV0 {
- /// Publisher ID
- pub publisher: Digest,
-
- /// First sequence number to request
- pub from: u32,
-
- /// Last sequence number to request
- pub to: u32,
-}
-
-/// Events the responder has, see EventRespV0
-#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
-pub struct HaveEventsV0 {
- /// Publisher ID
- pub publisher: Digest,
-
- /// First sequence number to send
- pub from: u32,
-
- /// Last sequence number to send
- pub to: u32,
-}
-
-/// Request missed events for a pub/sub topic
-/// for the specified range of publisher sequence numbers
-///
-/// In response an EventResp then a stream of Events are sent
-#[derive(Clone, Debug, Serialize, Deserialize)]
-pub struct EventReqV0 {
- /// Topic public key
- pub topic: TopicId,
-
- /// Events needed by the requestor
- pub need: Vec,
-}
-
-/// Request missed events for a pub/sub topic
-#[derive(Clone, Debug, Serialize, Deserialize)]
-pub enum EventReq {
- V0(EventReqV0),
-}
-
-/// Response to an EventReq
-#[derive(Clone, Debug, Serialize, Deserialize)]
-pub struct EventRespV0 {
- /// Events the responder has
- pub have: Vec,
-}
-
-/// Response to an EventReq
-#[derive(Clone, Debug, Serialize, Deserialize)]
-pub enum EventResp {
- V0(EventRespV0),
-}
-
-/// Content of CoreRequestV0
+/// Status of a Forwarded Peer, sent in the Advert
#[derive(Clone, Debug, Serialize, Deserialize)]
-pub enum CoreRequestContentV0 {
- EventReq(EventReq),
- BranchHeadsReq(BranchHeadsReq),
- BranchSyncReq(BranchSyncReq),
+pub enum PeerStatus {
+ Connected,
+ Disconnected,
}
-/// Request sent to an overlay
+/// ForwardedPeerAdvertV0
+/// peer_advert.forwarded_by is matched with sessionid->peerid
#[derive(Clone, Debug, Serialize, Deserialize)]
-pub struct CoreRequestV0 {
- /// Request ID
- pub id: i64,
+pub struct ForwardedPeerAdvertV0 {
+ /// PeerAdvert received from Client
+ pub peer_advert: PeerAdvertV0,
- /// Request content
- pub content: CoreRequestContentV0,
-}
+ /// Hashed user Id, used to prevent concurrent connection from different brokers
+ /// BLAKE3 keyed hash over the UserId
+ /// - key: BLAKE3 derive_key ("NextGraph Overlay Id BLAKE3 key", overlayId)
+ pub user_hash: Digest,
-/// Request sent to an overlay
-#[derive(Clone, Debug, Serialize, Deserialize)]
-pub enum CoreRequest {
- V0(CoreRequestV0),
+ /// whether the Advert is about connection or disconnection
+ pub status: PeerStatus,
}
-/// Content of CoreResponseV0
+/// Forwarded Peer advertisement
#[derive(Clone, Debug, Serialize, Deserialize)]
-pub enum CoreResponseContentV0 {
- EmptyResponse(()),
- Block(Block),
- EventResp(EventResp),
- Event(Event),
+pub enum ForwardedPeerAdvert {
+ V0(ForwardedPeerAdvertV0),
}
-/// Request sent to an overlay
+/// ForwardedPeerConflictV0
+/// peer_advert.forwarded_by is matched with sessionid->peerid
+/// When a conflict occurs, the peer is immediately removed locally
+/// When the forwarding broker receives the conflict (or notices it), it disconnects the client(s).
+/// In order to avoid conflicts, the highest version of PeerAdvert should win, when the Forwarding Broker is different.
+/// Disconnect wins over connect, for the exact same peer, version and forwarding broker.
+/// Conflict can occur when same user_hash, on 2 different Forwarding Broker
+/// Or when same peerId appears on 2 different Forwarding Broker.
#[derive(Clone, Debug, Serialize, Deserialize)]
-pub struct CoreResponseV0 {
- /// Request ID
- pub id: i64,
-
- /// Result
- pub result: u16,
+pub struct ForwardedPeerConflictV0 {
+ /// First conflicting PeerAdvert
+ pub advert_1: ForwardedPeerAdvertV0,
+ /// Second conflicting PeerAdvert
+ pub advert_2: ForwardedPeerAdvertV0,
- /// Response content
- pub content: CoreResponseContentV0,
+ pub error_code: u16,
}
-/// Request sent to an CoreRequest
+/// Forwarded Peer advertisement conflict
#[derive(Clone, Debug, Serialize, Deserialize)]
-pub enum CoreResponse {
- V0(CoreResponseV0),
+pub enum ForwardedPeerConflict {
+ V0(ForwardedPeerConflictV0),
}
/// Content of PeerAdvertV0
@@ -1832,10 +1749,13 @@ pub struct PeerAdvertContentV0 {
/// Peer ID
pub peer: PeerId,
+ /// Id of the broker that is forwarding the peer
+ pub forwarded_by: Option,
+
/// Topic subscriptions
- pub subs: BloomFilter128,
+ // pub subs: BloomFilter128,
- /// Network addresses
+ /// Network addresses, must be empty for forwarded peers
pub address: Vec,
/// Version number
@@ -1848,7 +1768,10 @@ pub struct PeerAdvertContentV0 {
/// Peer advertisement
///
-/// Sent periodically across the overlay along random walks.
+/// Sent when a peer joins an inner overlay.
+/// Used only for forwardedPeer for now.
+/// In the future, Core brokers could exchange PeerAdvert on the global overlay, and also do some PeerSearch to search for IPs/newer version of PeerAdvert
+/// When the forwarding broker receives a client connection, it checks that the peer isn't
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PeerAdvertV0 {
/// Peer advertisement content
@@ -1856,9 +1779,6 @@ pub struct PeerAdvertV0 {
/// Signature over content by peer's private key
pub sig: Sig,
-
- /// Time-to-live, decremented at each hop
- pub ttl: u8,
}
/// Peer advertisement
@@ -1880,120 +1800,510 @@ impl PeerAdvert {
}
}
-/// Content of CoreMessagePaddedV0
+/// Content of InnerOverlayMessageV0
#[derive(Clone, Debug, Serialize, Deserialize)]
-pub enum CoreMessageContentV0 {
- OverlayConnect(OverlayConnect),
- OverlayDisconnect(OverlayDisconnect),
- PeerAdvert(PeerAdvert),
- TopicAdvert(TopicAdvert),
+pub enum InnerOverlayMessageContentV0 {
+ OverlayLeave(OverlayLeave),
+ ForwardedPeerAdvert(ForwardedPeerAdvert),
+ ForwardedPeerConflict(ForwardedPeerConflict),
+ PublisherJoined(PublisherAdvert),
+ PublisherLeft(PublisherAdvert),
SubReq(SubReq),
- SubAck(SubAck),
+ SubMarker(SubMarker),
UnsubReq(UnsubReq),
- UnsubAck(UnsubAck),
Event(Event),
- BlockSearchTopic(BlockSearchTopic),
- BlockSearchRandom(BlockSearchRandom),
- BlockResult(BlockResult),
- CoreRequest(CoreRequest),
- CoreResponse(CoreResponse),
+ //PostInboxRequest(PostInboxRequest),
+ //PostInboxResponse(PostInboxResponse),
}
-/// Overlay message
+/// Inner Overlay message payload V0
#[derive(Clone, Debug, Serialize, Deserialize)]
-pub struct CoreMessageV0 {
- /// Overlay ID
- pub overlay: OverlayId,
+pub struct InnerOverlayMessagePayloadV0 {
+ /// Sequence number incremented by peer when sending every overlay message in a session
+ /// Used to prevent replay attacks inside the overlay
+ pub seq: u64,
+
+ pub content: InnerOverlayMessageContentV0,
+}
+/// Inner Overlay message V0
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct InnerOverlayMessageV0 {
/// Session ID
pub session: SessionId,
- /// Padded content encrypted with ChaCha20
- /// - overlay_secret: BLAKE3 derive_key ("NextGraph Overlay BLAKE3 key",
- /// repo_pubkey + repo_secret)
- /// - key: BLAKE3 derive_key ("NextGraph CoreMessage ChaCha20 key",
- /// overlay_secret + session_id)
- /// - nonce: per-session message sequence number of sending peer
- pub content: CoreMessageContentV0,
+ pub payload: InnerOverlayMessagePayloadV0,
+
+ /// Signature with Session private key, over payload
+ pub sig: Sig,
/// Optional padding
#[serde(with = "serde_bytes")]
pub padding: Vec,
-
- /// BLAKE3 MAC
- /// BLAKE3 keyed hash over the encrypted content
- /// - key: BLAKE3 derive_key ("NextGraph CoreMessage BLAKE3 key",
- /// overlay_secret + session_id)
- pub mac: Digest,
-}
-
-/// Overlay message
-#[derive(Clone, Debug, Serialize, Deserialize)]
-pub enum CoreMessage {
- V0(CoreMessageV0),
}
-//
-// ADMIN PROTOCOL
-//
-
-/// Content of `AdminRequestV0`
+/// Inner Overlay message
#[derive(Clone, Debug, Serialize, Deserialize)]
-pub enum AdminRequestContentV0 {
- AddUser(AddUser),
- DelUser(DelUser),
- ListUsers(ListUsers),
- ListInvitations(ListInvitations),
- AddInvitation(AddInvitation),
-}
-impl AdminRequestContentV0 {
- pub fn type_id(&self) -> TypeId {
- match self {
- Self::AddUser(a) => a.type_id(),
- Self::DelUser(a) => a.type_id(),
- Self::ListUsers(a) => a.type_id(),
- Self::ListInvitations(a) => a.type_id(),
- Self::AddInvitation(a) => a.type_id(),
- }
- }
- pub fn get_actor(&self) -> Box {
- match self {
- Self::AddUser(a) => a.get_actor(),
- Self::DelUser(a) => a.get_actor(),
- Self::ListUsers(a) => a.get_actor(),
- Self::ListInvitations(a) => a.get_actor(),
- Self::AddInvitation(a) => a.get_actor(),
- }
- }
+pub enum InnerOverlayMessage {
+ V0(InnerOverlayMessageV0),
}
-/// Admin request
+/// Overlay Advert Payload V0
#[derive(Clone, Debug, Serialize, Deserialize)]
-pub struct AdminRequestV0 {
- /// Request ID
- pub id: i64,
+pub struct OverlayAdvertPayloadV0 {
+ /// the target Overlay ID (cannot be an Outer overlay)
+ pub overlay: OverlayId,
- /// Request content
- pub content: AdminRequestContentV0,
+ /// the newly generated session ID the peer will use in this overlay
+ /// All the revoked sessionIDs are kept locally by their initiator.
+ pub session: SessionId,
- /// Signature over content by admin key
- pub sig: Sig,
+ /// Current sequence number. For a new session, must be zero.
+ pub seq: u64,
- /// THe admin user requesting this operation
- pub admin_user: PubKey,
+ /// the previous session ID the peer was using in this overlay. Used to cleanup seq counters maintained in each other peer
+ /// if the previous session is empty (because it is the first time broker joins this overlay)
+ /// or if a remote peer doesn't find this session kept locally, it is not an error.
+ /// In the later case (if broker missed some intermediary sessions), the remote peer can ask the initiator peer if the last known
+ /// session can be locally revoked with a ConfirmRevokedSession message (answered with yes or no)
+ pub previous_session: Option,
- /// Optional padding
- #[serde(with = "serde_bytes")]
- pub padding: Vec,
+ /// peer ID of the broker issuing this Advert
+ pub peer: DirectPeerId,
}
-impl AdminRequestV0 {
- pub fn get_actor(&self) -> Box {
- self.content.get_actor()
- }
+/// Overlay Advert V0 : used by a broker peer every time it (re)joins an overlay
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct OverlayAdvertV0 {
+ pub payload: OverlayAdvertPayloadV0,
+
+ /// Signature with peerId private key, over payload
+ pub sig: Sig,
}
-/// Admin request
+/// Overlay Advert : used by a broker peer every time it (re)joins an overlay
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum OverlayAdvert {
+ V0(OverlayAdvertV0),
+}
+
+/// CoreBrokerJoinedAdvert V0
+/// Each broker that is already part of an overlay, when receiving the CoreBrokerJoinedAdvert, should answer with one direct message
+/// to the joining peer (found in OverlayAdvertPayloadV0.peer) for each overlay, containing an OverlayAdvertMarker containing their current sequence number.
+/// This is sent for each path (in case multiple paths arrive to the same broker). Only the first sequence number received by joining peer is kept locally
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct CoreBrokerJoinedAdvertV0 {
+ /// list of overlays joined by an initiator broker, and that the forwarding broker has also previously joined
+ /// the forwarding broker keeps the ingress edge and all egress edges in the coreRoutingTable
+ pub overlays: Vec,
+}
+
+/// CoreBrokerLeftAdvert V0
+/// A broker has disconnected from another broker, and the routes need to be updated
+/// this is not used to leave one specific overlay. see OverlayLeave message for that purpose
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct CoreBrokerLeftAdvertV0 {
+ /// The broker that disconnected from the one that is emitting this advert.
+ pub disconnected: DirectPeerId,
+}
+
+/// CoreOverlayJoinedAdvert V0
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct CoreOverlayJoinedAdvertV0 {
+ /// One additional overlay joined by an initiator broker, and that the forwarding broker has also previously joined
+ /// the forwarding broker keeps the ingress edge and all egress edges in the coreRoutingTable
+ pub overlay: OverlayAdvertV0,
+}
+
+/// Content of CoreAdvert V0
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum CoreAdvertContentV0 {
+ BrokerJoined(CoreBrokerJoinedAdvertV0),
+ BrokerLeft(CoreBrokerLeftAdvertV0),
+ OverlayJoined(CoreOverlayJoinedAdvertV0),
+}
+
+/// CoreAdvert V0
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct CoreAdvertV0 {
+ pub content: CoreAdvertContentV0,
+
+ /// list of brokers on the path that was followed to deliver this advert.
+ /// new entry pushed each time a forward is happening in the core network
+ pub path: Vec,
+
+ /// content signed by the first broker in the path
+ pub sig: Sig,
+
+ /// Optional padding
+ #[serde(with = "serde_bytes")]
+ pub padding: Vec,
+}
+
+/// OverlayAdvertMarker V0
+/// when receiving a marker, the broker saves the ingress edge and the corresponding remote peer and
+/// overlay that can be reached (the OverlayAdvertPayloadV0.peer and .overlay) in the CoreRoutingTable
+/// It also saves the sessionId and seq number
+/// then a ReturnPathTimingAdvert is sent back
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct OverlayAdvertMarkerV0 {
+ pub marker: OverlayAdvertV0,
+
+ /// New SessionId that triggered this marker (to avoid replay attacks in the core network)
+ pub in_reply_to: SessionId,
+
+ /// path from the new broker who started a session, to the broker that is sending the marker
+ pub path: Vec,
+
+ /// randomly generated nonce used for the reply (a ReturnPathTimingMarker) that will be sent after receiving the marker
+ pub reply_nonce: u64,
+}
+
+/// Core Block Get V0
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct CoreBlockGetV0 {
+ /// Block ID to request
+ pub id: BlockId,
+
+ /// Whether or not to include all children recursively
+ pub include_children: bool,
+
+ /// randomly generated number by requester, used for sending reply. Purpose is to defeat replay attacks in the overlay
+ /// the requester keeps track of req_nonce and destination peerid.
+ pub req_nonce: u64,
+}
+
+/// Core Block Result V0
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct CoreBlockResultV0 {
+ /// Resulting Object(s)
+ pub payload: Vec,
+
+ /// randomly generated number by requester, as received in the request
+ pub req_nonce: u64,
+}
+
+/// ReturnPathTimingAdvertV0
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct ReturnPathTimingAdvertV0 {
+ /// Signature over nonce, by sessionId
+ pub sig: Sig,
+
+ /// randomly generated number as received in the OverlayAdvertMarker
+ pub nonce: u64,
+}
+
+/// Content of CoreDirectMessage V0
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum CoreDirectMessageContentV0 {
+ OverlayAdvertMarker(OverlayAdvertMarkerV0),
+ ReturnPathTimingAdvert(ReturnPathTimingAdvertV0),
+ BlockGet(CoreBlockGetV0),
+ BlockResult(CoreBlockResultV0),
+ //PostInbox,
+ //PartialSignature,
+ //ClientDirectMessage //for messages between forwarded or direct peers
+}
+
+/// CoreDirectMessage V0
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct CoreDirectMessageV0 {
+ pub content: CoreDirectMessageContentV0,
+
+ /// list of brokers on the path that must be followed to deliver this message, next hop is at the bottom of the list.
+ /// last entry on the list is popped each time a broker is forwarding upstream
+ /// when list size is zero, the final destination is reached.
+ /// if only one peer in list, and peer not found in local CoreRoutingTable, use the best route to reach it (without popping)
+ pub reverse_path: Vec,
+
+ /// The sender
+ pub from: DirectPeerId,
+
+ /// content signed by the sender
+ pub sig: Sig,
+
+ /// Optional padding
+ #[serde(with = "serde_bytes")]
+ pub padding: Vec,
+}
+
+/// CoreBrokerConnect V0
+/// replied with CoreBrokerConnectResponse
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct CoreBrokerConnectV0 {
+ pub inner_overlays: Vec,
+ pub outer_overlays: Vec,
+}
+
+/// CoreBrokerConnect
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum CoreBrokerConnect {
+ V0(CoreBrokerConnectV0),
+}
+
+/// CoreBrokerConnectResponse
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum CoreBrokerConnectResponse {
+ V0(CoreBrokerConnectResponseV0),
+}
+
+impl CoreBrokerConnect {
+ pub fn core_message(&self, id: i64) -> CoreMessage {
+ match self {
+ CoreBrokerConnect::V0(v0) => CoreMessage::V0(CoreMessageV0::Request(CoreRequestV0 {
+ padding: vec![],
+ id,
+ content: CoreRequestContentV0::BrokerConnect(v0.clone()),
+ })),
+ }
+ }
+}
+
+/// sent to a direct peer just before closing the connection
+pub type CoreBrokerDisconnectV0 = ();
+
+/// Content of CoreOverlayJoin V0
+/// // replied with an emptyResponse, and an error code if OverlayId not present on remote broker
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum CoreOverlayJoinV0 {
+ Inner(OverlayAdvertV0),
+ Outer(Digest),
+}
+
+/// Content of OuterOverlayResponse V0
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum OuterOverlayResponseContentV0 {
+ EmptyResponse(()),
+ Block(Block),
+ //PostInboxResponse(PostInboxResponse),
+}
+
+/// Content of OuterOverlayRequest V0
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum OuterOverlayRequestContentV0 {
+ TopicSyncReq(TopicSyncReq),
+ OverlayLeave(OverlayLeave),
+ TopicSub(PubKey),
+ TopicUnsub(PubKey),
+ BlockGet(BlockGet),
+ //PostInboxRequest(PostInboxRequest),
+}
+
+/// OuterOverlayRequestV0 V0
+/// replied with OuterOverlayResponseV0
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct OuterOverlayRequestV0 {
+ pub overlay: Digest,
+ pub content: OuterOverlayRequestContentV0,
+}
+
+/// OuterOverlayResponse V0
+/// reply to an OuterOverlayRequest V0
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct OuterOverlayResponseV0 {
+ pub overlay: Digest,
+ pub content: OuterOverlayResponseContentV0,
+}
+
+/// Core Topic synchronization request
+///
+/// behaves like BlockSearchTopic (primarily searches among the publishers)
+/// fanout is 1 for now
+/// In response a stream of `Block`s of the requested Objects are sent
+/// that are not present in the requestor's known heads and commits
+///
+/// if some target_heads are not found locally, then all successors of known_heads are sent anyway.
+/// Then this temporary HEAD is used to propagate the CoreTopicSyncReq to upstream brokers
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct CoreTopicSyncReqV0 {
+ /// Topic public key
+ pub topic: TopicId,
+
+ /// Also search in subscribers, in addition to publishers
+ pub search_in_subs: bool,
+
+ /// Fully synchronized until these commits
+ pub known_heads: Vec,
+
+ /// Stop synchronizing when these commits are met
+ pub target_heads: Vec,
+
+ /// Known commit IDs since known_heads
+ // TODO: is this going to be used?
+ pub known_commits: BloomFilter,
+}
+
+/// Topic synchronization request
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum CoreTopicSyncReq {
+ V0(CoreTopicSyncReqV0),
+}
+
+/// Content of CoreRequest V0
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum CoreRequestContentV0 {
+ BrokerConnect(CoreBrokerConnectV0),
+ BrokerDisconnect(CoreBrokerDisconnectV0),
+ OverlayJoin(CoreOverlayJoinV0),
+ BlockSearchTopic(BlockSearchTopicV0),
+ BlockSearchRandom(BlockSearchRandomV0),
+ TopicSyncReq(CoreTopicSyncReqV0),
+ OuterOverlayRequest(OuterOverlayRequestV0),
+}
+
+/// CoreRequest V0
+/// replied with CoreResponse V0
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct CoreRequestV0 {
+ /// Request ID
+ pub id: i64,
+ pub content: CoreRequestContentV0,
+
+ /// Optional padding
+ #[serde(with = "serde_bytes")]
+ pub padding: Vec,
+}
+
+/// Request sent to a broker in the core network
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum CoreRequest {
+ V0(CoreRequestV0),
+}
+
+/// CoreBrokerConnectResponse V0
+/// reply to a CoreBrokerConnect V0
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct CoreBrokerConnectResponseV0 {
+ pub successes: Vec,
+ pub errors: Vec,
+}
+
+/// Content CoreResponse V0
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum CoreResponseContentV0 {
+ BrokerConnectResponse(CoreBrokerConnectResponseV0),
+ BlockResult(BlockResultV0),
+ OuterOverlayResponse(OuterOverlayResponseV0),
+ EmptyResponse(()),
+}
+
+/// CoreResponse V0
+/// reply to an CoreRequest V0
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct CoreResponseV0 {
+ /// Request ID
+ pub id: i64,
+
+ /// Result
+ pub result: u16,
+ pub content: CoreResponseContentV0,
+
+ /// Optional padding
+ #[serde(with = "serde_bytes")]
+ pub padding: Vec,
+}
+
+/// Response to a Request sent to a broker in the core network
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum CoreResponse {
+ V0(CoreResponseV0),
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum OuterOverlayMessageContentV0 {
+ Event(Event),
+}
+
+/// OuterOverlayMessage V0
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct OuterOverlayMessageV0 {
+ pub overlay: Digest,
+
+ pub content: OuterOverlayMessageContentV0,
+
+ /// Optional padding
+ #[serde(with = "serde_bytes")]
+ pub padding: Vec,
+}
+
+/// CoreMessageV0
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum CoreMessageV0 {
+ Request(CoreRequestV0),
+ Response(CoreResponseV0),
+ Advert(CoreAdvertV0),
+ Direct(CoreDirectMessageV0),
+ InnerOverlay(InnerOverlayMessageV0),
+ OuterOverlay(OuterOverlayMessageV0),
+}
+
+/// Core message
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum CoreMessage {
+ V0(CoreMessageV0),
+}
+
+//
+// ADMIN PROTOCOL
+//
+
+/// Content of `AdminRequestV0`
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum AdminRequestContentV0 {
+ AddUser(AddUser),
+ DelUser(DelUser),
+ ListUsers(ListUsers),
+ ListInvitations(ListInvitations),
+ AddInvitation(AddInvitation),
+}
+impl AdminRequestContentV0 {
+ pub fn type_id(&self) -> TypeId {
+ match self {
+ Self::AddUser(a) => a.type_id(),
+ Self::DelUser(a) => a.type_id(),
+ Self::ListUsers(a) => a.type_id(),
+ Self::ListInvitations(a) => a.type_id(),
+ Self::AddInvitation(a) => a.type_id(),
+ }
+ }
+ pub fn get_actor(&self) -> Box {
+ match self {
+ Self::AddUser(a) => a.get_actor(),
+ Self::DelUser(a) => a.get_actor(),
+ Self::ListUsers(a) => a.get_actor(),
+ Self::ListInvitations(a) => a.get_actor(),
+ Self::AddInvitation(a) => a.get_actor(),
+ }
+ }
+}
+
+/// Admin request
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct AdminRequestV0 {
+ /// Request ID
+ pub id: i64,
+
+ /// Request content
+ pub content: AdminRequestContentV0,
+
+ /// Signature over content by admin key
+ pub sig: Sig,
+
+ /// THe admin user requesting this operation
+ pub admin_user: PubKey,
+
+ /// Optional padding
+ #[serde(with = "serde_bytes")]
+ pub padding: Vec,
+}
+
+impl AdminRequestV0 {
+ pub fn get_actor(&self) -> Box {
+ self.content.get_actor()
+ }
+}
+
+/// Admin request
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum AdminRequest {
V0(AdminRequestV0),
@@ -2153,70 +2463,220 @@ impl AdminResponse {
// CLIENT PROTOCOL
//
-/// Request to join an overlay
+/// Request to open a repo in a non-durable way (without pinning it).
+/// When client will disconnect, the subscriptions and publisherAdvert of the topics will be removed,
+/// except if a PinRepo occurred before or after the OpenRepo
#[derive(Clone, Debug, Serialize, Deserialize)]
-pub struct OverlayJoinV0 {
- /// Overlay secret
- pub secret: SymKey,
+pub struct OpenRepoV0 {
+ /// Repo Hash
+ pub hash: RepoHash,
- /// Repository the overlay belongs to.
- /// Only set for local brokers.
- pub repo_pubkey: Option,
+ // for RW overlay, the overlay that should be used in the clientmessage is the innerOverlay
+ pub overlay: OverlayAccess,
- /// Peers to connect to
+ /// Broker peers to connect to in order to join the overlay
pub peers: Vec,
+
+ /// Maximum number of peers to connect to for this overlay (only valid for an inner (RW/WO) overlay)
+ pub max_peer_count: u16,
+
+ /// list of topics that should be subscribed to
+ pub ro_topics: Vec,
+
+ /// list of topics for which we will be a publisher
+ /// only possible with inner (RW or WO) overlays.
+ pub rw_topics: Vec,
}
-/// Request to join an overlay
+/// Request to open a repo
#[derive(Clone, Debug, Serialize, Deserialize)]
-pub enum OverlayJoin {
- V0(OverlayJoinV0),
+pub enum OpenRepo {
+ V0(OpenRepoV0),
}
-impl OverlayJoin {
- pub fn repo_pubkey(&self) -> Option {
+impl OpenRepo {
+ pub fn peers(&self) -> &Vec {
match self {
- OverlayJoin::V0(o) => o.repo_pubkey,
+ OpenRepo::V0(o) => &o.peers,
}
}
- pub fn secret(&self) -> &SymKey {
+}
+
+/// Block pinning strategy. When Pinning a repo, user can choose to Pin locally on the broker:
+/// all their published commits (if they are publisher) or all the commits of all the users.
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum BlockPinningStrategy {
+ MyCommits,
+ AllCommits,
+ None,
+}
+
+/// Request to pin a repo on the broker.
+/// When client will disconnect, the subscriptions and publisherAdvert of the topics will be remain active on the broker,
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct PinRepoV0 {
+ /// Repo Hash
+ pub hash: RepoHash,
+
+ /// for RW overlay, the overlay that should be used in the clientmessage is the innerOverlay
+ pub overlay: OverlayAccess,
+
+ /// Root topic of the overlay, used to listen to overlay refreshes. Only used for inner (RW or WO) overlays
+ pub overlay_root_topic: Option,
+
+ /// only possible for RW overlays
+ pub expose_outer: bool,
+
+ /// Broker peers to connect to in order to join the overlay
+ /// If the repo has previously been opened (during the same session) then peers info can be omitted
+ pub peers: Vec,
+
+ /// Maximum number of peers to connect to for this overlay (only valid for an inner (RW/WO) overlay)
+ pub max_peer_count: u16,
+
+ /// list of topics that should be subscribed to
+ /// If the repo has previously been opened (during the same session) then ro_topics info can be omitted
+ pub ro_topics: Vec,
+
+ /// list of topics for which we will be a publisher
+ /// only possible with inner (RW or WO) overlays.
+ /// If the repo has previously been opened (during the same session) then rw_topics info can be omitted
+ pub rw_topics: Vec,
+
+ /// Pin incoming commits' blocks (for subscribed topics)
+ pub pin_all_events: bool, // TODO pub inbox_proof
+
+ // TODO pub signer_proof
+}
+
+/// Request to pin a repo
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum PinRepo {
+ V0(PinRepoV0),
+}
+
+impl PinRepo {
+ pub fn peers(&self) -> &Vec {
match self {
- OverlayJoin::V0(o) => &o.secret,
+ PinRepo::V0(o) => &o.peers,
}
}
- pub fn peers(&self) -> &Vec {
+}
+
+/// Request to unpin a repo on the broker.
+/// When client will disconnect, the subscriptions and publisherAdvert of the topics will be removed on the broker
+/// (for that user only. other users might continue to have the repo pinned)
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct UnpinRepoV0 {
+ /// Repo Hash
+ pub hash: RepoHash,
+}
+
+/// Request to unpin a repo
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum UnpinRepo {
+ V0(UnpinRepoV0),
+}
+
+impl UnpinRepo {
+ pub fn hash(&self) -> &RepoHash {
match self {
- OverlayJoin::V0(o) => &o.peers,
+ UnpinRepo::V0(o) => &o.hash,
}
}
}
-/// Request to leave an overlay
-#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
-pub enum OverlayLeave {
- V0(),
+/// Request the status of pinning for a repo on the broker. V0
+/// returns an error code if not pinned, otherwise returns a RepoPinStatusV0
+/// the overlay entered in ClientMessage is important. if it is the outer, only outer pinning will be checked.
+/// if it is the inner overlay, only the inner pinning will be checked.
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct RepoPinStatusReqV0 {
+ /// Repo Hash
+ pub hash: RepoHash,
}
-/// Overlay status request
-#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
-pub enum OverlayStatusReq {
- V0(),
+/// Request the status of pinning for a repo on the broker.
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum RepoPinStatusReq {
+ V0(RepoPinStatusReqV0),
+}
+
+impl RepoPinStatusReq {
+ pub fn hash(&self) -> &RepoHash {
+ match self {
+ RepoPinStatusReq::V0(o) => &o.hash,
+ }
+ }
}
-/// Overlay status response
+/// Response with the status of pinning for a repo on the broker. V0
#[derive(Clone, Debug, Serialize, Deserialize)]
-pub struct OverlayStatusRespV0 {
- /// Whether or not the broker has joined the overlay
- pub joined: bool,
+pub struct RepoPinStatusV0 {
+ /// Repo Hash
+ pub hash: RepoHash,
- /// List of peers currently connected in the overlay
- pub peers: Vec,
+ /// only possible for RW overlays
+ pub expose_outer: bool,
+
+ /// list of topics that are subscribed to (not included the RW ones. see list just below)
+ pub ro_topics: Vec,
+
+ /// list of topics that are publisher
+ pub rw_topics: Vec,
+ // TODO pub inbox_proof
+
+ // TODO pub signer_proof
}
-/// Overlay status response
+/// Response with the status of pinning for a repo on the broker.
#[derive(Clone, Debug, Serialize, Deserialize)]
-pub enum OverlayStatusResp {
- V0(OverlayStatusRespV0),
+pub enum RepoPinStatus {
+ V0(RepoPinStatusV0),
+}
+
+impl RepoPinStatus {
+ pub fn hash(&self) -> &RepoHash {
+ match self {
+ RepoPinStatus::V0(o) => &o.hash,
+ }
+ }
+}
+
+/// Request subscription to a `Topic` of an already opened or pinned Repo
+#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
+pub struct TopicSubV0 {
+ /// Topic to subscribe
+ pub topic: PubKey,
+
+ /// Hash of the repo that was previously opened or pinned
+ pub repo_hash: RepoHash,
+
+ /// Publisher need to provide a signed `PublisherAdvert` for the PeerId of the broker
+ pub publisher: Option,
+}
+
+/// Request subscription to a `Topic` of an already opened or pinned Repo
+#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
+pub enum TopicSub {
+ V0(TopicSubV0),
+}
+
+/// Request unsubscription from a `Topic` of an already opened or pinned Repo
+#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
+pub struct TopicUnsubV0 {
+ /// Topic to unsubscribe
+ pub topic: PubKey,
+
+ /// Hash of the repo that was previously opened or pinned
+ pub repo_hash: RepoHash,
+}
+
+/// Request unsubscription from a `Topic` of an already opened or pinned Repo
+#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
+pub enum TopicUnsub {
+ V0(TopicUnsubV0),
}
/// Request a Block by ID
@@ -2256,16 +2716,44 @@ impl BlockGet {
}
}
-/// Request to store an object
+/// Request to store one or more blocks
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct BlocksPutV0 {
+ /// Blocks to store
+ pub blocks: Vec,
+}
+
+/// Request to store one or more blocks
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum BlocksPut {
+ V0(BlocksPutV0),
+}
+
+impl BlocksPut {
+ pub fn blocks(&self) -> &Vec {
+ match self {
+ BlocksPut::V0(o) => &o.blocks,
+ }
+ }
+}
+
+/// Request to know if some blocks are present locally
#[derive(Clone, Debug, Serialize, Deserialize)]
-pub enum BlockPut {
- V0(Block),
+pub struct BlocksExistV0 {
+ /// Ids of Blocks to check
+ pub blocks: Vec,
}
-impl BlockPut {
- pub fn block(&self) -> &Block {
+/// Request to store one or more blocks
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum BlocksExist {
+ V0(BlocksExistV0),
+}
+
+impl BlocksExist {
+ pub fn blocks(&self) -> &Vec {
match self {
- BlockPut::V0(o) => &o,
+ BlocksExist::V0(o) => &o.blocks,
}
}
}
@@ -2275,9 +2763,6 @@ impl BlockPut {
/// Brokers maintain an LRU cache of objects,
/// where old, unused objects might get deleted to free up space for new ones.
/// Pinned objects are retained, regardless of last access.
-/// Note that expiry is still observed in case of pinned objects.
-/// To make an object survive its expiry,
-/// it needs to be copied with a different expiry time.
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct ObjectPinV0 {
pub id: ObjectId,
@@ -2317,36 +2802,8 @@ impl ObjectUnpin {
}
}
-/// Request to copy an object with a different expiry time
-#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
-pub struct ObjectCopyV0 {
- /// Object ID to copy
- pub id: ObjectId,
-
- /// New expiry time
- pub expiry: Option,
-}
-
-/// Request to copy an object with a different expiry time
-#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
-pub enum ObjectCopy {
- V0(ObjectCopyV0),
-}
-
-impl ObjectCopy {
- pub fn id(&self) -> ObjectId {
- match self {
- ObjectCopy::V0(o) => o.id,
- }
- }
- pub fn expiry(&self) -> Option {
- match self {
- ObjectCopy::V0(o) => o.expiry,
- }
- }
-}
-
/// Request to delete an object
+/// only effective if the refcount for this object is zero (basically it removes it from LRU)
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct ObjectDelV0 {
pub id: ObjectId,
@@ -2366,81 +2823,30 @@ impl ObjectDel {
}
}
-/// Request subscription to a `Topic`
-#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
-pub struct TopicSubV0 {
- /// Topic to subscribe
- pub topic: PubKey,
-
- /// Publisher need to provide a signed `TopicAdvert` for the PeerId of the broker
- pub advert: Option,
-}
-
-/// Request subscription to a `Topic`
-#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
-pub enum TopicSub {
- V0(TopicSubV0),
-}
-
-/// Request unsubscription from a `Topic`
-#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
-pub struct TopicUnsubV0 {
- /// Topic to unsubscribe
- pub topic: PubKey,
-}
-
-/// Request unsubscription from a `Topic`
-#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
-pub enum TopicUnsub {
- V0(TopicUnsubV0),
-}
-
-/// Connect to an already subscribed `Topic`, and start receiving its `Event`s
-#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
-pub struct TopicConnectV0 {
- /// Topic to connect
- pub topic: PubKey,
-}
+/// Content of `ClientRequestV0`
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum ClientRequestContentV0 {
+ OpenRepo(OpenRepoV0),
+ PinRepo(PinRepoV0),
+ UnpinRepo(UnpinRepoV0),
+ RepoPinStatusReq(RepoPinStatusReqV0),
-/// Connect to an already subscribed `Topic`, and start receiving its `Event`s
-#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
-pub enum TopicConnect {
- V0(TopicConnectV0),
-}
+ // once repo is opened or pinned:
+ TopicSub(TopicSubV0),
+ TopicUnsub(TopicUnsubV0),
-/// Disconnect from a Topic, and stop receiving its `Event`s
-#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
-pub struct TopicDisconnectV0 {
- /// Topic to disconnect
- pub topic: PubKey,
-}
+ BlocksExist(BlocksExistV0),
+ BlockGet(BlockGetV0),
+ TopicSyncReq(TopicSyncReqV0),
-/// Disconnect from a Topic, and stop receiving its `Event`s
-#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
-pub enum TopicDisconnect {
- V0(TopicDisconnectV0),
-}
+ // For Pinned Repos only :
+ ObjectPin(ObjectPinV0),
+ ObjectUnpin(ObjectUnpinV0),
+ ObjectDel(ObjectDelV0),
-/// Content of `ClientRequestV0`
-#[derive(Clone, Debug, Serialize, Deserialize)]
-pub enum ClientRequestContentV0 {
- OverlayConnect(OverlayConnect), // FIXME remove
- OverlayStatusReq(OverlayStatusReq),
- OverlayJoin(OverlayJoin),
- OverlayLeave(OverlayLeave),
- TopicSub(TopicSub),
- TopicUnsub(TopicUnsub),
- TopicConnect(TopicConnect),
- TopicDisconnect(TopicDisconnect),
- Event(Event),
- BlockGet(BlockGet),
- BlockPut(BlockPut),
- ObjectPin(ObjectPin),
- ObjectUnpin(ObjectUnpin),
- ObjectCopy(ObjectCopy),
- ObjectDel(ObjectDel),
- BranchHeadsReq(BranchHeadsReq),
- BranchSyncReq(BranchSyncReq),
+ // For InnerOverlay's only :
+ BlocksPut(BlocksPutV0),
+ PublishEvent(EventV0),
}
/// Broker overlay request
#[derive(Clone, Debug, Serialize, Deserialize)]
@@ -2478,13 +2884,42 @@ impl ClientRequest {
}
}
+/// Response which blocks have been found locally. V0
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct BlocksFoundV0 {
+ /// Ids of Blocks that were found locally
+ pub found: Vec,
+
+ /// Ids of Blocks that were missing locally
+ pub missing: Vec,
+}
+
+/// Response which blocks have been found locally.
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum BlocksFound {
+ V0(BlocksFoundV0),
+}
+
+impl BlocksFound {
+ pub fn found(&self) -> &Vec {
+ match self {
+ BlocksFound::V0(o) => &o.found,
+ }
+ }
+ pub fn missing(&self) -> &Vec {
+ match self {
+ BlocksFound::V0(o) => &o.missing,
+ }
+ }
+}
+
/// Content of `ClientResponseV0`
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ClientResponseContentV0 {
EmptyResponse,
Block(Block),
- ObjectId(ObjectId),
- OverlayStatusResp(OverlayStatusResp),
+ BlocksFound(BlocksFoundV0),
+ RepoPinStatus(RepoPinStatusV0),
}
/// Response to a `ClientRequest`
@@ -2532,14 +2967,6 @@ impl ClientResponse {
},
}
}
- pub fn object_id(&self) -> ObjectId {
- match self {
- ClientResponse::V0(o) => match &o.content {
- ClientResponseContentV0::ObjectId(id) => id.clone(),
- _ => panic!("this not an objectId response"),
- },
- }
- }
}
/// Content of `ClientMessageV0`
@@ -2547,7 +2974,7 @@ impl ClientResponse {
pub enum ClientMessageContentV0 {
ClientRequest(ClientRequest),
ClientResponse(ClientResponse),
- Event(Event),
+ ForwardedEvent(Event),
}
/// Broker message for an overlay
#[derive(Clone, Debug, Serialize, Deserialize)]
@@ -2603,7 +3030,7 @@ impl ClientMessage {
ClientMessage::V0(o) => match &o.content {
ClientMessageContentV0::ClientResponse(r) => r.id(),
ClientMessageContentV0::ClientRequest(r) => r.id(),
- ClientMessageContentV0::Event(_) => {
+ ClientMessageContentV0::ForwardedEvent(_) => {
panic!("it is an event")
}
},
@@ -2614,7 +3041,7 @@ impl ClientMessage {
ClientMessage::V0(o) => match &mut o.content {
ClientMessageContentV0::ClientResponse(ref mut r) => r.set_id(id),
ClientMessageContentV0::ClientRequest(ref mut r) => r.set_id(id),
- ClientMessageContentV0::Event(_) => {
+ ClientMessageContentV0::ForwardedEvent(_) => {
panic!("it is an event")
}
},
@@ -2627,7 +3054,7 @@ impl ClientMessage {
ClientMessageContentV0::ClientRequest(r) => {
panic!("it is not a response");
}
- ClientMessageContentV0::Event(_) => {
+ ClientMessageContentV0::ForwardedEvent(_) => {
panic!("it is not a response");
}
},
@@ -2640,20 +3067,7 @@ impl ClientMessage {
ClientMessageContentV0::ClientRequest(r) => {
panic!("it is not a response");
}
- ClientMessageContentV0::Event(_) => {
- panic!("it is not a response");
- }
- },
- }
- }
- pub fn object_id<'a>(&self) -> ObjectId {
- match self {
- ClientMessage::V0(o) => match &o.content {
- ClientMessageContentV0::ClientResponse(r) => r.object_id(),
- ClientMessageContentV0::ClientRequest(r) => {
- panic!("it is not a response");
- }
- ClientMessageContentV0::Event(_) => {
+ ClientMessageContentV0::ForwardedEvent(_) => {
panic!("it is not a response");
}
},
@@ -2693,37 +3107,34 @@ pub enum ExtObjectGet {
V0(ExtObjectGetV0),
}
-/// Branch heads request
-pub type ExtBranchHeadsReq = BranchHeadsReq;
-
-/// Branch synchronization request
-pub type ExtBranchSyncReq = BranchSyncReq;
+/// Topic synchronization request
+pub type ExtTopicSyncReq = TopicSyncReq;
/// Content of ExtRequestV0
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ExtRequestContentV0 {
ExtObjectGet(ExtObjectGet),
- ExtBranchHeadsReq(ExtBranchHeadsReq),
- ExtBranchSyncReq(ExtBranchSyncReq),
-}
-
-/// External Request Payload V0
-#[derive(Clone, Debug, Serialize, Deserialize)]
-pub struct ExtRequestPayload {
- content: ExtRequestContentV0,
- // ...
+ ExtTopicSyncReq(ExtTopicSyncReq),
+ // TODO inbox requests
+ // TODO subreq ?
}
/// External request with its request ID
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ExtRequestV0 {
+ /// outer overlayId
+ pub overlay: Digest,
+
/// Request ID
pub id: i64,
- /// Request payload
- pub payload: ExtRequestPayload,
+ /// Request content
+ pub content: ExtRequestContentV0,
}
+/// External request are made by clients directly to a core broker of their choice.
+/// They differ from OuterOverlayRequests in the sense that the broker where the client is attached, is not involved in the request.
+/// It is a direct connection that is established between the client and the core broker that will give the response.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ExtRequest {
V0(ExtRequestV0),
@@ -2748,8 +3159,8 @@ impl ExtRequest {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ExtResponseContentV0 {
Block(Block),
- EventResp(EventResp),
- Event(Event),
+ // TODO inbox related replies
+ // TODO event ?
}
/// Response to an ExtRequest
@@ -3059,15 +3470,17 @@ impl From for ProtocolMessage {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RepoLinkV0 {
/// Repository public key ID
- pub id: PubKey,
+ pub id: Identity,
- /// Repository secret
- pub secret: SymKey,
+ pub overlay: OverlayLink,
+
+ /// Repository secret. Only set for editors
+ pub repo_secret: Option,
/// current root branch definition commit
pub root_branch_def_ref: ObjectRef,
- /// Peers to connect to
+ /// Peer brokers to connect to
pub peers: Vec,
}
@@ -3078,14 +3491,14 @@ pub enum RepoLink {
}
impl RepoLink {
- pub fn id(&self) -> &PubKey {
+ pub fn id(&self) -> &Identity {
match self {
RepoLink::V0(o) => &o.id,
}
}
- pub fn secret(&self) -> &SymKey {
+ pub fn secret(&self) -> &Option {
match self {
- RepoLink::V0(o) => &o.secret,
+ RepoLink::V0(o) => &o.repo_secret,
}
}
pub fn peers(&self) -> &Vec {
diff --git a/p2p-repo/src/block.rs b/p2p-repo/src/block.rs
index e1f149d..b206b8b 100644
--- a/p2p-repo/src/block.rs
+++ b/p2p-repo/src/block.rs
@@ -16,23 +16,38 @@ use crate::types::*;
impl BlockV0 {
pub fn new(
children: Vec,
- deps: ObjectDeps,
- expiry: Option,
+ header_ref: Option,
content: Vec,
key: Option,
) -> BlockV0 {
+ let (commit_header_id, commit_header_key) = header_ref.map_or((None, None), |obj_ref| {
+ (Some(obj_ref.id), Some(obj_ref.key))
+ });
+ let bc = BlockContentV0 {
+ children,
+ commit_header_id,
+ encrypted_content: content,
+ };
let mut b = BlockV0 {
id: None,
key,
- children,
- deps,
- expiry,
- content,
+ content: BlockContent::V0(bc),
+ commit_header_key,
};
- let block = Block::V0(b.clone());
- b.id = Some(block.get_id());
+ b.id = Some(b.compute_id());
b
}
+
+ /// Compute the ID
+ pub fn compute_id(&self) -> BlockId {
+ let ser = serde_bare::to_vec(&self.content).unwrap();
+ let hash = blake3::hash(ser.as_slice());
+ Digest::Blake3Digest32(hash.as_bytes().clone())
+ }
+
+ pub fn children(&self) -> &Vec {
+ self.content.children()
+ }
}
impl From for String {
@@ -42,68 +57,92 @@ impl From for String {
}
}
+impl BlockContent {
+ /// Get the encrypted content
+ pub fn encrypted_content(&self) -> &Vec {
+ match self {
+ BlockContent::V0(bc) => &bc.encrypted_content,
+ }
+ }
+
+ /// Get the header id
+ pub fn header_id(&self) -> &Option {
+ match self {
+ BlockContent::V0(bc) => &bc.commit_header_id,
+ }
+ }
+
+ /// Get the children
+ pub fn children(&self) -> &Vec {
+ match self {
+ BlockContent::V0(b) => &b.children,
+ }
+ }
+}
+
impl Block {
pub fn new(
children: Vec,
- deps: ObjectDeps,
- expiry: Option,
+ header_ref: Option,
content: Vec,
key: Option,
) -> Block {
- Block::V0(BlockV0::new(children, deps, expiry, content, key))
+ Block::V0(BlockV0::new(children, header_ref, content, key))
}
/// Compute the ID
- pub fn get_id(&self) -> BlockId {
- let ser = serde_bare::to_vec(self).unwrap();
- let hash = blake3::hash(ser.as_slice());
- Digest::Blake3Digest32(hash.as_bytes().clone())
+ pub fn compute_id(&self) -> BlockId {
+ match self {
+ Block::V0(v0) => v0.compute_id(),
+ }
}
- /// Get the already computed ID
- pub fn id(&self) -> BlockId {
- match self {
+ /// Get the already computed ID or computes it, saves it, and returns it
+ pub fn get_and_save_id(&mut self) -> BlockId {
+ match &self {
Block::V0(b) => match b.id {
Some(id) => id,
- None => self.get_id(),
+ None => {
+ let id = self.compute_id();
+ let Block::V0(c) = self;
+ c.id = Some(id);
+ id
+ }
},
}
}
- /// Get the content
- pub fn content(&self) -> &Vec {
- match self {
- Block::V0(b) => &b.content,
- }
- }
-
- /// Get the children
- pub fn children(&self) -> &Vec {
+ /// Get the already computed ID or computes it
+ pub fn id(&self) -> BlockId {
match self {
- Block::V0(b) => &b.children,
+ Block::V0(b) => match b.id {
+ Some(id) => id,
+ None => self.compute_id(),
+ },
}
}
- /// Get the dependencies
- pub fn deps(&self) -> &ObjectDeps {
+ /// Get the encrypted content
+ pub fn encrypted_content(&self) -> &Vec {
match self {
- Block::V0(b) => &b.deps,
+ Block::V0(b) => &b.content.encrypted_content(),
}
}
- /// Get the expiry
- pub fn expiry(&self) -> Option {
+ /// Get the children
+ pub fn children(&self) -> &Vec {
match self {
- Block::V0(b) => b.expiry,
+ Block::V0(b) => &b.content.children(),
}
}
- pub fn set_expiry(&mut self, expiry: Option) {
+ /// Get the header
+ pub fn header_ref(&self) -> Option {
match self {
- Block::V0(b) => {
- b.id = None;
- b.expiry = expiry
- }
+ Block::V0(b) => b.commit_header_key.as_ref().map(|key| ObjectRef {
+ key: key.clone(),
+ id: b.content.header_id().unwrap().clone(),
+ }),
}
}
diff --git a/p2p-repo/src/branch.rs b/p2p-repo/src/branch.rs
index f7bfc54..5b4ae4e 100644
--- a/p2p-repo/src/branch.rs
+++ b/p2p-repo/src/branch.rs
@@ -12,7 +12,7 @@
//! Branch of a Repository
use crate::log::*;
-use std::collections::{HashMap, HashSet};
+use std::collections::HashSet;
use fastbloom_rs::{BloomFilter as Filter, Membership};
@@ -20,55 +20,23 @@ use crate::object::*;
use crate::store::*;
use crate::types::*;
-impl MemberV0 {
- /// New member
- pub fn new(id: PubKey, commit_types: Vec, metadata: Vec) -> MemberV0 {
- MemberV0 {
- id,
- commit_types,
- metadata,
- }
- }
-
- /// Check whether this member has permission for the given commit type
- pub fn has_perm(&self, commit_type: CommitType) -> bool {
- self.commit_types.contains(&commit_type)
- }
-}
-
-impl Member {
- /// New member
- pub fn new(id: PubKey, commit_types: Vec, metadata: Vec) -> Member {
- Member::V0(MemberV0::new(id, commit_types, metadata))
- }
-
- /// Check whether this member has permission for the given commit type
- pub fn has_perm(&self, commit_type: CommitType) -> bool {
- match self {
- Member::V0(m) => m.has_perm(commit_type),
- }
- }
-}
-
impl BranchV0 {
pub fn new(
id: PubKey,
- topic: PubKey,
- secret: SymKey,
- members: Vec,
- quorum: HashMap,
- ack_delay: RelTime,
- tags: Vec,
+ repo: ObjectRef,
+ root_branch_def_id: ObjectId,
+ topic_priv: PrivKey,
metadata: Vec,
) -> BranchV0 {
+ let topic_privkey: Vec = vec![];
+ //TODO: topic_privkey is topic_priv encrypted with the repo_secret, branch_id, topic_id
+ let topic = topic_priv.to_pub();
BranchV0 {
id,
+ repo,
+ root_branch_def_id,
topic,
- secret,
- members,
- quorum,
- ack_delay,
- tags,
+ topic_privkey,
metadata,
}
}
@@ -77,33 +45,20 @@ impl BranchV0 {
impl Branch {
pub fn new(
id: PubKey,
- topic: PubKey,
- secret: SymKey,
- members: Vec,
- quorum: HashMap,
- ack_delay: RelTime,
- tags: Vec,
+ repo: ObjectRef,
+ root_branch_def_id: ObjectId,
+ topic_priv: PrivKey,
metadata: Vec,
) -> Branch {
Branch::V0(BranchV0::new(
- id, topic, secret, members, quorum, ack_delay, tags, metadata,
+ id,
+ repo,
+ root_branch_def_id,
+ topic_priv,
+ metadata,
))
}
- /// Get member by ID
- pub fn get_member(&self, id: &PubKey) -> Option<&MemberV0> {
- match self {
- Branch::V0(b) => {
- for m in b.members.iter() {
- if m.id == *id {
- return Some(m);
- }
- }
- }
- }
- None
- }
-
/// Branch sync request from another peer
///
/// Return ObjectIds to send
@@ -111,7 +66,7 @@ impl Branch {
our_heads: &[ObjectId],
their_heads: &[ObjectId],
their_filter: &BloomFilter,
- store: &impl RepoStore,
+ store: &Box,
) -> Result, ObjectParseError> {
//log_debug!(">> sync_req");
//log_debug!(" our_heads: {:?}", our_heads);
@@ -121,7 +76,7 @@ impl Branch {
/// and collect `ObjectId`s starting from `our_heads` towards `their_heads`
fn load_branch(
cobj: &Object,
- store: &impl RepoStore,
+ store: &Box,
their_heads: &[ObjectId],
visited: &mut HashSet,
missing: &mut HashSet,
@@ -129,9 +84,9 @@ impl Branch {
//log_debug!(">>> load_branch: {}", cobj.id());
let id = cobj.id();
- // root has no deps
+ // root has no acks
let is_root = cobj.is_root();
- //log_debug!(" deps: {:?}", cobj.deps());
+ //log_debug!(" acks: {:?}", cobj.acks());
// check if this commit object is present in their_heads
let mut their_head_found = their_heads.contains(&id);
@@ -140,9 +95,9 @@ impl Branch {
if !is_root && !their_head_found {
visited.insert(id);
for id in cobj.deps() {
- match Object::load(*id, None, store) {
+ match Object::load(id, None, store) {
Ok(o) => {
- if !visited.contains(id) {
+ if !visited.contains(&id) {
if load_branch(&o, store, their_heads, visited, missing)? {
their_head_found = true;
}
@@ -218,49 +173,43 @@ mod test {
use crate::commit::*;
use crate::object::*;
use crate::repo;
+ use crate::repo::Repo;
use crate::store::*;
#[test]
pub fn test_branch() {
fn add_obj(
- content: ObjectContent,
- deps: Vec,
- expiry: Option,
+ content: ObjectContentV0,
+ header: Option,
repo_pubkey: PubKey,
repo_secret: SymKey,
- store: &mut impl RepoStore,
+ store: &Box,
) -> ObjectRef {
let max_object_size = 4000;
- let obj = Object::new(
- content,
- deps,
- expiry,
- max_object_size,
- repo_pubkey,
- repo_secret,
- );
+ let obj = Object::new(content, header, max_object_size, repo_pubkey, repo_secret);
log_debug!(">>> add_obj");
log_debug!(" id: {:?}", obj.id());
- log_debug!(" deps: {:?}", obj.deps());
+ log_debug!(" header: {:?}", obj.header());
obj.save(store).unwrap();
obj.reference().unwrap()
}
fn add_commit(
- branch: ObjectRef,
+ branch: BranchId,
author_privkey: PrivKey,
author_pubkey: PubKey,
- seq: u32,
+ seq: u64,
deps: Vec,
acks: Vec,
body_ref: ObjectRef,
repo_pubkey: PubKey,
repo_secret: SymKey,
- store: &mut impl RepoStore,
+ store: &Box,
) -> ObjectRef {
- let mut obj_deps: Vec = vec![];
- obj_deps.extend(deps.iter().map(|r| r.id));
- obj_deps.extend(acks.iter().map(|r| r.id));
+ let header = CommitHeaderV0::new_with_deps_and_acks(
+ deps.iter().map(|r| r.id).collect(),
+ acks.iter().map(|r| r.id).collect(),
+ );
let obj_ref = ObjectRef {
id: ObjectId::Blake3Digest32([1; 32]),
@@ -268,26 +217,27 @@ mod test {
};
let refs = vec![obj_ref];
let metadata = vec![5u8; 55];
- let expiry = None;
- let commit = Commit::new(
+ let commit = CommitV0::new(
author_privkey,
author_pubkey,
seq,
branch,
+ QuorumType::NoSigning,
deps,
+ vec![],
acks,
+ vec![],
refs,
+ vec![],
metadata,
body_ref,
- expiry,
)
.unwrap();
//log_debug!("commit: {:?}", commit);
add_obj(
- ObjectContent::Commit(commit),
- obj_deps,
- expiry,
+ ObjectContentV0::Commit(commit),
+ header,
repo_pubkey,
repo_secret,
store,
@@ -295,19 +245,16 @@ mod test {
}
fn add_body_branch(
- branch: Branch,
+ branch: BranchV0,
repo_pubkey: PubKey,
repo_secret: SymKey,
- store: &mut impl RepoStore,
+ store: &Box,
) -> ObjectRef {
- let deps = vec![];
- let expiry = None;
- let body = CommitBody::Branch(branch);
+ let body = CommitBodyV0::Branch(branch);
//log_debug!("body: {:?}", body);
add_obj(
- ObjectContent::CommitBody(body),
- deps,
- expiry,
+ ObjectContentV0::CommitBody(body),
+ None,
repo_pubkey,
repo_secret,
store,
@@ -315,45 +262,24 @@ mod test {
}
fn add_body_trans(
- deps: Vec,
+ header: Option,
repo_pubkey: PubKey,
repo_secret: SymKey,
- store: &mut impl RepoStore,
+ store: &Box,
) -> ObjectRef {
- let expiry = None;
let content = [7u8; 777].to_vec();
- let body = CommitBody::Transaction(Transaction::V0(content));
- //log_debug!("body: {:?}", body);
- add_obj(
- ObjectContent::CommitBody(body),
- deps,
- expiry,
- repo_pubkey,
- repo_secret,
- store,
- )
- }
-
- fn add_body_ack(
- deps: Vec,
- repo_pubkey: PubKey,
- repo_secret: SymKey,
- store: &mut impl RepoStore,
- ) -> ObjectRef {
- let expiry = None;
- let body = CommitBody::Ack(Ack::V0());
+ let body = CommitBodyV0::Transaction(content);
//log_debug!("body: {:?}", body);
add_obj(
- ObjectContent::CommitBody(body),
- deps,
- expiry,
+ ObjectContentV0::CommitBody(body),
+ header,
repo_pubkey,
repo_secret,
store,
)
}
- let mut store = HashMapRepoStore::new();
+ let store = Box::new(HashMapRepoStore::new());
let mut rng = OsRng {};
// repo
@@ -369,7 +295,7 @@ mod test {
repo_keypair.public.as_bytes().len(),
repo_keypair.public.as_bytes()
);
- let _repo_privkey = PrivKey::Ed25519PrivKey(repo_keypair.secret.to_bytes());
+ let repo_privkey = PrivKey::Ed25519PrivKey(repo_keypair.secret.to_bytes());
let repo_pubkey = PubKey::Ed25519PubKey(repo_keypair.public.to_bytes());
let repo_secret = SymKey::ChaCha20Key([9; 32]);
@@ -385,23 +311,26 @@ mod test {
let member_pubkey = PubKey::Ed25519PubKey(member_keypair.public.to_bytes());
let metadata = [66u8; 64].to_vec();
- let commit_types = vec![CommitType::Ack, CommitType::Transaction];
- let secret = SymKey::ChaCha20Key([0; 32]);
-
- let member = MemberV0::new(member_pubkey, commit_types, metadata.clone());
- let members = vec![member];
- let mut quorum = HashMap::new();
- quorum.insert(CommitType::Transaction, 3);
- let ack_delay = RelTime::Minutes(3);
- let tags = [99u8; 32].to_vec();
- let branch = Branch::new(
- branch_pubkey,
+
+ let repo = Repo::new_with_member(
+ &repo_pubkey,
+ member_pubkey,
+ &[Permission::Transaction],
+ store,
+ );
+
+ let repo_ref = ObjectRef {
+ id: ObjectId::Blake3Digest32([1; 32]),
+ key: SymKey::ChaCha20Key([2; 32]),
+ };
+
+ let root_branch_def_id = ObjectId::Blake3Digest32([1; 32]);
+
+ let branch = BranchV0::new(
branch_pubkey,
- secret,
- members,
- quorum,
- ack_delay,
- tags,
+ repo_ref,
+ root_branch_def_id,
+ repo_privkey,
metadata,
);
//log_debug!("branch: {:?}", branch);
@@ -427,16 +356,16 @@ mod test {
branch.clone(),
repo_pubkey.clone(),
repo_secret.clone(),
- &mut store,
+ repo.get_store(),
);
- let ack_body = add_body_ack(vec![], repo_pubkey, repo_secret.clone(), &mut store);
- let trans_body = add_body_trans(vec![], repo_pubkey, repo_secret.clone(), &mut store);
+
+ let trans_body = add_body_trans(None, repo_pubkey, repo_secret.clone(), repo.get_store());
// create & add commits to store
log_debug!(">> br");
let br = add_commit(
- branch_body.clone(),
+ branch_pubkey,
member_privkey.clone(),
member_pubkey,
0,
@@ -445,12 +374,12 @@ mod test {
branch_body.clone(),
repo_pubkey,
repo_secret.clone(),
- &mut store,
+ repo.get_store(),
);
log_debug!(">> t1");
let t1 = add_commit(
- branch_body.clone(),
+ branch_pubkey,
member_privkey.clone(),
member_pubkey,
1,
@@ -459,12 +388,12 @@ mod test {
trans_body.clone(),
repo_pubkey,
repo_secret.clone(),
- &mut store,
+ repo.get_store(),
);
log_debug!(">> t2");
let t2 = add_commit(
- branch_body.clone(),
+ branch_pubkey,
member_privkey.clone(),
member_pubkey,
2,
@@ -473,26 +402,26 @@ mod test {
trans_body.clone(),
repo_pubkey,
repo_secret.clone(),
- &mut store,
+ repo.get_store(),
);
- log_debug!(">> a3");
- let a3 = add_commit(
- branch_body.clone(),
- member_privkey.clone(),
- member_pubkey,
- 3,
- vec![t1.clone()],
- vec![],
- ack_body.clone(),
- repo_pubkey,
- repo_secret.clone(),
- &mut store,
- );
+ // log_debug!(">> a3");
+ // let a3 = add_commit(
+ // branch_pubkey,
+ // member_privkey.clone(),
+ // member_pubkey,
+ // 3,
+ // vec![t1.clone()],
+ // vec![],
+ // ack_body.clone(),
+ // repo_pubkey,
+ // repo_secret.clone(),
+ // &mut store,
+ // );
log_debug!(">> t4");
let t4 = add_commit(
- branch_body.clone(),
+ branch_pubkey,
member_privkey.clone(),
member_pubkey,
4,
@@ -501,12 +430,12 @@ mod test {
trans_body.clone(),
repo_pubkey,
repo_secret.clone(),
- &mut store,
+ repo.get_store(),
);
log_debug!(">> t5");
let t5 = add_commit(
- branch_body.clone(),
+ branch_pubkey,
member_privkey.clone(),
member_pubkey,
5,
@@ -515,42 +444,42 @@ mod test {
trans_body.clone(),
repo_pubkey,
repo_secret.clone(),
- &mut store,
+ repo.get_store(),
);
log_debug!(">> a6");
let a6 = add_commit(
- branch_body.clone(),
+ branch_pubkey,
member_privkey.clone(),
member_pubkey,
6,
vec![t4.clone()],
vec![],
- ack_body.clone(),
+ trans_body.clone(),
repo_pubkey,
repo_secret.clone(),
- &mut store,
+ repo.get_store(),
);
log_debug!(">> a7");
let a7 = add_commit(
- branch_body.clone(),
+ branch_pubkey,
member_privkey.clone(),
member_pubkey,
7,
vec![t4.clone()],
vec![],
- ack_body.clone(),
+ trans_body.clone(),
repo_pubkey,
repo_secret.clone(),
- &mut store,
+ repo.get_store(),
);
- let c7 = Commit::load(a7.clone(), &store).unwrap();
- c7.verify(&branch, &store).unwrap();
+ let c7 = Commit::load(a7.clone(), repo.get_store()).unwrap();
+ c7.verify(&repo, repo.get_store()).unwrap();
let mut filter = Filter::new(FilterBuilder::new(10, 0.01));
- for commit_ref in [br, t1, t2, a3.clone(), t5.clone(), a6.clone()] {
+ for commit_ref in [br, t1, t2, t5.clone(), a6.clone()] {
match commit_ref.id {
ObjectId::Blake3Digest32(d) => filter.add(&d),
}
@@ -568,10 +497,10 @@ mod test {
log_debug!(" their_commits: [br, t1, t2, a3, t5, a6]");
let ids = Branch::sync_req(
- &[a3.id, t5.id, a6.id, a7.id],
- &[a3.id, t5.id],
+ &[t5.id, a6.id, a7.id],
+ &[t5.id],
&their_commits,
- &store,
+ repo.get_store(),
)
.unwrap();
diff --git a/p2p-repo/src/commit.rs b/p2p-repo/src/commit.rs
index 7da31ba..25e9800 100644
--- a/p2p-repo/src/commit.rs
+++ b/p2p-repo/src/commit.rs
@@ -13,19 +13,22 @@
use ed25519_dalek::*;
-use std::collections::HashSet;
-use std::iter::FromIterator;
-
+use crate::errors::NgError;
use crate::log::*;
use crate::object::*;
+use crate::repo::Repo;
use crate::store::*;
use crate::types::*;
+use std::collections::HashSet;
+use std::iter::FromIterator;
#[derive(Debug)]
pub enum CommitLoadError {
MissingBlocks(Vec),
ObjectParseError,
DeserializeError,
+ CannotBeAtRootOfBranch,
+ MustBeAtRootOfBranch,
}
#[derive(Debug)]
@@ -35,47 +38,33 @@ pub enum CommitVerifyError {
BodyLoadError(CommitLoadError),
DepLoadError(CommitLoadError),
}
-impl CommitBody {
- /// Get CommitType corresponding to CommitBody
- pub fn to_type(&self) -> CommitType {
- match self {
- CommitBody::Ack(_) => CommitType::Ack,
- CommitBody::AddBranch(_) => CommitType::AddBranch,
- CommitBody::AddMembers(_) => CommitType::AddMembers,
- CommitBody::Branch(_) => CommitType::Branch,
- CommitBody::EndOfBranch(_) => CommitType::EndOfBranch,
- CommitBody::RemoveBranch(_) => CommitType::RemoveBranch,
- CommitBody::Repository(_) => CommitType::Repository,
- CommitBody::Snapshot(_) => CommitType::Snapshot,
- CommitBody::Transaction(_) => CommitType::Transaction,
- }
- }
-}
impl CommitV0 {
/// New commit
pub fn new(
author_privkey: PrivKey,
author_pubkey: PubKey,
- seq: u32,
- branch: ObjectRef,
+ seq: u64,
+ branch: BranchId,
+ quorum: QuorumType,
deps: Vec,
+ ndeps: Vec,
acks: Vec,
+ nacks: Vec,
refs: Vec,
+ nrefs: Vec,
metadata: Vec,
body: ObjectRef,
- expiry: Option,
) -> Result {
+ let headers = CommitHeaderV0::new_with(deps, ndeps, acks, nacks, refs, nrefs);
let content = CommitContentV0 {
author: author_pubkey,
seq,
branch,
- deps,
- acks,
- refs,
+ header_keys: headers.1,
+ quorum,
metadata,
body,
- expiry,
};
let content_ser = serde_bare::to_vec(&content).unwrap();
@@ -96,6 +85,7 @@ impl CommitV0 {
sig,
id: None,
key: None,
+ header: headers.0,
})
}
}
@@ -105,32 +95,41 @@ impl Commit {
pub fn new(
author_privkey: PrivKey,
author_pubkey: PubKey,
- seq: u32,
- branch: ObjectRef,
+ seq: u64,
+ branch: BranchId,
+ quorum: QuorumType,
deps: Vec,
+ ndeps: Vec,
acks: Vec,
+ nacks: Vec,
refs: Vec,
+ nrefs: Vec,
metadata: Vec,
body: ObjectRef,
- expiry: Option,
) -> Result {
CommitV0::new(
author_privkey,
author_pubkey,
seq,
branch,
+ quorum,
deps,
+ ndeps,
acks,
+ nacks,
refs,
+ nrefs,
metadata,
body,
- expiry,
)
.map(|c| Commit::V0(c))
}
/// Load commit from store
- pub fn load(commit_ref: ObjectRef, store: &impl RepoStore) -> Result {
+ pub fn load(
+ commit_ref: ObjectRef,
+ store: &Box,
+ ) -> Result {
let (id, key) = (commit_ref.id, commit_ref.key);
match Object::load(id, Some(key.clone()), store) {
Ok(obj) => {
@@ -138,12 +137,15 @@ impl Commit {
.content()
.map_err(|_e| CommitLoadError::ObjectParseError)?;
let mut commit = match content {
- ObjectContent::Commit(c) => c,
+ ObjectContent::V0(ObjectContentV0::Commit(c)) => c,
_ => return Err(CommitLoadError::DeserializeError),
};
- commit.set_id(id);
- commit.set_key(key.clone());
- Ok(commit)
+ commit.id = Some(id);
+ commit.key = Some(key.clone());
+ if let Some(CommitHeader::V0(header_v0)) = obj.header() {
+ commit.header = Some(header_v0.clone());
+ }
+ Ok(Commit::V0(commit))
}
Err(ObjectParseError::MissingBlocks(missing)) => {
Err(CommitLoadError::MissingBlocks(missing))
@@ -153,8 +155,12 @@ impl Commit {
}
/// Load commit body from store
- pub fn load_body(&self, store: &impl RepoStore) -> Result {
- let content = self.content();
+ pub fn load_body(
+ &self,
+ store: &Box,
+ ) -> Result {
+ // TODO store body in CommitV0 (with #[serde(skip)]) as a cache for subsequent calls to load_body
+ let content = self.content_v0();
let (id, key) = (content.body.id, content.body.key.clone());
let obj = Object::load(id.clone(), Some(key.clone()), store).map_err(|e| match e {
ObjectParseError::MissingBlocks(missing) => CommitLoadError::MissingBlocks(missing),
@@ -164,7 +170,7 @@ impl Commit {
.content()
.map_err(|_e| CommitLoadError::ObjectParseError)?;
match content {
- ObjectContent::CommitBody(body) => Ok(body),
+ ObjectContent::V0(ObjectContentV0::CommitBody(body)) => Ok(CommitBody::V0(body)),
_ => Err(CommitLoadError::DeserializeError),
}
}
@@ -204,36 +210,92 @@ impl Commit {
}
}
- /// Get commit content
- pub fn content(&self) -> &CommitContentV0 {
+ /// Get commit content V0
+ pub fn content_v0(&self) -> &CommitContentV0 {
match self {
Commit::V0(c) => &c.content,
}
}
+ /// This commit is the first one in the branch (doesn't have any ACKs nor Nacks)
+ pub fn is_root_commit_of_branch(&self) -> bool {
+ match self {
+ Commit::V0(c) => match &c.content.header_keys {
+ Some(hk) => hk.acks.is_empty() && hk.nacks.is_empty(),
+ None => true,
+ },
+ _ => unimplemented!(),
+ }
+ }
+
/// Get acks
pub fn acks(&self) -> Vec {
+ let mut res: Vec = vec![];
match self {
- Commit::V0(c) => c.content.acks.clone(),
- }
+ Commit::V0(c) => match &c.header {
+ Some(header_v0) => match &c.content.header_keys {
+ Some(hk) => {
+ for ack in header_v0.acks.iter().zip(hk.acks.iter()) {
+ res.push(ack.into());
+ }
+ }
+ None => {}
+ },
+ None => {}
+ },
+ _ => {}
+ };
+ res
}
/// Get deps
pub fn deps(&self) -> Vec {
+ let mut res: Vec = vec![];
match self {
- Commit::V0(c) => c.content.deps.clone(),
- }
+ Commit::V0(c) => match &c.header {
+ Some(header_v0) => match &c.content.header_keys {
+ Some(hk) => {
+ for dep in header_v0.deps.iter().zip(hk.deps.iter()) {
+ res.push(dep.into());
+ }
+ }
+ None => {}
+ },
+ None => {}
+ },
+ _ => {}
+ };
+ res
}
- /// Get all direct commit dependencies of the commit (`deps`, `acks`)
- pub fn deps_acks(&self) -> Vec {
+ /// Get all commits that are in the direct causal past of the commit (`deps`, `acks`, `nacks`, `ndeps`)
+ pub fn direct_causal_past(&self) -> Vec {
+ let mut res: Vec = vec![];
match self {
- Commit::V0(c) => [c.content.acks.clone(), c.content.deps.clone()].concat(),
- }
+ Commit::V0(c) => match (&c.header, &c.content.header_keys) {
+ (Some(header_v0), Some(hk)) => {
+ for ack in header_v0.acks.iter().zip(hk.acks.iter()) {
+ res.push(ack.into());
+ }
+ for nack in header_v0.nacks.iter().zip(hk.nacks.iter()) {
+ res.push(nack.into());
+ }
+ for dep in header_v0.deps.iter().zip(hk.deps.iter()) {
+ res.push(dep.into());
+ }
+ for ndep in header_v0.ndeps.iter().zip(hk.ndeps.iter()) {
+ res.push(ndep.into());
+ }
+ }
+ _ => {}
+ },
+ _ => {}
+ };
+ res
}
/// Get seq
- pub fn seq(&self) -> u32 {
+ pub fn seq(&self) -> u64 {
match self {
Commit::V0(c) => c.content.seq,
}
@@ -258,32 +320,30 @@ impl Commit {
}
/// Verify commit permissions
- pub fn verify_perm(&self, body: &CommitBody, branch: &Branch) -> Result<(), CommitVerifyError> {
- let content = self.content();
- match branch.get_member(&content.author) {
- Some(m) => {
- if m.has_perm(body.to_type()) {
- return Ok(());
- }
- }
- None => (),
- }
- Err(CommitVerifyError::PermissionDenied)
+ pub fn verify_perm(&self, repo: &Repo) -> Result<(), CommitVerifyError> {
+ repo.verify_permission(self)
+ .map_err(|_| CommitVerifyError::PermissionDenied)
}
- /// Verify if the commit's `body` and dependencies (`deps` & `acks`) are available in the `store`
- pub fn verify_deps(&self, store: &impl RepoStore) -> Result, CommitLoadError> {
- //log_debug!(">> verify_deps: #{}", self.seq());
+ /// Verify if the commit's `body`, `header` and direct_causal_past, and recursively all their refs are available in the `store`
+ pub fn verify_full_object_refs_of_branch_at_commit(
+ &self,
+ store: &Box,
+ ) -> Result, CommitLoadError> {
+ //log_debug!(">> verify_full_object_refs_of_branch_at_commit: #{}", self.seq());
+
/// Load `Commit`s of a `Branch` from the `RepoStore` starting from the given `Commit`,
/// and collect missing `ObjectId`s
- fn load_branch(
+ fn load_direct_object_refs(
commit: &Commit,
- store: &impl RepoStore,
+ store: &Box,
visited: &mut HashSet,
missing: &mut HashSet,
) -> Result<(), CommitLoadError> {
//log_debug!(">>> load_branch: #{}", commit.seq());
- // the commit verify_deps() was called on may not have an ID set,
+
+ // FIXME: what about this comment? seems like a Commit always has an id
+ // the self of verify_full_object_refs_of_branch_at_commit() may not have an ID set,
// but the commits loaded from store should have it
match commit.id() {
Some(id) => {
@@ -292,40 +352,53 @@ impl Commit {
}
visited.insert(id);
}
- None => (),
+ None => panic!("Commit without an ID"),
}
- // load body & check if it's the Branch commit at the root
- let is_root = match commit.load_body(store) {
- Ok(body) => body.to_type() == CommitType::Branch,
+ // load body & check if it's the Branch root commit
+ match commit.load_body(store) {
+ Ok(body) => {
+ if commit.is_root_commit_of_branch() {
+ if body.must_be_root_commit_in_branch() {
+ Ok(())
+ } else {
+ Err(CommitLoadError::CannotBeAtRootOfBranch)
+ }
+ } else {
+ if body.must_be_root_commit_in_branch() {
+ Err(CommitLoadError::MustBeAtRootOfBranch)
+ } else {
+ Ok(())
+ }
+ }
+ }
Err(CommitLoadError::MissingBlocks(m)) => {
+ // The commit body is missing.
missing.extend(m);
- false
+ Ok(())
}
- Err(e) => return Err(e),
- };
- log_debug!("!!! is_root: {}", is_root);
-
- // load deps
- if !is_root {
- for dep in commit.deps_acks() {
- match Commit::load(dep, store) {
- Ok(c) => {
- load_branch(&c, store, visited, missing)?;
- }
- Err(CommitLoadError::MissingBlocks(m)) => {
- missing.extend(m);
- }
- Err(e) => return Err(e),
+ Err(e) => Err(e),
+ }?;
+
+ // load direct causal past
+ for blockref in commit.direct_causal_past() {
+ match Commit::load(blockref, store) {
+ Ok(c) => {
+ load_direct_object_refs(&c, store, visited, missing)?;
+ }
+ Err(CommitLoadError::MissingBlocks(m)) => {
+ missing.extend(m);
}
+ Err(e) => return Err(e),
}
}
+
Ok(())
}
let mut visited = HashSet::new();
let mut missing = HashSet::new();
- load_branch(self, store, &mut visited, &mut missing)?;
+ load_direct_object_refs(self, store, &mut visited, &mut missing)?;
if !missing.is_empty() {
return Err(CommitLoadError::MissingBlocks(Vec::from_iter(missing)));
@@ -333,15 +406,16 @@ impl Commit {
Ok(Vec::from_iter(visited))
}
- /// Verify signature, permissions, and dependencies
- pub fn verify(&self, branch: &Branch, store: &impl RepoStore) -> Result<(), CommitVerifyError> {
+ /// Verify signature, permissions, and full causal past
+ pub fn verify(
+ &self,
+ repo: &Repo,
+ store: &Box,
+ ) -> Result<(), CommitVerifyError> {
self.verify_sig()
.map_err(|_e| CommitVerifyError::InvalidSignature)?;
- let body = self
- .load_body(store)
- .map_err(|e| CommitVerifyError::BodyLoadError(e))?;
- self.verify_perm(&body, branch)?;
- self.verify_deps(store)
+ self.verify_perm(repo)?;
+ self.verify_full_object_refs_of_branch_at_commit(repo.get_store())
.map_err(|e| CommitVerifyError::DepLoadError(e))?;
Ok(())
}
@@ -382,46 +456,40 @@ mod test {
key: SymKey::ChaCha20Key([2; 32]),
};
let obj_refs = vec![obj_ref.clone()];
- let branch = obj_ref.clone();
+ let branch = pub_key;
let deps = obj_refs.clone();
let acks = obj_refs.clone();
let refs = obj_refs.clone();
let metadata = vec![1, 2, 3];
let body_ref = obj_ref.clone();
- let expiry = Some(2342);
let commit = Commit::new(
- priv_key, pub_key, seq, branch, deps, acks, refs, metadata, body_ref, expiry,
+ priv_key,
+ pub_key,
+ seq,
+ branch,
+ QuorumType::NoSigning,
+ deps,
+ vec![],
+ acks,
+ vec![],
+ refs,
+ vec![],
+ metadata,
+ body_ref,
)
.unwrap();
log_debug!("commit: {:?}", commit);
- let store = HashMapRepoStore::new();
- let metadata = [66u8; 64].to_vec();
- let commit_types = vec![CommitType::Ack, CommitType::Transaction];
- let key: [u8; 32] = [0; 32];
- let secret = SymKey::ChaCha20Key(key);
- let member = MemberV0::new(pub_key, commit_types, metadata.clone());
- let members = vec![member];
- let mut quorum = HashMap::new();
- quorum.insert(CommitType::Transaction, 3);
- let ack_delay = RelTime::Minutes(3);
- let tags = [99u8; 32].to_vec();
- let branch = Branch::new(
- pub_key.clone(),
- pub_key.clone(),
- secret,
- members,
- quorum,
- ack_delay,
- tags,
- metadata,
- );
- //log_debug!("branch: {:?}", branch);
- let body = CommitBody::Ack(Ack::V0());
+ let store = Box::new(HashMapRepoStore::new());
+
+ let repo =
+ Repo::new_with_member(&pub_key, pub_key.clone(), &[Permission::Transaction], store);
+
+ //let body = CommitBody::Ack(Ack::V0());
//log_debug!("body: {:?}", body);
- match commit.load_body(&store) {
+ match commit.load_body(repo.get_store()) {
Ok(_b) => panic!("Body should not exist"),
Err(CommitLoadError::MissingBlocks(missing)) => {
assert_eq!(missing.len(), 1);
@@ -429,15 +497,13 @@ mod test {
Err(e) => panic!("Commit verify error: {:?}", e),
}
- let content = commit.content();
+ let content = commit.content_v0();
log_debug!("content: {:?}", content);
commit.verify_sig().expect("Invalid signature");
- commit
- .verify_perm(&body, &branch)
- .expect("Permission denied");
+ commit.verify_perm(&repo).expect("Permission denied");
- match commit.verify_deps(&store) {
+ match commit.verify_full_object_refs_of_branch_at_commit(repo.get_store()) {
Ok(_) => panic!("Commit should not be Ok"),
Err(CommitLoadError::MissingBlocks(missing)) => {
assert_eq!(missing.len(), 1);
@@ -445,7 +511,7 @@ mod test {
Err(e) => panic!("Commit verify error: {:?}", e),
}
- match commit.verify(&branch, &store) {
+ match commit.verify(&repo, repo.get_store()) {
Ok(_) => panic!("Commit should not be Ok"),
Err(CommitVerifyError::BodyLoadError(CommitLoadError::MissingBlocks(missing))) => {
assert_eq!(missing.len(), 1);
diff --git a/p2p-repo/src/errors.rs b/p2p-repo/src/errors.rs
index 3ae58e6..efe97ad 100644
--- a/p2p-repo/src/errors.rs
+++ b/p2p-repo/src/errors.rs
@@ -11,6 +11,7 @@
//! Errors
+use crate::commit::CommitLoadError;
use core::fmt;
use std::error::Error;
@@ -23,6 +24,9 @@ pub enum NgError {
InvalidInvitation,
InvalidCreateAccount,
InvalidFileFormat,
+ InvalidArgument,
+ PermissionDenied,
+ RepoLoadError,
}
impl Error for NgError {}
@@ -44,3 +48,9 @@ impl From for NgError {
NgError::InvalidSignature
}
}
+
+impl From for NgError {
+ fn from(e: CommitLoadError) -> Self {
+ NgError::RepoLoadError
+ }
+}
diff --git a/p2p-repo/src/lib.rs b/p2p-repo/src/lib.rs
index ab39356..2369127 100644
--- a/p2p-repo/src/lib.rs
+++ b/p2p-repo/src/lib.rs
@@ -20,6 +20,8 @@ pub mod branch;
pub mod repo;
+pub mod site;
+
pub mod utils;
pub mod errors;
diff --git a/p2p-repo/src/object.rs b/p2p-repo/src/object.rs
index 6f51c65..73a068b 100644
--- a/p2p-repo/src/object.rs
+++ b/p2p-repo/src/object.rs
@@ -20,8 +20,11 @@ use crate::log::*;
use crate::store::*;
use crate::types::*;
+// TODO: review all those constants. they were done for LMDB but we now use RocksDB.
+// Also, the format of Blocks have changed so all should be checked.
+
/// Size of a serialized empty Block
-const EMPTY_BLOCK_SIZE: usize = 12;
+const EMPTY_BLOCK_SIZE: usize = 12 + 1;
/// Size of a serialized BlockId
const BLOCK_ID_SIZE: usize = 33;
/// Size of serialized SymKey
@@ -34,16 +37,18 @@ const DEPSREF_OVERLOAD: usize = EMPTY_ROOT_SIZE_DEPSREF - EMPTY_BLOCK_SIZE;
const BIG_VARINT_EXTRA: usize = 3;
/// Varint extra bytes when reaching the maximum size of data byte arrays.
const DATA_VARINT_EXTRA: usize = 4;
-/// Max extra space used by the deps list
-const MAX_DEPS_SIZE: usize = 8 * BLOCK_ID_SIZE;
+// Max extra space used by the deps list
+//const MAX_DEPS_SIZE: usize = 8 * BLOCK_ID_SIZE;
+const MAX_HEADER_SIZE: usize = BLOCK_ID_SIZE;
#[derive(Debug)]
+/// An Object in memory. This is not used to serialize data
pub struct Object {
/// Blocks of the Object (nodes of the tree)
blocks: Vec,
- /// Dependencies
- deps: Vec,
+ /// Header
+ header: Option,
}
/// Object parsing errors
@@ -59,8 +64,8 @@ pub enum ObjectParseError {
InvalidChildren,
/// Number of keys does not match number of children of a block
InvalidKeys,
- /// Invalid DepList object content
- InvalidDeps,
+ /// Invalid CommitHeader object content
+ InvalidHeader,
/// Error deserializing content of a block
BlockDeserializeError,
/// Error deserializing content of the object
@@ -89,8 +94,7 @@ impl Object {
content: &[u8],
conv_key: &[u8; blake3::OUT_LEN],
children: Vec,
- deps: ObjectDeps,
- expiry: Option,
+ header_ref: Option,
) -> Block {
let key_hash = blake3::keyed_hash(conv_key, content);
let nonce = [0u8; 12];
@@ -100,45 +104,38 @@ impl Object {
let mut content_enc_slice = &mut content_enc.as_mut_slice();
cipher.apply_keystream(&mut content_enc_slice);
let key = SymKey::ChaCha20Key(key.clone());
- let block = Block::new(children, deps, expiry, content_enc, Some(key));
+ let block = Block::new(children, header_ref, content_enc, Some(key));
//log_debug!(">>> make_block:");
//log_debug!("!! id: {:?}", obj.id());
//log_debug!("!! children: ({}) {:?}", children.len(), children);
block
}
- fn make_deps(
- deps_vec: Vec,
+ fn make_header_v0(
+ header: CommitHeaderV0,
object_size: usize,
repo_pubkey: PubKey,
repo_secret: SymKey,
- ) -> ObjectDeps {
- if deps_vec.len() <= 8 {
- ObjectDeps::ObjectIdList(deps_vec)
- } else {
- let dep_list = DepList::V0(deps_vec);
- let dep_obj = Object::new(
- ObjectContent::DepList(dep_list),
- vec![],
- None,
- object_size,
- repo_pubkey,
- repo_secret,
- );
- let dep_ref = ObjectRef {
- id: dep_obj.id(),
- key: dep_obj.key().unwrap(),
- };
- ObjectDeps::DepListRef(dep_ref)
- }
+ ) -> ObjectRef {
+ let header_obj = Object::new(
+ ObjectContentV0::CommitHeader(header),
+ None,
+ object_size,
+ repo_pubkey,
+ repo_secret,
+ );
+ let header_ref = ObjectRef {
+ id: header_obj.id(),
+ key: header_obj.key().unwrap(),
+ };
+ header_ref
}
/// Build tree from leaves, returns parent nodes
fn make_tree(
leaves: &[Block],
conv_key: &ChaCha20Key,
- root_deps: &ObjectDeps,
- expiry: Option,
+ header_ref: &Option,
arity: usize,
) -> Vec {
let mut parents = vec![];
@@ -147,27 +144,26 @@ impl Object {
while let Some(nodes) = it.next() {
let keys = nodes.iter().map(|block| block.key().unwrap()).collect();
let children = nodes.iter().map(|block| block.id()).collect();
- let content = BlockContentV0::InternalNode(keys);
+ let content = ChunkContentV0::InternalNode(keys);
let content_ser = serde_bare::to_vec(&content).unwrap();
- let child_deps = ObjectDeps::ObjectIdList(vec![]);
- let deps = if parents.is_empty() && it.peek().is_none() {
- root_deps.clone()
+ let child_header = None;
+ let header = if parents.is_empty() && it.peek().is_none() {
+ header_ref
} else {
- child_deps
+ &child_header
};
parents.push(Self::make_block(
content_ser.as_slice(),
conv_key,
children,
- deps,
- expiry,
+ header.clone(),
));
}
//log_debug!("parents += {}", parents.len());
if 1 < parents.len() {
let mut great_parents =
- Self::make_tree(parents.as_slice(), conv_key, root_deps, expiry, arity);
+ Self::make_tree(parents.as_slice(), conv_key, header_ref, arity);
parents.append(&mut great_parents);
}
parents
@@ -180,14 +176,13 @@ impl Object {
///
/// Arguments:
/// * `content`: Object content
- /// * `deps`: Dependencies of the object
+ /// * `header`: CommitHeaderV0 : All references of the object
/// * `block_size`: Desired block size for chunking content, rounded up to nearest valid block size
/// * `repo_pubkey`: Repository public key
/// * `repo_secret`: Repository secret
pub fn new(
- content: ObjectContent,
- deps: Vec,
- expiry: Option,
+ content: ObjectContentV0,
+ header: Option,
block_size: usize,
repo_pubkey: PubKey,
repo_secret: SymKey,
@@ -200,106 +195,66 @@ impl Object {
let mut blocks: Vec = vec![];
let conv_key = Self::convergence_key(repo_pubkey, repo_secret.clone());
- let obj_deps = Self::make_deps(
- deps.clone(),
- valid_block_size,
- repo_pubkey,
- repo_secret.clone(),
- );
+ let header_ref = header
+ .clone()
+ .map(|ch| Self::make_header_v0(ch, valid_block_size, repo_pubkey, repo_secret.clone()));
let content_ser = serde_bare::to_vec(&content).unwrap();
- if EMPTY_BLOCK_SIZE + DATA_VARINT_EXTRA + BLOCK_ID_SIZE * deps.len() + content_ser.len()
+ if EMPTY_BLOCK_SIZE
+ + DATA_VARINT_EXTRA
+ + BLOCK_ID_SIZE * header_ref.as_ref().map_or(0, |_| 1)
+ + content_ser.len()
<= valid_block_size
{
// content fits in root node
- let data_chunk = BlockContentV0::DataChunk(content_ser.clone());
+ let data_chunk = ChunkContentV0::DataChunk(content_ser.clone());
let content_ser = serde_bare::to_vec(&data_chunk).unwrap();
blocks.push(Self::make_block(
content_ser.as_slice(),
&conv_key,
vec![],
- obj_deps,
- expiry,
+ header_ref,
));
} else {
// chunk content and create leaf nodes
for chunk in content_ser.chunks(data_chunk_size) {
- let data_chunk = BlockContentV0::DataChunk(chunk.to_vec());
+ let data_chunk = ChunkContentV0::DataChunk(chunk.to_vec());
let content_ser = serde_bare::to_vec(&data_chunk).unwrap();
blocks.push(Self::make_block(
content_ser.as_slice(),
&conv_key,
vec![],
- ObjectDeps::ObjectIdList(vec![]),
- expiry,
+ None,
));
}
// internal nodes
// arity: max number of ObjectRefs that fit inside an InternalNode Object within the object_size limit
let arity: usize =
- (valid_block_size - EMPTY_BLOCK_SIZE - BIG_VARINT_EXTRA * 2 - MAX_DEPS_SIZE)
+ (valid_block_size - EMPTY_BLOCK_SIZE - BIG_VARINT_EXTRA * 2 - MAX_HEADER_SIZE)
/ (BLOCK_ID_SIZE + BLOCK_KEY_SIZE);
- let mut parents =
- Self::make_tree(blocks.as_slice(), &conv_key, &obj_deps, expiry, arity);
+ let mut parents = Self::make_tree(blocks.as_slice(), &conv_key, &header_ref, arity);
blocks.append(&mut parents);
}
- Object { blocks, deps }
- }
-
- pub fn copy(
- &self,
- expiry: Option,
- repo_pubkey: PubKey,
- repo_secret: SymKey,
- ) -> Result {
- // getting the old object from store
- let leaves: Vec = self.leaves().map_err(|_e| ObjectCopyError::ParseError)?;
-
- let conv_key = Self::convergence_key(repo_pubkey, repo_secret);
- let block_size = leaves.first().unwrap().content().len();
- let valid_block_size = store_valid_value_size(block_size);
-
- let mut blocks: Vec = vec![];
- for block in leaves {
- let mut copy = block.clone();
- copy.set_expiry(expiry);
- blocks.push(copy);
- }
-
- // internal nodes
- // arity: max number of ObjectRefs that fit inside an InternalNode Object within the object_size limit
- let arity: usize =
- (valid_block_size - EMPTY_BLOCK_SIZE - BIG_VARINT_EXTRA * 2 - MAX_DEPS_SIZE)
- / (BLOCK_ID_SIZE + BLOCK_KEY_SIZE);
- let mut parents = Self::make_tree(
- blocks.as_slice(),
- &conv_key,
- self.root().deps(),
- expiry,
- arity,
- );
- blocks.append(&mut parents);
-
- Ok(Object {
+ Object {
blocks,
- deps: self.deps().clone(),
- })
+ header: header.map(|h| CommitHeader::V0(h)),
+ }
}
/// Load an Object from RepoStore
///
- /// Returns Ok(Object) or an Err(Vec) of missing BlockIds
+ /// Returns Ok(Object) or an Err(ObjectParseError::MissingBlocks(Vec)) of missing BlockIds
pub fn load(
id: ObjectId,
key: Option,
- store: &impl RepoStore,
+ store: &Box,
) -> Result {
fn load_tree(
parents: Vec,
- store: &impl RepoStore,
+ store: &Box,
blocks: &mut Vec,
missing: &mut Vec,
) {
@@ -307,13 +262,12 @@ impl Object {
for id in parents {
match store.get(&id) {
Ok(block) => {
- //FIXME: remove the block.clone()
- blocks.insert(0, block.clone());
- match block {
+ match &block {
Block::V0(o) => {
- children.extend(o.children.iter().rev());
+ children.extend(o.children().iter().rev());
}
}
+ blocks.insert(0, block);
}
Err(_) => missing.push(id.clone()),
}
@@ -337,22 +291,24 @@ impl Object {
root.set_key(key);
}
- let deps = match root.deps().clone() {
- ObjectDeps::ObjectIdList(deps_vec) => deps_vec,
- ObjectDeps::DepListRef(deps_ref) => {
- let obj = Object::load(deps_ref.id, Some(deps_ref.key), store)?;
+ let header = match root.header_ref() {
+ Some(header_ref) => {
+ let obj = Object::load(header_ref.id, Some(header_ref.key), store)?;
match obj.content()? {
- ObjectContent::DepList(DepList::V0(deps_vec)) => deps_vec,
- _ => return Err(ObjectParseError::InvalidDeps),
+ ObjectContent::V0(ObjectContentV0::CommitHeader(commit_header)) => {
+ Some(CommitHeader::V0(commit_header))
+ }
+ _ => return Err(ObjectParseError::InvalidHeader),
}
}
+ None => None,
};
- Ok(Object { blocks, deps })
+ Ok(Object { blocks, header })
}
/// Save blocks of the object in the store
- pub fn save(&self, store: &mut impl RepoStore) -> Result<(), StorageError> {
+ pub fn save(&self, store: &Box) -> Result<(), StorageError> {
let mut deduplicated: HashSet = HashSet::new();
for block in &self.blocks {
let id = block.id();
@@ -387,20 +343,29 @@ impl Object {
}
pub fn is_root(&self) -> bool {
- self.deps().len() == 0
- //TODO: add && sdeps().len() == 0 && self.acks().len() == 0 && self.nacks().len() == 0
+ self.header.as_ref().map_or(true, |h| h.is_root())
}
- pub fn root(&self) -> &Block {
- self.blocks.last().unwrap()
+ pub fn deps(&self) -> Vec {
+ match &self.header {
+ Some(h) => h.deps(),
+ None => vec![],
+ }
+ }
+
+ pub fn acks(&self) -> Vec {
+ match &self.header {
+ Some(h) => h.acks(),
+ None => vec![],
+ }
}
- pub fn expiry(&self) -> Option {
- self.blocks.last().unwrap().expiry()
+ pub fn root_block(&self) -> &Block {
+ self.blocks.last().unwrap()
}
- pub fn deps(&self) -> &Vec {
- &self.deps
+ pub fn header(&self) -> &Option {
+ &self.header
}
pub fn blocks(&self) -> &Vec {
@@ -445,7 +410,7 @@ impl Object {
match block {
Block::V0(b) => {
// decrypt content
- let mut content_dec = b.content.clone();
+ let mut content_dec = b.content.encrypted_content().clone();
match key {
SymKey::ChaCha20Key(key) => {
let nonce = [0u8; 12];
@@ -456,7 +421,7 @@ impl Object {
}
// deserialize content
- let content: BlockContentV0;
+ let content: ChunkContentV0;
match serde_bare::from_slice(content_dec.as_slice()) {
Ok(c) => content = c,
Err(e) => {
@@ -464,26 +429,26 @@ impl Object {
return Err(ObjectParseError::BlockDeserializeError);
}
}
-
+ let b_children = b.children();
// parse content
match content {
- BlockContentV0::InternalNode(keys) => {
- if keys.len() != b.children.len() {
+ ChunkContentV0::InternalNode(keys) => {
+ if keys.len() != b_children.len() {
log_debug!(
"Invalid keys length: got {}, expected {}",
keys.len(),
- b.children.len()
+ b_children.len()
);
- log_debug!("!!! children: {:?}", b.children);
+ log_debug!("!!! children: {:?}", b_children);
log_debug!("!!! keys: {:?}", keys);
return Err(ObjectParseError::InvalidKeys);
}
- for (id, key) in b.children.iter().zip(keys.iter()) {
+ for (id, key) in b_children.iter().zip(keys.iter()) {
children.push((id.clone(), key.clone()));
}
}
- BlockContentV0::DataChunk(chunk) => {
+ ChunkContentV0::DataChunk(chunk) => {
if leaves.is_some() {
let mut leaf = block.clone();
leaf.set_key(Some(key.clone()));
@@ -548,9 +513,9 @@ impl Object {
&mut Some(&mut obj_content),
) {
Ok(_) => {
- let content: ObjectContent;
+ let content: ObjectContentV0;
match serde_bare::from_slice(obj_content.as_slice()) {
- Ok(c) => Ok(c),
+ Ok(c) => Ok(ObjectContent::V0(c)),
Err(e) => {
log_debug!("Object deserialize error: {}", e);
Err(ObjectParseError::ObjectDeserializeError)
@@ -560,6 +525,14 @@ impl Object {
Err(e) => Err(e),
}
}
+
+ pub fn content_v0(&self) -> Result {
+ match self.content() {
+ Ok(ObjectContent::V0(v0)) => Ok(v0),
+ Err(e) => Err(e),
+ _ => unimplemented!(),
+ }
+ }
}
#[cfg(test)]
@@ -599,28 +572,20 @@ mod test {
.read_to_end(&mut img_buffer)
.expect("read of test.jpg");
- let file = File::V0(FileV0 {
+ let file = FileV0 {
content_type: "image/jpeg".into(),
metadata: vec![],
content: img_buffer,
- });
- let content = ObjectContent::File(file);
+ };
+ let content = ObjectContentV0::File(file);
let deps: Vec = vec![Digest::Blake3Digest32([9; 32])];
- let exp = Some(2u32.pow(31));
let max_object_size = store_max_value_size();
let repo_secret = SymKey::ChaCha20Key([0; 32]);
let repo_pubkey = PubKey::Ed25519PubKey([1; 32]);
- let obj = Object::new(
- content,
- vec![],
- exp,
- max_object_size,
- repo_pubkey,
- repo_secret,
- );
+ let obj = Object::new(content, None, max_object_size, repo_pubkey, repo_secret);
log_debug!("obj.id: {:?}", obj.id());
log_debug!("obj.key: {:?}", obj.key());
@@ -642,14 +607,15 @@ mod test {
/// Test tree API
#[test]
pub fn test_object() {
- let file = File::V0(FileV0 {
+ let file = FileV0 {
content_type: "file/test".into(),
metadata: Vec::from("some meta data here"),
content: [(0..255).collect::>().as_slice(); 320].concat(),
- });
- let content = ObjectContent::File(file);
+ };
+ let content = ObjectContentV0::File(file);
- let deps: Vec = vec![Digest::Blake3Digest32([9; 32])];
+ let deps = vec![Digest::Blake3Digest32([9; 32])];
+ let header = CommitHeaderV0::new_with_deps(deps.clone());
let exp = Some(2u32.pow(31));
let max_object_size = 0;
@@ -658,8 +624,7 @@ mod test {
let obj = Object::new(
content.clone(),
- deps.clone(),
- exp,
+ header,
max_object_size,
repo_pubkey,
repo_secret.clone(),
@@ -667,6 +632,7 @@ mod test {
log_debug!("obj.id: {:?}", obj.id());
log_debug!("obj.key: {:?}", obj.key());
+ log_debug!("obj.acks: {:?}", obj.acks());
log_debug!("obj.deps: {:?}", obj.deps());
log_debug!("obj.blocks.len: {:?}", obj.blocks().len());
@@ -679,19 +645,20 @@ mod test {
assert_eq!(*obj.deps(), deps);
match obj.content() {
- Ok(cnt) => {
+ Ok(ObjectContent::V0(cnt)) => {
assert_eq!(content, cnt);
}
Err(e) => panic!("Object parse error: {:?}", e),
}
- let mut store = HashMapRepoStore::new();
+ let store = Box::new(HashMapRepoStore::new());
- obj.save(&mut store).expect("Object save error");
+ obj.save(&store).expect("Object save error");
let obj2 = Object::load(obj.id(), obj.key(), &store).unwrap();
log_debug!("obj2.id: {:?}", obj2.id());
log_debug!("obj2.key: {:?}", obj2.key());
+ log_debug!("obj2.acks: {:?}", obj2.acks());
log_debug!("obj2.deps: {:?}", obj2.deps());
log_debug!("obj2.blocks.len: {:?}", obj2.blocks().len());
let mut i = 0;
@@ -700,10 +667,9 @@ mod test {
i += 1;
}
- assert_eq!(*obj2.deps(), deps);
assert_eq!(*obj2.deps(), deps);
- match obj2.content() {
+ match obj2.content_v0() {
Ok(cnt) => {
assert_eq!(content, cnt);
}
@@ -729,20 +695,6 @@ mod test {
Err(e) => panic!("Object3 parse error: {:?}", e),
Ok(_) => panic!("Object3 should not return content"),
}
-
- let exp4 = Some(2342);
- let obj4 = obj.copy(exp4, repo_pubkey, repo_secret).unwrap();
- obj4.save(&mut store).unwrap();
-
- assert_eq!(obj4.expiry(), exp4);
- assert_eq!(*obj.deps(), deps);
-
- match obj4.content() {
- Ok(cnt) => {
- assert_eq!(content, cnt);
- }
- Err(e) => panic!("Object3 parse error: {:?}", e),
- }
}
/// Checks that a content that fits the root node, will not be chunked into children nodes
@@ -750,27 +702,27 @@ mod test {
pub fn test_depth_1() {
let deps: Vec = vec![Digest::Blake3Digest32([9; 32])];
- let empty_file = ObjectContent::File(File::V0(FileV0 {
+ let empty_file = ObjectContentV0::File(FileV0 {
content_type: "".into(),
metadata: vec![],
content: vec![],
- }));
+ });
let empty_file_ser = serde_bare::to_vec(&empty_file).unwrap();
log_debug!("empty file size: {}", empty_file_ser.len());
let size = store_max_value_size()
- EMPTY_BLOCK_SIZE
- DATA_VARINT_EXTRA
- - BLOCK_ID_SIZE * deps.len()
+ - BLOCK_ID_SIZE
- empty_file_ser.len()
- DATA_VARINT_EXTRA;
log_debug!("file size: {}", size);
- let content = ObjectContent::File(File::V0(FileV0 {
+ let content = ObjectContentV0::File(FileV0 {
content_type: "".into(),
metadata: vec![],
content: vec![99; size],
- }));
+ });
let content_ser = serde_bare::to_vec(&content).unwrap();
log_debug!("content len: {}", content_ser.len());
@@ -782,8 +734,7 @@ mod test {
let object = Object::new(
content,
- deps,
- expiry,
+ CommitHeaderV0::new_with_deps(deps),
max_object_size,
repo_pubkey,
repo_secret,
@@ -792,7 +743,7 @@ mod test {
log_debug!("root_id: {:?}", object.id());
log_debug!("root_key: {:?}", object.key().unwrap());
log_debug!("nodes.len: {:?}", object.blocks().len());
- //log_debug!("root: {:?}", tree.root());
+ //log_debug!("root: {:?}", tree.root_block());
//log_debug!("nodes: {:?}", object.blocks);
assert_eq!(object.blocks.len(), 1);
}
@@ -805,80 +756,48 @@ mod test {
let id = Digest::Blake3Digest32([0u8; 32]);
let key = SymKey::ChaCha20Key([0u8; 32]);
- let one_key = BlockContentV0::InternalNode(vec![key.clone()]);
+ let one_key = ChunkContentV0::InternalNode(vec![key.clone()]);
let one_key_ser = serde_bare::to_vec(&one_key).unwrap();
- let two_keys = BlockContentV0::InternalNode(vec![key.clone(), key.clone()]);
+ let two_keys = ChunkContentV0::InternalNode(vec![key.clone(), key.clone()]);
let two_keys_ser = serde_bare::to_vec(&two_keys).unwrap();
- let max_keys = BlockContentV0::InternalNode(vec![key.clone(); MAX_ARITY_LEAVES]);
+ let max_keys = ChunkContentV0::InternalNode(vec![key.clone(); MAX_ARITY_LEAVES]);
let max_keys_ser = serde_bare::to_vec(&max_keys).unwrap();
- let data = BlockContentV0::DataChunk(vec![]);
+ let data = ChunkContentV0::DataChunk(vec![]);
let data_ser = serde_bare::to_vec(&data).unwrap();
- let data_full = BlockContentV0::DataChunk(vec![0; MAX_DATA_PAYLOAD_SIZE]);
+ let data_full = ChunkContentV0::DataChunk(vec![0; MAX_DATA_PAYLOAD_SIZE]);
let data_full_ser = serde_bare::to_vec(&data_full).unwrap();
- let leaf_empty = Block::new(
- vec![],
- ObjectDeps::ObjectIdList(vec![]),
- Some(2342),
- data_ser.clone(),
- None,
- );
+ let leaf_empty = Block::new(vec![], None, data_ser.clone(), None);
let leaf_empty_ser = serde_bare::to_vec(&leaf_empty).unwrap();
- let leaf_full_data = Block::new(
- vec![],
- ObjectDeps::ObjectIdList(vec![]),
- Some(2342),
- data_full_ser.clone(),
- None,
- );
+ let leaf_full_data = Block::new(vec![], None, data_full_ser.clone(), None);
let leaf_full_data_ser = serde_bare::to_vec(&leaf_full_data).unwrap();
let root_depsref = Block::new(
vec![],
- ObjectDeps::DepListRef(ObjectRef { id: id, key: key }),
- Some(2342),
+ Some(ObjectRef::from_id_key(id, key.clone())),
data_ser.clone(),
None,
);
let root_depsref_ser = serde_bare::to_vec(&root_depsref).unwrap();
- let internal_max = Block::new(
- vec![id; MAX_ARITY_LEAVES],
- ObjectDeps::ObjectIdList(vec![]),
- Some(2342),
- max_keys_ser.clone(),
- None,
- );
+ let internal_max = Block::new(vec![id; MAX_ARITY_LEAVES], None, max_keys_ser.clone(), None);
let internal_max_ser = serde_bare::to_vec(&internal_max).unwrap();
- let internal_one = Block::new(
- vec![id; 1],
- ObjectDeps::ObjectIdList(vec![]),
- Some(2342),
- one_key_ser.clone(),
- None,
- );
+ let internal_one = Block::new(vec![id; 1], None, one_key_ser.clone(), None);
let internal_one_ser = serde_bare::to_vec(&internal_one).unwrap();
- let internal_two = Block::new(
- vec![id; 2],
- ObjectDeps::ObjectIdList(vec![]),
- Some(2342),
- two_keys_ser.clone(),
- None,
- );
+ let internal_two = Block::new(vec![id; 2], None, two_keys_ser.clone(), None);
let internal_two_ser = serde_bare::to_vec(&internal_two).unwrap();
let root_one = Block::new(
vec![id; 1],
- ObjectDeps::ObjectIdList(vec![id; 8]),
- Some(2342),
+ Some(ObjectRef::from_id_key(id, key.clone())),
one_key_ser.clone(),
None,
);
@@ -886,8 +805,7 @@ mod test {
let root_two = Block::new(
vec![id; 2],
- ObjectDeps::ObjectIdList(vec![id; 8]),
- Some(2342),
+ Some(ObjectRef::from_id_key(id, key)),
two_keys_ser.clone(),
None,
);
@@ -906,7 +824,7 @@ mod test {
log_debug!(
"max_data_payload_depth_1: {}",
- max_block_size - EMPTY_BLOCK_SIZE - DATA_VARINT_EXTRA - MAX_DEPS_SIZE
+ max_block_size - EMPTY_BLOCK_SIZE - DATA_VARINT_EXTRA - MAX_HEADER_SIZE
);
log_debug!(
@@ -928,7 +846,7 @@ mod test {
MAX_DATA_PAYLOAD_SIZE
);
let max_arity_root =
- (max_block_size - EMPTY_BLOCK_SIZE - MAX_DEPS_SIZE - BIG_VARINT_EXTRA * 2)
+ (max_block_size - EMPTY_BLOCK_SIZE - MAX_HEADER_SIZE - BIG_VARINT_EXTRA * 2)
/ (BLOCK_ID_SIZE + BLOCK_KEY_SIZE);
log_debug!("max_arity_root: {}", max_arity_root);
assert_eq!(max_arity_root, MAX_ARITY_ROOT);
diff --git a/p2p-repo/src/repo.rs b/p2p-repo/src/repo.rs
index 8434ec2..6d723c9 100644
--- a/p2p-repo/src/repo.rs
+++ b/p2p-repo/src/repo.rs
@@ -1,7 +1,5 @@
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
-// This code is partly derived from work written by TG x Thoth from P2Pcollab.
-// Copyright 2022 TG x Thoth
// Licensed under the Apache License, Version 2.0
//
// or the MIT license ,
@@ -9,38 +7,97 @@
// notice may not be copied, modified, or distributed except
// according to those terms.
-//! Repository
+//! Repository serde implementation and in memory helper
+use crate::errors::*;
+use crate::store::*;
use crate::types::*;
+use std::collections::HashMap;
+use std::collections::HashSet;
+
impl RepositoryV0 {
- pub fn new(
- id: &PubKey,
- branches: &Vec,
- allow_ext_requests: bool,
- metadata: &Vec,
- ) -> RepositoryV0 {
+ pub fn new(id: &PubKey, metadata: &Vec) -> RepositoryV0 {
RepositoryV0 {
id: id.clone(),
- branches: branches.clone(),
- allow_ext_requests,
metadata: metadata.clone(),
+ verification_program: vec![],
+ creator: None,
}
}
}
impl Repository {
- pub fn new(
+ pub fn new(id: &PubKey, metadata: &Vec) -> Repository {
+ Repository::V0(RepositoryV0::new(id, metadata))
+ }
+}
+
+pub struct UserInfo {
+ /// list of permissions granted to user, with optional metadata
+ pub permissions: HashMap>,
+}
+
+impl UserInfo {
+ pub fn has_any_perm(&self, perms: &HashSet<&Permission>) -> Result<(), NgError> {
+ let has_perms: HashSet<&Permission> = self.permissions.keys().collect();
+ if has_perms.intersection(perms).count() > 0 {
+ Ok(())
+ } else {
+ Err(NgError::PermissionDenied)
+ }
+ //
+ }
+ pub fn has_perm(&self, perm: &Permission) -> Result<&Vec, NgError> {
+ self.permissions.get(perm).ok_or(NgError::PermissionDenied)
+ }
+}
+
+/// In memory Repository representation. With helper functions that access the underlying UserStore and keeps proxy of the values
+pub struct Repo<'a> {
+ /// Repo definition
+ pub repo_def: Repository,
+
+ pub members: HashMap,
+
+ store: Box,
+}
+
+impl<'a> Repo<'a> {
+ pub fn new_with_member(
id: &PubKey,
- branches: &Vec,
- allow_ext_requests: bool,
- metadata: &Vec,
- ) -> Repository {
- Repository::V0(RepositoryV0::new(
- id,
- branches,
- allow_ext_requests,
- metadata,
- ))
+ member: UserId,
+ perms: &[Permission],
+ store: Box,
+ ) -> Self {
+ let mut members = HashMap::new();
+ let permissions = HashMap::from_iter(
+ perms
+ .iter()
+ .map(|p| (*p, vec![]))
+ .collect::)>>()
+ .iter()
+ .cloned(),
+ );
+ members.insert(member, UserInfo { permissions });
+ Self {
+ repo_def: Repository::new(id, &vec![]),
+ members,
+ store,
+ }
+ }
+
+ pub fn verify_permission(&self, commit: &Commit) -> Result<(), NgError> {
+ let content = commit.content_v0();
+ let body = commit.load_body(&self.store)?;
+ match self.members.get(&content.author) {
+ Some(info) => return info.has_any_perm(&body.required_permission()),
+ None => {}
+ }
+ Err(NgError::PermissionDenied)
+ }
+
+ pub fn get_store(&self) -> &Box {
+ &self.store
}
}
diff --git a/p2p-net/src/site.rs b/p2p-repo/src/site.rs
similarity index 90%
rename from p2p-net/src/site.rs
rename to p2p-repo/src/site.rs
index 523fc9b..9cd8c20 100644
--- a/p2p-net/src/site.rs
+++ b/p2p-repo/src/site.rs
@@ -9,11 +9,9 @@
* according to those terms.
*/
-use crate::types::{SiteStore, SiteType, SiteV0};
-
-use p2p_repo::errors::NgError;
-use p2p_repo::types::{BlockRef, PrivKey, SymKey};
-use p2p_repo::utils::{generate_keypair, sign, verify};
+use crate::errors::NgError;
+use crate::types::{BlockRef, PrivKey, SiteStore, SiteType, SiteV0, SymKey};
+use crate::utils::{generate_keypair, sign, verify};
impl SiteV0 {
// pub fn site_identity(&self) -> &Identity {
diff --git a/p2p-repo/src/store.rs b/p2p-repo/src/store.rs
index f10bc45..6cdec57 100644
--- a/p2p-repo/src/store.rs
+++ b/p2p-repo/src/store.rs
@@ -23,7 +23,7 @@ use std::{
mem::size_of_val,
};
-pub trait RepoStore {
+pub trait RepoStore: Send + Sync {
/// Load a block from the store.
fn get(&self, id: &BlockId) -> Result;
diff --git a/p2p-repo/src/types.rs b/p2p-repo/src/types.rs
index 8dd75cb..9687d04 100644
--- a/p2p-repo/src/types.rs
+++ b/p2p-repo/src/types.rs
@@ -21,7 +21,7 @@ use crate::utils::{
use core::fmt;
use serde::{Deserialize, Serialize};
use serde_bare::to_vec;
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
use std::hash::Hash;
use zeroize::{Zeroize, ZeroizeOnDrop};
@@ -272,22 +272,10 @@ pub type BloomFilter1K = [[u8; 32]; 32];
// REPOSITORY TYPES
//
-/// List of Permissions
-pub enum PermissionType {
- ADD_BRANCH,
- REMOVE_BRANCH,
- CHANGE_NAME,
- ADD_MEMBER,
- REMOVE_MEMBER,
- CHANGE_PERMISSION,
- TRANSACTION,
- SNAPSHOT,
- SHARING,
- CHANGE_ACK_CONFIG,
-}
-
-/// RepoHash:
-/// BLAKE3 hash of the RepoId
+/// RepoId is a PubKey
+pub type RepoId = PubKey;
+
+/// RepoHash is the BLAKE3 Digest over the RepoId
pub type RepoHash = Digest;
// impl From for String {
@@ -296,13 +284,21 @@ pub type RepoHash = Digest;
// }
// }
-/// RepoId is a PubKey
-pub type RepoId = PubKey;
+/// Topic ID: public key of the topic
+pub type TopicId = PubKey;
+
+/// User ID: user account for broker
+pub type UserId = PubKey;
+
+/// BranchId is a PubKey
+pub type BranchId = PubKey;
/// Block ID:
-/// BLAKE3 hash over the serialized Object with encrypted content
+/// BLAKE3 hash over the serialized BlockContent (contains encrypted content)
pub type BlockId = Digest;
+pub type BlockKey = SymKey;
+
/// Block reference
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct BlockRef {
@@ -310,7 +306,7 @@ pub struct BlockRef {
pub id: BlockId,
/// Key for decrypting the Object
- pub key: SymKey,
+ pub key: BlockKey,
}
impl BlockRef {
@@ -321,20 +317,138 @@ impl BlockRef {
key: SymKey::ChaCha20Key([0u8; 32]),
}
}
+ pub fn from_id_key(id: BlockId, key: BlockKey) -> Self {
+ BlockRef { id, key }
+ }
+}
+
+impl From<(&BlockId, &BlockKey)> for BlockRef {
+ fn from(id_key: (&BlockId, &BlockKey)) -> Self {
+ BlockRef {
+ id: id_key.0.clone(),
+ key: id_key.1.clone(),
+ }
+ }
}
/// Object ID
pub type ObjectId = BlockId;
+/// Object Key
+pub type ObjectKey = BlockKey;
+
/// Object reference
pub type ObjectRef = BlockRef;
+/// IDENTITY, SITE, STORE, OVERLAY common types
+
+/// List of Identity types
+#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
+pub enum Identity {
+ OrgSite(PubKey),
+ IndividualSite(PubKey),
+ OrgPublicStore(PubKey),
+ OrgProtectedStore(PubKey),
+ OrgPrivateStore(PubKey),
+ IndividualPublicStore(PubKey),
+ IndividualProtectedStore(PubKey),
+ IndividualPrivateStore(PubKey),
+}
+
+/// List of Store Overlay types
+#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
+pub enum StoreOverlay {
+ PublicStore(PubKey),
+ ProtectedStore(PubKey),
+ PrivateStore(PubKey),
+ Group(PubKey),
+ Dialog(Digest),
+ //Document(RepoId),
+}
+
+/// List of Store Root Repo types
+#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
+pub enum StoreRootRepo {
+ PublicStore(RepoId),
+ ProtectedStore(RepoId),
+ PrivateStore(RepoId),
+ Group(RepoId),
+ Dialog(RepoId),
+}
+
+/// Site type
+#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
+pub enum SiteType {
+ Org,
+ Individual, // formerly Personal
+}
+
+/// Site Store
+#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
+pub struct SiteStore {
+ // pub identity: Identity,
+ pub key: PrivKey,
+ // signature with site_key
+ // pub sig: Sig,
+ pub root_branch_def_ref: ObjectRef,
+
+ pub repo_secret: SymKey,
+}
+
+/// Site Store type
+#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
+pub enum SiteStoreType {
+ Public,
+ Protected,
+ Private,
+}
+
+/// Site V0
+#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
+pub struct SiteV0 {
+ pub site_type: SiteType,
+ // Identity::OrgSite or Identity::IndividualSite
+ // pub site_identity: Identity,
+ pub site_key: PrivKey,
+
+ // Identity::OrgPublicStore or Identity::IndividualPublicStore
+ pub public: SiteStore,
+
+ // Identity::OrgProtectedStore or Identity::IndividualProtectedStore
+ pub protected: SiteStore,
+
+ // Identity::OrgPrivateStore or Identity::IndividualPrivateStore
+ pub private: SiteStore,
+
+ pub cores: Vec<(PubKey, Option<[u8; 32]>)>,
+
+ pub bootstraps: Vec,
+}
+
+/// Reduced Site (for QRcode)
+#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
+pub struct ReducedSiteV0 {
+ pub site_key: PrivKey,
+
+ pub private_site_key: PrivKey,
+
+ pub private_site_root_branch_def_ref: ObjectRef,
+
+ pub private_site_repo_secret: SymKey,
+
+ pub core: PubKey,
+
+ pub bootstraps: Vec,
+}
+
+/// BLOCKS common types
+
/// Internal node of a Merkle tree
-pub type InternalNode = Vec;
+pub type InternalNode = Vec