user_storage keeps repos and stores. events sent to broker on first connection

pull/19/head
Niko PLP 8 months ago
parent 86e5ec52ef
commit 5fed085379
  1. 1
      Cargo.lock
  2. 1
      nextgraph/.gitignore
  3. 10
      nextgraph/examples/open.rs
  4. 1
      nextgraph/examples/persistent.rs
  5. 52
      nextgraph/src/local_broker.rs
  6. 5
      ng-app/src/routes/WalletCreate.svelte
  7. 14
      ng-broker/src/broker_storage/account.rs
  8. 13
      ng-broker/src/broker_storage/config.rs
  9. 12
      ng-broker/src/broker_storage/invitation.rs
  10. 8
      ng-broker/src/broker_storage/overlay.rs
  11. 13
      ng-broker/src/broker_storage/peer.rs
  12. 8
      ng-broker/src/broker_storage/topic.rs
  13. 6
      ng-broker/src/broker_storage/wallet.rs
  14. 72
      ng-broker/src/server_storage.rs
  15. 2
      ng-broker/src/server_ws.rs
  16. 5
      ng-net/src/actors/client/mod.rs
  17. 124
      ng-net/src/actors/client/pin_repo.rs
  18. 88
      ng-net/src/actors/client/repo_pin_status.rs
  19. 113
      ng-net/src/actors/client/topic_sub.rs
  20. 2
      ng-net/src/actors/mod.rs
  21. 32
      ng-net/src/broker.rs
  22. 3
      ng-net/src/errors.rs
  23. 24
      ng-net/src/server_storage.rs
  24. 297
      ng-net/src/types.rs
  25. 7
      ng-repo/src/errors.rs
  26. 11
      ng-repo/src/event.rs
  27. 12
      ng-repo/src/kcv_storage.rs
  28. 29
      ng-repo/src/repo.rs
  29. 12
      ng-repo/src/store.rs
  30. 33
      ng-repo/src/types.rs
  31. 30
      ng-storage-lmdb/src/kcv_storage.rs
  32. 8
      ng-storage-rocksdb/src/block_storage.rs
  33. 101
      ng-storage-rocksdb/src/kcv_storage.rs
  34. 1
      ng-verifier/Cargo.toml
  35. 37
      ng-verifier/src/rocksdb_user_storage.rs
  36. 27
      ng-verifier/src/site.rs
  37. 13
      ng-verifier/src/types.rs
  38. 173
      ng-verifier/src/user_storage/branch.rs
  39. 29
      ng-verifier/src/user_storage/mod.rs
  40. 352
      ng-verifier/src/user_storage/repo.rs
  41. 36
      ng-verifier/src/user_storage/storage.rs
  42. 390
      ng-verifier/src/verifier.rs
  43. 19
      ng-wallet/src/lib.rs
  44. 2
      ngcli/src/old.rs
  45. 6
      ngone/src/main.rs
  46. 8
      ngone/src/store/dynpeer.rs
  47. 11
      ngone/src/store/wallet_record.rs

1
Cargo.lock generated

@ -3419,6 +3419,7 @@ dependencies = [
"automerge",
"blake3",
"chacha20",
"either",
"getrandom 0.2.10",
"ng-net",
"ng-repo",

@ -0,0 +1 @@
tests

@ -37,7 +37,7 @@ async fn main() -> std::io::Result<()> {
}))
.await;
let wallet_name = "EJdLRVx93o3iUXoB0wSTqxh1-zYac-84vHb3oBbZ_HY".to_string();
let wallet_name = "hQK0RBKua5TUm2jqeSGPOMMzqplllAkbUgEh5P6Otf4".to_string();
// as we have previously saved the wallet,
// we can retrieve it, display the security phrase and image to the user, ask for the pazzle or mnemonic, and then open the wallet
@ -48,8 +48,8 @@ async fn main() -> std::io::Result<()> {
// now let's open the wallet, by providing the pazzle and PIN code
let opened_wallet = wallet_open_with_pazzle(
&wallet,
vec![117, 134, 59, 92, 98, 35, 70, 22, 9],
[1, 2, 1, 2],
vec![134, 54, 112, 46, 94, 65, 20, 2, 99],
[2, 3, 2, 3],
)?;
let user_id = opened_wallet.personal_identity();
@ -65,8 +65,8 @@ async fn main() -> std::io::Result<()> {
let status = user_connect(&user_id).await?;
// The connection cannot succeed because we miss-configured the core_bootstrap of the wallet. its Peer ID is invalid.
let error_reason = status[0].3.as_ref().unwrap();
assert!(error_reason == "NoiseHandshakeFailed" || error_reason == "ConnectionError");
println!("Connection was : {:?}", status[0]);
//assert!(error_reason == "NoiseHandshakeFailed" || error_reason == "ConnectionError");
// Then we should disconnect
user_disconnect(&user_id).await?;

@ -44,7 +44,6 @@ async fn main() -> std::io::Result<()> {
// the peer_id should come from somewhere else.
// this is just given for the sake of an example
#[allow(deprecated)]
let peer_id_of_server_broker = PubKey::nil();
// Create your wallet

@ -168,6 +168,12 @@ impl LocalBrokerConfig {
_ => false,
}
}
pub fn is_persistent(&self) -> bool {
match self {
Self::BasePath(_) => true,
_ => false,
}
}
#[doc(hidden)]
pub fn is_js(&self) -> bool {
match self {
@ -702,7 +708,7 @@ impl LocalBroker {
key_material.as_slice(),
);
key_material.zeroize();
let verifier = Verifier::new(
let mut verifier = Verifier::new(
VerifierConfig {
config_type: broker.verifier_config_type_from_session_config(&config),
user_master_key: key,
@ -714,6 +720,10 @@ impl LocalBroker {
block_storage,
)?;
key.zeroize();
//load verifier from local_storage (if rocks_db)
let _ = verifier.load();
broker.opened_sessions_list.push(Some(Session {
config,
peer_key: session.peer_key.clone(),
@ -722,6 +732,7 @@ impl LocalBroker {
}));
let idx = broker.opened_sessions_list.len() - 1;
broker.opened_sessions.insert(user_id, idx as u8);
Ok(SessionInfo {
session_id: idx as u8,
user: user_id,
@ -892,7 +903,7 @@ pub async fn wallet_create_v0(params: CreateWalletV0) -> Result<CreateWalletResu
.unwrap();
let (mut res, site, brokers) =
create_wallet_second_step_v0(intermediate, &mut session.verifier)?;
create_wallet_second_step_v0(intermediate, &mut session.verifier).await?;
broker.wallets.get_mut(&res.wallet_name).unwrap().wallet = res.wallet.clone();
LocalBroker::wallet_save(&mut broker)?;
@ -1256,6 +1267,13 @@ pub async fn user_connect_with_device_info(
if tried.is_some() && tried.as_ref().unwrap().3.is_none() {
session.verifier.connected_server_id = Some(server_key);
// successful. we can stop here
// we immediately send the events present in the outbox
let res = session.verifier.send_outbox().await;
log_info!("SENDING EVENTS FROM OUTBOX: {:?}", res);
// TODO: load verifier from remote connection (if not RocksDb type)
break;
} else {
log_debug!("Failed connection {:?}", tried);
@ -1406,6 +1424,7 @@ mod test {
};
use ng_net::types::BootstrapContentV0;
use ng_wallet::{display_mnemonic, emojis::display_pazzle};
use std::fs::read_to_string;
use std::fs::{create_dir_all, File};
use std::io::BufReader;
use std::io::Read;
@ -1431,7 +1450,7 @@ mod test {
init_local_broker(Box::new(|| LocalBrokerConfig::InMemory)).await;
#[allow(deprecated)]
let peer_id = "X0nh-gOTGKSx0yL0LYJviOWRNacyqIzjQW_LKdK6opU";
let peer_id_of_server_broker = PubKey::nil();
let wallet_result = wallet_create_v0(CreateWalletV0 {
@ -1495,7 +1514,30 @@ mod test {
file.write_all(&ser).expect("write of opened_wallet file");
}
async fn init_session_for_test() -> (UserId, String) {
#[async_std::test]
async fn gen_opened_wallet_file_for_test() {
let wallet_file = read("tests/wallet.ngw").expect("read wallet file");
init_local_broker(Box::new(|| LocalBrokerConfig::InMemory)).await;
let wallet = wallet_read_file(wallet_file)
.await
.expect("wallet_read_file");
let pazzle_string = read_to_string("tests/wallet.pazzle").expect("read pazzle file");
let pazzle_words = pazzle_string.split(' ').map(|s| s.to_string()).collect();
let opened_wallet = wallet_open_with_pazzle_words(&wallet, &pazzle_words, [2, 3, 2, 3])
.expect("opening of wallet");
let mut file =
File::create("tests/opened_wallet.ngw").expect("open for write opened_wallet file");
let ser = serde_bare::to_vec(&opened_wallet).expect("serialization of opened wallet");
file.write_all(&ser).expect("write of opened_wallet file");
}
async fn import_session_for_test() -> (UserId, String) {
let wallet_file = read("tests/wallet.ngw").expect("read wallet file");
let opened_wallet_file = read("tests/opened_wallet.ngw").expect("read opened_wallet file");
let opened_wallet: SensitiveWallet =
@ -1523,7 +1565,7 @@ mod test {
#[async_std::test]
async fn import_wallet() {
let (user_id, wallet_name) = init_session_for_test().await;
let (user_id, wallet_name) = import_session_for_test().await;
let status = user_connect(&user_id).await.expect("user_connect");

@ -1527,7 +1527,10 @@
/>
then throw it away.<br /> The order of each image is important.<br
/>
Now click on "Continue to Login"<br /><br />
Now click on "Continue to Login."<br /><br />It is important that
you login with this wallet at least once from this device<br />
(while connected to the internet), so that your personal site is
created on your broker.<br /><br />
<a href="/wallet/login" use:link>
<button
tabindex="-1"

@ -16,7 +16,7 @@ use std::time::SystemTime;
use ng_net::types::*;
use ng_repo::errors::StorageError;
use ng_repo::kcv_storage::KCVStore;
use ng_repo::kcv_storage::KCVStorage;
use ng_repo::log::*;
use ng_repo::types::UserId;
use serde_bare::{from_slice, to_vec};
@ -24,7 +24,7 @@ use serde_bare::{from_slice, to_vec};
pub struct Account<'a> {
/// User ID
id: UserId,
store: &'a dyn KCVStore,
store: &'a dyn KCVStorage,
}
impl<'a> Account<'a> {
@ -38,7 +38,7 @@ impl<'a> Account<'a> {
const ALL_CLIENT_PROPERTIES: [u8; 2] = [Self::INFO, Self::LAST_SEEN];
pub fn open(id: &UserId, store: &'a dyn KCVStore) -> Result<Account<'a>, StorageError> {
pub fn open(id: &UserId, store: &'a dyn KCVStorage) -> Result<Account<'a>, StorageError> {
let opening = Account {
id: id.clone(),
store,
@ -51,7 +51,7 @@ impl<'a> Account<'a> {
pub fn create(
id: &UserId,
admin: bool,
store: &'a dyn KCVStore,
store: &'a dyn KCVStorage,
) -> Result<Account<'a>, StorageError> {
let acc = Account {
id: id.clone(),
@ -73,7 +73,7 @@ impl<'a> Account<'a> {
#[allow(deprecated)]
pub fn get_all_users(
admins: bool,
store: &'a dyn KCVStore,
store: &'a dyn KCVStorage,
) -> Result<Vec<UserId>, StorageError> {
let size = to_vec(&UserId::nil())?.len();
let mut res: Vec<UserId> = vec![];
@ -248,7 +248,7 @@ mod test {
use ng_repo::errors::StorageError;
use ng_repo::types::*;
use ng_repo::utils::*;
use ng_storage_rocksdb::kcv_storage::RocksdbKCVStore;
use ng_storage_rocksdb::kcv_storage::RocksdbKCVStorage;
use std::fs;
use tempfile::Builder;
@ -261,7 +261,7 @@ mod test {
let key: [u8; 32] = [0; 32];
fs::create_dir_all(root.path()).unwrap();
println!("{}", root.path().to_str().unwrap());
let mut store = RocksdbKCVStore::open(root.path(), key).unwrap();
let mut store = RocksdbKCVStorage::open(root.path(), key).unwrap();
let user_id = PubKey::Ed25519PubKey([1; 32]);

@ -11,7 +11,7 @@
use ng_net::types::*;
use ng_repo::errors::StorageError;
use ng_repo::kcv_storage::KCVStore;
use ng_repo::kcv_storage::KCVStorage;
use ng_repo::types::*;
use serde::{Deserialize, Serialize};
use serde_bare::{from_slice, to_vec};
@ -24,7 +24,7 @@ pub enum ConfigMode {
}
pub struct Config<'a> {
store: &'a dyn KCVStore,
store: &'a dyn KCVStorage,
}
impl<'a> Config<'a> {
@ -39,7 +39,7 @@ impl<'a> Config<'a> {
const SUFFIX_FOR_EXIST_CHECK: u8 = Self::MODE;
pub fn open(store: &'a dyn KCVStore) -> Result<Config<'a>, StorageError> {
pub fn open(store: &'a dyn KCVStorage) -> Result<Config<'a>, StorageError> {
let opening = Config { store };
if !opening.exists() {
return Err(StorageError::NotFound);
@ -48,7 +48,7 @@ impl<'a> Config<'a> {
}
pub fn get_or_create(
mode: &ConfigMode,
store: &'a dyn KCVStore,
store: &'a dyn KCVStorage,
) -> Result<Config<'a>, StorageError> {
match Self::open(store) {
Err(e) => {
@ -66,7 +66,10 @@ impl<'a> Config<'a> {
}
}
}
pub fn create(mode: &ConfigMode, store: &'a dyn KCVStore) -> Result<Config<'a>, StorageError> {
pub fn create(
mode: &ConfigMode,
store: &'a dyn KCVStorage,
) -> Result<Config<'a>, StorageError> {
let acc = Config { store };
if acc.exists() {
return Err(StorageError::BackendError);

@ -17,7 +17,7 @@ use std::time::SystemTime;
use ng_net::errors::ProtocolError;
use ng_net::types::*;
use ng_repo::errors::StorageError;
use ng_repo::kcv_storage::KCVStore;
use ng_repo::kcv_storage::KCVStorage;
use ng_repo::types::SymKey;
use ng_repo::types::Timestamp;
use ng_repo::utils::now_timestamp;
@ -25,9 +25,9 @@ use serde_bare::from_slice;
use serde_bare::to_vec;
pub struct Invitation<'a> {
/// User ID
/// code
id: [u8; 32],
store: &'a dyn KCVStore,
store: &'a dyn KCVStorage,
}
impl<'a> Invitation<'a> {
@ -45,7 +45,7 @@ impl<'a> Invitation<'a> {
const SUFFIX_FOR_EXIST_CHECK: u8 = Self::TYPE;
pub fn open(id: &[u8; 32], store: &'a dyn KCVStore) -> Result<Invitation<'a>, StorageError> {
pub fn open(id: &[u8; 32], store: &'a dyn KCVStorage) -> Result<Invitation<'a>, StorageError> {
let opening = Invitation {
id: id.clone(),
store,
@ -59,7 +59,7 @@ impl<'a> Invitation<'a> {
id: &InvitationCode,
expiry: u32,
memo: &Option<String>,
store: &'a dyn KCVStore,
store: &'a dyn KCVStorage,
) -> Result<Invitation<'a>, StorageError> {
let (code_type, code) = match id {
InvitationCode::Unique(c) => (0u8, c.slice()),
@ -88,7 +88,7 @@ impl<'a> Invitation<'a> {
}
pub fn get_all_invitations(
store: &'a dyn KCVStore,
store: &'a dyn KCVStorage,
mut admin: bool,
mut unique: bool,
mut multi: bool,

@ -11,7 +11,7 @@
use ng_net::types::*;
use ng_repo::errors::StorageError;
use ng_repo::kcv_storage::KCVStore;
use ng_repo::kcv_storage::KCVStorage;
use ng_repo::types::*;
use ng_repo::utils::now_timestamp;
use serde::{Deserialize, Serialize};
@ -27,7 +27,7 @@ pub struct OverlayMeta {
pub struct Overlay<'a> {
/// Overlay ID
id: OverlayId,
store: &'a dyn KCVStore,
store: &'a dyn KCVStorage,
}
impl<'a> Overlay<'a> {
@ -50,7 +50,7 @@ impl<'a> Overlay<'a> {
const SUFFIX_FOR_EXIST_CHECK: u8 = Self::SECRET;
pub fn open(id: &OverlayId, store: &'a dyn KCVStore) -> Result<Overlay<'a>, StorageError> {
pub fn open(id: &OverlayId, store: &'a dyn KCVStorage) -> Result<Overlay<'a>, StorageError> {
let opening = Overlay {
id: id.clone(),
store,
@ -64,7 +64,7 @@ impl<'a> Overlay<'a> {
id: &OverlayId,
secret: &SymKey,
repo: Option<PubKey>,
store: &'a dyn KCVStore,
store: &'a dyn KCVStorage,
) -> Result<Overlay<'a>, StorageError> {
let acc = Overlay {
id: id.clone(),

@ -11,7 +11,7 @@
use ng_net::types::*;
use ng_repo::errors::StorageError;
use ng_repo::kcv_storage::KCVStore;
use ng_repo::kcv_storage::KCVStorage;
use ng_repo::types::*;
use serde::{Deserialize, Serialize};
use serde_bare::{from_slice, to_vec};
@ -19,7 +19,7 @@ use serde_bare::{from_slice, to_vec};
pub struct Peer<'a> {
/// Topic ID
id: PeerId,
store: &'a dyn KCVStore,
store: &'a dyn KCVStorage,
}
impl<'a> Peer<'a> {
@ -33,7 +33,7 @@ impl<'a> Peer<'a> {
const SUFFIX_FOR_EXIST_CHECK: u8 = Self::VERSION;
pub fn open(id: &PeerId, store: &'a dyn KCVStore) -> Result<Peer<'a>, StorageError> {
pub fn open(id: &PeerId, store: &'a dyn KCVStorage) -> Result<Peer<'a>, StorageError> {
let opening = Peer {
id: id.clone(),
store,
@ -45,7 +45,7 @@ impl<'a> Peer<'a> {
}
pub fn update_or_create(
advert: &PeerAdvert,
store: &'a dyn KCVStore,
store: &'a dyn KCVStorage,
) -> Result<Peer<'a>, StorageError> {
let id = advert.peer();
match Self::open(id, store) {
@ -62,7 +62,10 @@ impl<'a> Peer<'a> {
}
}
}
pub fn create(advert: &PeerAdvert, store: &'a dyn KCVStore) -> Result<Peer<'a>, StorageError> {
pub fn create(
advert: &PeerAdvert,
store: &'a dyn KCVStorage,
) -> Result<Peer<'a>, StorageError> {
let id = advert.peer();
let acc = Peer {
id: id.clone(),

@ -11,7 +11,7 @@
use ng_net::types::*;
use ng_repo::errors::StorageError;
use ng_repo::kcv_storage::KCVStore;
use ng_repo::kcv_storage::KCVStorage;
use ng_repo::types::*;
use serde::{Deserialize, Serialize};
use serde_bare::{from_slice, to_vec};
@ -25,7 +25,7 @@ pub struct TopicMeta {
pub struct Topic<'a> {
/// Topic ID
id: TopicId,
store: &'a dyn KCVStore,
store: &'a dyn KCVStorage,
}
impl<'a> Topic<'a> {
@ -40,7 +40,7 @@ impl<'a> Topic<'a> {
const SUFFIX_FOR_EXIST_CHECK: u8 = Self::META;
pub fn open(id: &TopicId, store: &'a dyn KCVStore) -> Result<Topic<'a>, StorageError> {
pub fn open(id: &TopicId, store: &'a dyn KCVStorage) -> Result<Topic<'a>, StorageError> {
let opening = Topic {
id: id.clone(),
store,
@ -50,7 +50,7 @@ impl<'a> Topic<'a> {
}
Ok(opening)
}
pub fn create(id: &TopicId, store: &'a mut dyn KCVStore) -> Result<Topic<'a>, StorageError> {
pub fn create(id: &TopicId, store: &'a mut dyn KCVStorage) -> Result<Topic<'a>, StorageError> {
let acc = Topic {
id: id.clone(),
store,

@ -11,7 +11,7 @@
use ng_net::types::*;
use ng_repo::errors::StorageError;
use ng_repo::kcv_storage::KCVStore;
use ng_repo::kcv_storage::KCVStorage;
use ng_repo::kcv_storage::WriteTransaction;
use ng_repo::log::*;
use ng_repo::types::*;
@ -19,7 +19,7 @@ use serde::{Deserialize, Serialize};
use serde_bare::{from_slice, to_vec};
pub struct Wallet<'a> {
store: &'a dyn KCVStore,
store: &'a dyn KCVStorage,
}
impl<'a> Wallet<'a> {
@ -37,7 +37,7 @@ impl<'a> Wallet<'a> {
const SUFFIX_FOR_EXIST_CHECK: u8 = Self::SYM_KEY;
pub fn open(store: &'a dyn KCVStore) -> Wallet<'a> {
pub fn open(store: &'a dyn KCVStorage) -> Wallet<'a> {
Wallet { store }
}
pub fn get_or_create_single_key(

@ -21,17 +21,17 @@ use crate::broker_storage::wallet::Wallet;
use crate::types::*;
use ng_net::errors::{ProtocolError, ServerError};
use ng_net::server_storage::*;
use ng_net::types::{BootstrapContentV0, InvitationCode, InvitationV0};
use ng_net::types::*;
use ng_repo::errors::StorageError;
use ng_repo::kcv_storage::KCVStore;
use ng_repo::kcv_storage::KCVStorage;
use ng_repo::log::*;
use ng_repo::types::{PeerId, PubKey, SymKey};
use ng_storage_rocksdb::kcv_storage::RocksdbKCVStore;
use ng_repo::types::*;
use ng_storage_rocksdb::kcv_storage::RocksdbKCVStorage;
pub struct RocksdbServerStorage {
wallet_storage: RocksdbKCVStore,
accounts_storage: RocksdbKCVStore,
peers_storage: RocksdbKCVStore,
wallet_storage: RocksdbKCVStorage,
accounts_storage: RocksdbKCVStorage,
peers_storage: RocksdbKCVStorage,
peers_last_seq_path: PathBuf,
peers_last_seq: Mutex<HashMap<PeerId, u64>>,
}
@ -48,7 +48,7 @@ impl RocksdbServerStorage {
std::fs::create_dir_all(wallet_path.clone()).unwrap();
log_debug!("opening wallet DB");
//TODO redo the whole key passing mechanism in RKV so it uses zeroize all the way
let wallet_storage = RocksdbKCVStore::open(&wallet_path, master_key.slice().clone())?;
let wallet_storage = RocksdbKCVStorage::open(&wallet_path, master_key.slice().clone())?;
let wallet = Wallet::open(&wallet_storage);
// create/open the ACCOUNTS storage
@ -60,7 +60,7 @@ impl RocksdbServerStorage {
accounts_key = wallet.create_accounts_key()?;
std::fs::create_dir_all(accounts_path.clone()).unwrap();
let accounts_storage =
RocksdbKCVStore::open(&accounts_path, accounts_key.slice().clone())?;
RocksdbKCVStorage::open(&accounts_path, accounts_key.slice().clone())?;
let symkey = SymKey::random();
let invite_code = InvitationCode::Admin(symkey.clone());
let _ = Invitation::create(
@ -87,7 +87,8 @@ impl RocksdbServerStorage {
log_debug!("opening accounts DB");
std::fs::create_dir_all(accounts_path.clone()).unwrap();
//TODO redo the whole key passing mechanism in RKV so it uses zeroize all the way
let accounts_storage = RocksdbKCVStore::open(&accounts_path, accounts_key.slice().clone())?;
let accounts_storage =
RocksdbKCVStorage::open(&accounts_path, accounts_key.slice().clone())?;
// create/open the PEERS storage
log_debug!("opening peers DB");
@ -96,7 +97,7 @@ impl RocksdbServerStorage {
peers_path.push("peers");
std::fs::create_dir_all(peers_path.clone()).unwrap();
//TODO redo the whole key passing mechanism in RKV so it uses zeroize all the way
let peers_storage = RocksdbKCVStore::open(&peers_path, peers_key.slice().clone())?;
let peers_storage = RocksdbKCVStorage::open(&peers_path, peers_key.slice().clone())?;
// creates the path for peers_last_seq
let mut peers_last_seq_path = path.clone();
@ -198,4 +199,53 @@ impl ServerStorage for RocksdbServerStorage {
inv.del()?;
Ok(())
}
fn get_repo_pin_status(
&self,
overlay: &OverlayId,
repo: &RepoHash,
) -> Result<RepoPinStatus, ProtocolError> {
//TODO: implement correctly !
Ok(RepoPinStatus::V0(RepoPinStatusV0 {
hash: repo.clone(),
// only possible for RW overlays
expose_outer: false,
// list of topics that are subscribed to
topics: vec![],
}))
}
fn pin_repo(
&self,
overlay: &OverlayId,
repo: &RepoHash,
ro_topics: &Vec<TopicId>,
rw_topics: &Vec<PublisherAdvert>,
) -> Result<RepoOpened, ProtocolError> {
//TODO: implement correctly !
let mut opened = Vec::with_capacity(ro_topics.len() + rw_topics.len());
for topic in ro_topics {
opened.push((*topic).into());
}
for topic in rw_topics {
opened.push((*topic).into());
}
Ok(opened)
}
fn topic_sub(
&self,
overlay: &OverlayId,
repo: &RepoHash,
topic: &TopicId,
publisher: Option<&PublisherAdvert>,
) -> Result<TopicSubRes, ProtocolError> {
//TODO: implement correctly !
Ok(TopicSubRes::V0(TopicSubResV0 {
topic: topic.clone(),
known_heads: vec![],
publisher: publisher.is_some(),
}))
}
}

@ -586,7 +586,7 @@ pub async fn run_server_accept_one(
// let master_key: [u8; 32] = [0; 32];
// std::fs::create_dir_all(root.path()).unwrap();
// log_debug!("data directory: {}", root.path().to_str().unwrap());
// let store = RocksdbKCVStore::open(root.path(), master_key);
// let store = RocksdbKCVStorage::open(root.path(), master_key);
let socket = TcpListener::bind(addrs.as_str()).await?;
log_debug!("Listening on {}", addrs.as_str());

@ -0,0 +1,5 @@
pub mod repo_pin_status;
pub mod pin_repo;
pub mod topic_sub;

@ -0,0 +1,124 @@
/*
* Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
* All rights reserved.
* Licensed under the Apache License, Version 2.0
* <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
* or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
* at your option. All files in the project carrying such
* notice may not be copied, modified, or distributed except
* according to those terms.
*/
use crate::broker::{ServerConfig, BROKER};
use crate::connection::NoiseFSM;
use crate::types::*;
use crate::{actor::*, errors::ProtocolError, types::ProtocolMessage};
use async_std::sync::Mutex;
use ng_repo::log::*;
use ng_repo::repo::Repo;
use ng_repo::types::*;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
impl PinRepo {
pub fn get_actor(&self) -> Box<dyn EActor> {
Actor::<PinRepo, RepoOpened>::new_responder()
}
pub fn from_repo(repo: &Repo, broker_id: &DirectPeerId) -> PinRepo {
let overlay = OverlayAccess::ReadWrite((
OverlayId::inner_from_store(&repo.store),
OverlayId::outer(repo.store.id()),
));
let mut rw_topics = Vec::with_capacity(repo.branches.len());
let mut ro_topics = vec![];
for (_, branch) in repo.branches.iter() {
if let Some(privkey) = &branch.topic_priv_key {
rw_topics.push(PublisherAdvert::new(
branch.topic,
privkey.clone(),
*broker_id,
));
} else {
ro_topics.push(branch.topic);
}
}
PinRepo::V0(PinRepoV0 {
hash: repo.id.into(),
overlay,
// TODO: overlay_root_topic
overlay_root_topic: None,
expose_outer: false,
peers: vec![],
max_peer_count: 0,
allowed_peers: vec![],
ro_topics,
rw_topics,
})
}
}
impl TryFrom<ProtocolMessage> for PinRepo {
type Error = ProtocolError;
fn try_from(msg: ProtocolMessage) -> Result<Self, Self::Error> {
let req: ClientRequestContentV0 = msg.try_into()?;
if let ClientRequestContentV0::PinRepo(a) = req {
Ok(a)
} else {
log_debug!("INVALID {:?}", req);
Err(ProtocolError::InvalidValue)
}
}
}
impl From<PinRepo> for ProtocolMessage {
fn from(msg: PinRepo) -> ProtocolMessage {
let overlay = match msg {
PinRepo::V0(ref v0) => v0.overlay.overlay_id_for_client_protocol_purpose().clone(),
};
ProtocolMessage::from_client_request_v0(ClientRequestContentV0::PinRepo(msg), overlay)
}
}
impl TryFrom<ProtocolMessage> for RepoOpened {
type Error = ProtocolError;
fn try_from(msg: ProtocolMessage) -> Result<Self, Self::Error> {
let res: ClientResponseContentV0 = msg.try_into()?;
if let ClientResponseContentV0::RepoOpened(a) = res {
Ok(a)
} else {
log_debug!("INVALID {:?}", res);
Err(ProtocolError::InvalidValue)
}
}
}
impl From<RepoOpened> for ProtocolMessage {
fn from(res: RepoOpened) -> ProtocolMessage {
ClientResponseContentV0::RepoOpened(res).into()
}
}
impl Actor<'_, RepoPinStatusReq, RepoPinStatus> {}
#[async_trait::async_trait]
impl EActor for Actor<'_, PinRepo, RepoOpened> {
async fn respond(
&mut self,
msg: ProtocolMessage,
fsm: Arc<Mutex<NoiseFSM>>,
) -> Result<(), ProtocolError> {
let req = PinRepo::try_from(msg)?;
//TODO implement all the server side logic
let broker = BROKER.read().await;
let res = broker.get_server_storage()?.pin_repo(
req.overlay(),
req.hash(),
req.ro_topics(),
req.rw_topics(),
)?;
fsm.lock().await.send(res.into()).await?;
Ok(())
}
}

@ -0,0 +1,88 @@
/*
* Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
* All rights reserved.
* Licensed under the Apache License, Version 2.0
* <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
* or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
* at your option. All files in the project carrying such
* notice may not be copied, modified, or distributed except
* according to those terms.
*/
use crate::broker::{ServerConfig, BROKER};
use crate::connection::NoiseFSM;
use crate::types::*;
use crate::{actor::*, errors::ProtocolError, types::ProtocolMessage};
use async_std::sync::Mutex;
use ng_repo::log::*;
use ng_repo::types::PubKey;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
impl RepoPinStatusReq {
pub fn get_actor(&self) -> Box<dyn EActor> {
Actor::<RepoPinStatusReq, RepoPinStatus>::new_responder()
}
}
impl TryFrom<ProtocolMessage> for RepoPinStatusReq {
type Error = ProtocolError;
fn try_from(msg: ProtocolMessage) -> Result<Self, Self::Error> {
let req: ClientRequestContentV0 = msg.try_into()?;
if let ClientRequestContentV0::RepoPinStatusReq(a) = req {
Ok(a)
} else {
log_debug!("INVALID {:?}", req);
Err(ProtocolError::InvalidValue)
}
}
}
impl From<RepoPinStatusReq> for ProtocolMessage {
fn from(msg: RepoPinStatusReq) -> ProtocolMessage {
let overlay = *msg.overlay();
ProtocolMessage::from_client_request_v0(
ClientRequestContentV0::RepoPinStatusReq(msg),
overlay,
)
}
}
impl TryFrom<ProtocolMessage> for RepoPinStatus {
type Error = ProtocolError;
fn try_from(msg: ProtocolMessage) -> Result<Self, Self::Error> {
let res: ClientResponseContentV0 = msg.try_into()?;
if let ClientResponseContentV0::RepoPinStatus(a) = res {
Ok(a)
} else {
log_debug!("INVALID {:?}", res);
Err(ProtocolError::InvalidValue)
}
}
}
impl From<RepoPinStatus> for ProtocolMessage {
fn from(res: RepoPinStatus) -> ProtocolMessage {
ClientResponseContentV0::RepoPinStatus(res).into()
}
}
impl Actor<'_, RepoPinStatusReq, RepoPinStatus> {}
#[async_trait::async_trait]
impl EActor for Actor<'_, RepoPinStatusReq, RepoPinStatus> {
async fn respond(
&mut self,
msg: ProtocolMessage,
fsm: Arc<Mutex<NoiseFSM>>,
) -> Result<(), ProtocolError> {
let req = RepoPinStatusReq::try_from(msg)?;
let broker = BROKER.read().await;
let res = broker
.get_server_storage()?
.get_repo_pin_status(req.overlay(), req.hash())?;
fsm.lock().await.send(res.into()).await?;
Ok(())
}
}

@ -0,0 +1,113 @@
/*
* Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
* All rights reserved.
* Licensed under the Apache License, Version 2.0
* <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
* or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
* at your option. All files in the project carrying such
* notice may not be copied, modified, or distributed except
* according to those terms.
*/
use crate::broker::{ServerConfig, BROKER};
use crate::connection::NoiseFSM;
use crate::types::*;
use crate::{actor::*, errors::ProtocolError, types::ProtocolMessage};
use async_std::sync::Mutex;
use ng_repo::log::*;
use ng_repo::repo::{BranchInfo, Repo};
use ng_repo::types::*;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
impl TopicSub {
pub fn get_actor(&self) -> Box<dyn EActor> {
Actor::<TopicSub, TopicSubRes>::new_responder()
}
/// only set broker_id if you want to be a publisher
pub fn new(repo: &Repo, branch: &BranchInfo, broker_id: Option<&DirectPeerId>) -> TopicSub {
let (overlay, publisher) = if broker_id.is_some() && branch.topic_priv_key.is_some() {
(
OverlayId::inner_from_store(&repo.store),
Some(PublisherAdvert::new(
branch.topic,
branch.topic_priv_key.to_owned().unwrap(),
*broker_id.unwrap(),
)),
)
} else {
(OverlayId::outer(repo.store.id()), None)
};
TopicSub::V0(TopicSubV0 {
repo_hash: repo.id.into(),
overlay: Some(overlay),
topic: branch.topic,
publisher,
})
}
}
impl TryFrom<ProtocolMessage> for TopicSub {
type Error = ProtocolError;
fn try_from(msg: ProtocolMessage) -> Result<Self, Self::Error> {
let req: ClientRequestContentV0 = msg.try_into()?;
if let ClientRequestContentV0::TopicSub(a) = req {
Ok(a)
} else {
log_debug!("INVALID {:?}", req);
Err(ProtocolError::InvalidValue)
}
}
}
impl From<TopicSub> for ProtocolMessage {
fn from(msg: TopicSub) -> ProtocolMessage {
let overlay = *msg.overlay();
ProtocolMessage::from_client_request_v0(ClientRequestContentV0::TopicSub(msg), overlay)
}
}
impl TryFrom<ProtocolMessage> for TopicSubRes {
type Error = ProtocolError;
fn try_from(msg: ProtocolMessage) -> Result<Self, Self::Error> {
let res: ClientResponseContentV0 = msg.try_into()?;
if let ClientResponseContentV0::TopicSubRes(a) = res {
Ok(a)
} else {
log_debug!("INVALID {:?}", res);
Err(ProtocolError::InvalidValue)
}
}
}
impl From<TopicSubRes> for ProtocolMessage {
fn from(res: TopicSubRes) -> ProtocolMessage {
ClientResponseContentV0::TopicSubRes(res).into()
}
}
impl Actor<'_, TopicSub, TopicSubRes> {}
#[async_trait::async_trait]
impl EActor for Actor<'_, TopicSub, TopicSubRes> {
async fn respond(
&mut self,
msg: ProtocolMessage,
fsm: Arc<Mutex<NoiseFSM>>,
) -> Result<(), ProtocolError> {
let req = TopicSub::try_from(msg)?;
//TODO implement all the server side logic
let broker = BROKER.read().await;
let res = broker.get_server_storage()?.topic_sub(
req.overlay(),
req.hash(),
req.topic(),
req.publisher(),
)?;
fsm.lock().await.send(res.into()).await?;
Ok(())
}
}

@ -26,3 +26,5 @@ pub use list_invitations::*;
pub mod connecting;
pub use connecting::*;
pub mod client;

@ -12,6 +12,7 @@
//! Broker singleton present in every instance of NextGraph (Client, Server, Core node)
use crate::actor::EActor;
use crate::actor::SoS;
use crate::connection::*;
use crate::errors::*;
use crate::server_storage::ServerStorage;
@ -431,7 +432,7 @@ impl<'a> Broker<'a> {
None => {}
}
}
pub fn remove_peer_id(&mut self, peer_id: X25519PrivKey, user: Option<PubKey>) {
fn remove_peer_id(&mut self, peer_id: X25519PrivKey, user: Option<PubKey>) {
let removed = self.peers.remove(&(user, peer_id));
match removed {
Some(info) => match info.connected {
@ -460,10 +461,10 @@ impl<'a> Broker<'a> {
// #[cfg(not(target_arch = "wasm32"))]
// pub fn test_storage(&self, path: PathBuf) {
// use ng_storage_rocksdb::kcv_store::RocksdbKCVStore;
// use ng_storage_rocksdb::kcv_store::RocksdbKCVStorage;
// let key: [u8; 32] = [0; 32];
// let test_storage = RocksdbKCVStore::open(&path, key);
// let test_storage = RocksdbKCVStorage::open(&path, key);
// match test_storage {
// Err(e) => {
// log_debug!("storage error {}", e);
@ -494,9 +495,6 @@ impl<'a> Broker<'a> {
server_storage: None,
disconnections_sender,
disconnections_receiver: Some(disconnections_receiver),
// last_seq_function: None,
// in_memory: true,
// base_path: None,
local_broker: None,
}
}
@ -918,11 +916,31 @@ impl<'a> Broker<'a> {
Ok(())
}
pub async fn request<
A: Into<ProtocolMessage> + std::fmt::Debug + Sync + Send + 'static,
B: TryFrom<ProtocolMessage, Error = ProtocolError> + std::fmt::Debug + Sync + Send + 'static,
>(
&self,
user: &UserId,
remote_peer_id: &DirectPeerId,
msg: A,
) -> Result<SoS<B>, ProtocolError> {
let bpi = self
.peers
.get(&(Some(*user), remote_peer_id.to_dh_slice()))
.ok_or(ProtocolError::InvalidValue)?;
if let PeerConnection::Client(cnx) = &bpi.connected {
cnx.request(msg).await
} else {
Err(ProtocolError::BrokerError)
}
}
pub fn take_disconnections_receiver(&mut self) -> Option<Receiver<String>> {
self.disconnections_receiver.take()
}
pub async fn close_peer_connection_x(&mut self, peer_id: X25519PubKey, user: Option<PubKey>) {
async fn close_peer_connection_x(&mut self, peer_id: X25519PubKey, user: Option<PubKey>) {
if let Some(peer) = self.peers.get_mut(&(user, peer_id)) {
match &mut peer.connected {
PeerConnection::Core(_) => {

@ -85,6 +85,9 @@ pub enum ProtocolError {
EncryptionError,
WhereIsTheMagic,
RepoAlreadyOpened,
False,
InvalidNonce,
} //MAX 949 ProtocolErrors

@ -15,7 +15,7 @@ use crate::{
errors::{ProtocolError, ServerError},
types::*,
};
use ng_repo::types::{PeerId, PubKey};
use ng_repo::types::*;
pub trait ServerStorage: Send + Sync {
fn get_user(&self, user_id: PubKey) -> Result<bool, ProtocolError>;
@ -38,4 +38,26 @@ pub trait ServerStorage: Send + Sync {
fn remove_invitation(&self, invite: [u8; 32]) -> Result<(), ProtocolError>;
fn next_seq_for_peer(&self, peer: &PeerId, seq: u64) -> Result<(), ServerError>;
fn get_repo_pin_status(
&self,
overlay: &OverlayId,
repo: &RepoHash,
) -> Result<RepoPinStatus, ProtocolError>;
fn pin_repo(
&self,
overlay: &OverlayId,
repo: &RepoHash,
ro_topics: &Vec<TopicId>,
rw_topics: &Vec<PublisherAdvert>,
) -> Result<RepoOpened, ProtocolError>;
fn topic_sub(
&self,
overlay: &OverlayId,
repo: &RepoHash,
topic: &TopicId,
publisher: Option<&PublisherAdvert>,
) -> Result<TopicSubRes, ProtocolError>;
}

@ -19,7 +19,7 @@ use crate::WS_PORT_ALTERNATE;
use crate::{actor::EActor, actors::*, errors::ProtocolError};
use core::fmt;
use ng_repo::errors::NgError;
use ng_repo::log::*;
use ng_repo::types::*;
use serde::{Deserialize, Serialize};
use std::{
@ -1238,6 +1238,13 @@ impl OverlayAccess {
Err(NgError::InvalidArgument)
}
}
pub fn overlay_id_for_client_protocol_purpose(&self) -> &OverlayId {
match self {
Self::ReadOnly(ro) => ro,
Self::ReadWrite((inner, outer)) => inner,
Self::WriteOnly(wo) => wo,
}
}
}