moved disconnection handling to the LocalBroker

pull/19/head
Niko PLP 8 months ago
parent c8b7a04ab4
commit 1dbd931b0b
  1. 1
      Cargo.lock
  2. 1
      nextgraph/Cargo.toml
  3. 46
      nextgraph/src/local_broker.rs
  4. 8
      ng-app/src-tauri/src/lib.rs
  5. 2
      ng-app/src/lib/FullLayout.svelte
  6. 129
      ng-app/src/lib/Test.svelte
  7. 48
      ng-app/src/store.ts
  8. 4
      ng-client-ws/src/remote_ws_wasm.rs
  9. 136
      ng-net/src/broker.rs
  10. 51
      ng-sdk-js/src/lib.rs
  11. 4
      ng-verifier/src/request_processor.rs
  12. 6
      ng-verifier/src/types.rs
  13. 158
      ng-verifier/src/verifier.rs

1
Cargo.lock generated

@ -3228,6 +3228,7 @@ dependencies = [
"async-std", "async-std",
"async-trait", "async-trait",
"base64-url", "base64-url",
"futures",
"ng-client-ws", "ng-client-ws",
"ng-net", "ng-net",
"ng-repo", "ng-repo",

@ -32,6 +32,7 @@ async-std = { version = "1.12.0", features = [ "attributes", "unstable" ] }
zeroize = { version = "1.6.0", features = ["zeroize_derive"] } zeroize = { version = "1.6.0", features = ["zeroize_derive"] }
serde_json = "1.0" serde_json = "1.0"
async-trait = "0.1.64" async-trait = "0.1.64"
futures = "0.3.24"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies] [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
ng-storage-rocksdb = { path = "../ng-storage-rocksdb", version = "0.1.0" } ng-storage-rocksdb = { path = "../ng-storage-rocksdb", version = "0.1.0" }

@ -10,6 +10,8 @@
use async_once_cell::OnceCell; use async_once_cell::OnceCell;
use async_std::sync::{Arc, Mutex, RwLock, RwLockReadGuard}; use async_std::sync::{Arc, Mutex, RwLock, RwLockReadGuard};
use core::fmt; use core::fmt;
use futures::channel::mpsc;
use futures::SinkExt;
use ng_net::actor::EActor; use ng_net::actor::EActor;
use ng_net::connection::{ClientConfig, IConnect, NoiseFSM, StartConfig}; use ng_net::connection::{ClientConfig, IConnect, NoiseFSM, StartConfig};
use ng_net::types::{ClientInfo, ClientType, ProtocolMessage}; use ng_net::types::{ClientInfo, ClientType, ProtocolMessage};
@ -386,6 +388,9 @@ struct LocalBroker {
pub opened_sessions_list: Vec<Option<Session>>, pub opened_sessions_list: Vec<Option<Session>>,
tauri_streams: HashMap<String, CancelFn>, tauri_streams: HashMap<String, CancelFn>,
disconnections_sender: Sender<String>,
disconnections_receiver: Option<Receiver<String>>,
} }
impl fmt::Debug for LocalBroker { impl fmt::Debug for LocalBroker {
@ -407,6 +412,12 @@ impl ILocalBroker for LocalBroker {
session.verifier.deliver(event, overlay).await; session.verifier.deliver(event, overlay).await;
} }
} }
async fn user_disconnected(&mut self, user_id: UserId) {
if let Some(session) = self.get_mut_session_for_user(&user_id) {
session.verifier.connection_lost();
let _ = self.disconnections_sender.send(user_id.to_string()).await;
}
}
} }
// this is used if an Actor does a BROKER.local_broker.respond // this is used if an Actor does a BROKER.local_broker.respond
@ -512,7 +523,7 @@ impl LocalBroker {
let session = self.opened_sessions_list[*session as usize] let session = self.opened_sessions_list[*session as usize]
.as_mut() .as_mut()
.ok_or(NgError::SessionNotFound)?; .ok_or(NgError::SessionNotFound)?;
session.verifier.connected_server_id = None; session.verifier.connection_lost();
} }
None => {} None => {}
} }
@ -895,7 +906,7 @@ async fn init_(config: LocalBrokerConfig) -> Result<Arc<RwLock<LocalBroker>>, Ng
} }
} }
}; };
let (disconnections_sender, disconnections_receiver) = mpsc::unbounded::<String>();
let local_broker = LocalBroker { let local_broker = LocalBroker {
config, config,
wallets, wallets,
@ -904,6 +915,8 @@ async fn init_(config: LocalBrokerConfig) -> Result<Arc<RwLock<LocalBroker>>, Ng
opened_sessions: HashMap::new(), opened_sessions: HashMap::new(),
opened_sessions_list: vec![], opened_sessions_list: vec![],
tauri_streams: HashMap::new(), tauri_streams: HashMap::new(),
disconnections_sender,
disconnections_receiver: Some(disconnections_receiver),
}; };
//log_debug!("{:?}", &local_broker); //log_debug!("{:?}", &local_broker);
@ -948,6 +961,7 @@ pub async fn tauri_stream_cancel(stream_id: String) -> Result<(), NgError> {
broker.tauri_stream_cancel(stream_id); broker.tauri_stream_cancel(stream_id);
Ok(()) Ok(())
} }
/// Initialize the configuration of your local broker /// Initialize the configuration of your local broker
/// ///
/// , by passing in a function (or closure) that returns a `LocalBrokerConfig`. /// , by passing in a function (or closure) that returns a `LocalBrokerConfig`.
@ -1383,20 +1397,11 @@ pub async fn user_connect_with_device_info(
)); ));
} }
if tried.is_some() && tried.as_ref().unwrap().3.is_none() { if tried.is_some() && tried.as_ref().unwrap().3.is_none() {
session.verifier.connected_server_id = Some(server_key); if let Err(e) =
// successful. we can stop here session.verifier.connection_opened(server_key).await
{
// load verifier from remote connection (if not RocksDb type, or after import on tauri)
if let Err(e) = session.verifier.bootstrap().await {
session.verifier.connected_server_id = None;
Broker::close_all_connections().await; Broker::close_all_connections().await;
tried.as_mut().unwrap().3 = Some(e.to_string()); tried.as_mut().unwrap().3 = Some(e.to_string());
} else {
// we can send outbox now that the verifier is loaded
let res = session.verifier.send_outbox().await;
log_info!("SENDING EVENTS FROM OUTBOX RETURNED: {:?}", res);
//log_info!("VERIFIER DUMP {:?}", session.verifier);
} }
break; break;
@ -1591,6 +1596,19 @@ pub async fn personal_site_store(session_id: u64, store: SiteStoreType) -> Resul
} }
} }
#[doc(hidden)]
pub async fn take_disconnections_receiver() -> Result<Receiver<String>, NgError> {
let mut broker = match LOCAL_BROKER.get() {
None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized),
Some(Ok(broker)) => broker.write().await,
};
broker
.disconnections_receiver
.take()
.ok_or(NgError::BrokerError)
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;

@ -335,14 +335,12 @@ async fn cancel_stream(stream_id: &str) -> Result<(), String> {
} }
#[tauri::command(rename_all = "snake_case")] #[tauri::command(rename_all = "snake_case")]
async fn disconnections_subscribe(app: tauri::AppHandle) -> Result<(), ()> { async fn disconnections_subscribe(app: tauri::AppHandle) -> Result<(), String> {
let main_window = app.get_window("main").unwrap(); let main_window = app.get_window("main").unwrap();
let reader = BROKER let reader = nextgraph::local_broker::take_disconnections_receiver()
.write()
.await .await
.take_disconnections_receiver() .map_err(|e: NgError| e.to_string())?;
.ok_or(())?;
async fn inner_task( async fn inner_task(
mut reader: Receiver<String>, mut reader: Receiver<String>,

@ -62,6 +62,7 @@
"flex items-center p-2 text-base font-normal text-gray-900 rounded-lg dark:text-white hover:bg-gray-200 dark:hover:bg-gray-700"; "flex items-center p-2 text-base font-normal text-gray-900 rounded-lg dark:text-white hover:bg-gray-200 dark:hover:bg-gray-700";
</script> </script>
<svelte:window bind:innerWidth={width} />
{#if mobile} {#if mobile}
<div class="full-layout"> <div class="full-layout">
<main class="pb-14" bind:this={top}> <main class="pb-14" bind:this={top}>
@ -185,7 +186,6 @@
</main> </main>
</div> </div>
{/if} {/if}
<svelte:window bind:innerWidth={width} />
<style> <style>
.full-layout { .full-layout {

@ -11,13 +11,24 @@
<script lang="ts"> <script lang="ts">
import ng from "../api"; import ng from "../api";
import { branch_subs, active_session } from "../store"; import {
branch_subs,
active_session,
cannot_load_offline,
online,
} from "../store";
import { link } from "svelte-spa-router"; import { link } from "svelte-spa-router";
import { onMount, onDestroy } from "svelte";
import { Button } from "flowbite-svelte";
let is_tauri = import.meta.env.TAURI_PLATFORM;
let files = branch_subs("ok"); let files = branch_subs("ok");
let img_map = {}; let img_map = {};
onMount(() => {});
async function get_img(ref) { async function get_img(ref) {
if (!ref) return false; if (!ref) return false;
let cache = img_map[ref.nuri]; let cache = img_map[ref.nuri];
@ -189,56 +200,74 @@
</script> </script>
<div> <div>
<div class="row mt-2"> {#if $cannot_load_offline}
<!-- <a use:link href="/"> <div class="row p-4">
<p>
You are offline and using the web app. You need to connect to the broker
at least once before you can start using the app locally because the web
app does not keep a local copy of your documents.<br /><br />
Once connected, if you lose connectivity again, you will be able to have
limited access to some functionalities. Sending binary files won't be possible,
because the limit of local storage in your browser is around 5MB.<br
/><br />
All those limitations will be lifted once the "UserStorage for Web" feature
will be released. Stay tuned! <br /><br />
Check your connection status in the <a href="#/user">user panel</a>.
</p>
</div>
{:else}
<div class="row mt-2">
<!-- <a use:link href="/">
<button tabindex="-1" class=" mr-5 select-none"> Back home </button> <button tabindex="-1" class=" mr-5 select-none"> Back home </button>
</a> --> </a> -->
<button <Button
type="button" disabled={!$online && !is_tauri}
on:click={() => { type="button"
fileinput.click(); on:click={() => {
}} fileinput.click();
class="text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mr-2 mb-2" }}
> class="text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mr-2 mb-2"
<svg
class="w-8 h-8 mr-2 -ml-1"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
> >
<path <svg
stroke-linecap="round" class="w-8 h-8 mr-2 -ml-1"
stroke-linejoin="round" fill="none"
d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 001.5-1.5V6a1.5 1.5 0 00-1.5-1.5H3.75A1.5 1.5 0 002.25 6v12a1.5 1.5 0 001.5 1.5zm10.5-11.25h.008v.008h-.008V8.25zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z" stroke="currentColor"
/> stroke-width="1.5"
</svg> viewBox="0 0 24 24"
Add image xmlns="http://www.w3.org/2000/svg"
</button> aria-hidden="true"
<input >
style="display:none" <path
type="file" stroke-linecap="round"
accept=".jpg, .jpeg, .png" stroke-linejoin="round"
on:change={(e) => onFileSelected(e)} d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 001.5-1.5V6a1.5 1.5 0 00-1.5-1.5H3.75A1.5 1.5 0 002.25 6v12a1.5 1.5 0 001.5 1.5zm10.5-11.25h.008v.008h-.008V8.25zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z"
bind:this={fileinput} />
/> </svg>
</div> Add image
</Button>
{#await files.load()} <input
<p>Currently loading...</p> style="display:none"
{:then} type="file"
{#each $files as file} accept=".jpg, .jpeg, .png"
<p> on:change={(e) => onFileSelected(e)}
{file.V0.File.name}<br /> bind:this={fileinput}
did:ng{file.V0.File.nuri} />
{#await get_img(file.V0.File) then url} </div>
{#if url}
<img src={url} /> {#await files.load()}
{/if} <p>Currently loading...</p>
{/await} {:then}
</p> {#each $files as file}
{/each} <p>
{/await} {file.V0.File.name}<br />
did:ng{file.V0.File.nuri}
{#await get_img(file.V0.File) then url}
{#if url}
<img src={url} />
{/if}
{/await}
</p>
{/each}
{/await}
{/if}
</div> </div>

@ -35,15 +35,28 @@ export const online = derived(connections,($connections) => {
}); });
return true; } return true; }
else if ($connections[cnx].error=="ConnectionError" && !$connections[cnx].connecting && next_reconnect==null) { else if ($connections[cnx].error=="ConnectionError" && !$connections[cnx].connecting && next_reconnect==null) {
console.log("will try reconnect in 1 min"); console.log("will try reconnect in 20 sec");
next_reconnect = setTimeout(async ()=> { next_reconnect = setTimeout(async ()=> {
await reconnect(); await reconnect();
},60000); },20000);
} }
} }
return false; return false;
}); });
export const cannot_load_offline = writable(false);
if (!get(online) && !import.meta.env.TAURI_PLATFORM) {
cannot_load_offline.set(true);
let unsubscribe = online.subscribe(async (value) => {
if (value) {
cannot_load_offline.set(false);
unsubscribe();
}
});
}
export const has_wallets = derived(wallets,($wallets) => Object.keys($wallets).length); export const has_wallets = derived(wallets,($wallets) => Object.keys($wallets).length);
@ -105,6 +118,8 @@ export const reconnect = async function() {
get(active_session).user, get(active_session).user,
location.href location.href
)); ));
}catch (e) { }catch (e) {
console.error(e) console.error(e)
} }
@ -159,13 +174,15 @@ export const branch_subs = function(nura) {
return { return {
load: async () => { load: async () => {
console.log("load upper");
let already_subscribed = all_branches[nura]; let already_subscribed = all_branches[nura];
if (!already_subscribed) return; if (!already_subscribed) return;
if (already_subscribed.load) { if (already_subscribed.load) {
console.log("doing the load");
let loader = already_subscribed.load; let loader = already_subscribed.load;
already_subscribed.load = undefined; already_subscribed.load = undefined;
// already_subscribed.load2 = loader;
await loader(); await loader();
} }
}, },
subscribe: (run, invalid) => { subscribe: (run, invalid) => {
@ -178,15 +195,18 @@ export const branch_subs = function(nura) {
already_subscribed = { already_subscribed = {
load: async () => { load: async () => {
try { try {
console.log("load down");
let session = get(active_session); let session = get(active_session);
if (!session) { if (!session) {
console.error("no session"); console.error("no session");
return; return;
} }
await unsub(); unsub();
unsub = () => {};
set([]);
unsub = await ng.app_request_stream(session.session_id, await ng.doc_fetch_private_subscribe(), unsub = await ng.app_request_stream(session.session_id, await ng.doc_fetch_private_subscribe(),
async (commit) => { async (commit) => {
//console.log("GOT APP RESPONSE", commit); console.log("GOT APP RESPONSE", commit);
update( (old) => {old.unshift(commit); return old;} ) update( (old) => {old.unshift(commit); return old;} )
}); });
} }
@ -198,14 +218,17 @@ export const branch_subs = function(nura) {
}, },
increase: () => { increase: () => {
count += 1; count += 1;
console.log("increase sub to",count);
return readonly({subscribe}); return readonly({subscribe});
}, },
decrease: async () => { decrease: () => {
count -= 1; count -= 1;
if (count == 0) { console.log("decrease sub to",count);
await unsub(); // if (count == 0) {
delete all_branches[nura]; // unsub();
} // console.log("removed sub");
// delete all_branches[nura];
// }
}, },
} }
all_branches[nura] = already_subscribed; all_branches[nura] = already_subscribed;
@ -213,9 +236,10 @@ export const branch_subs = function(nura) {
let new_store = already_subscribed.increase(); let new_store = already_subscribed.increase();
let read_unsub = new_store.subscribe(run, invalid); let read_unsub = new_store.subscribe(run, invalid);
return async () => { return () => {
read_unsub(); read_unsub();
await already_subscribed.decrease(); console.log("callback unsub");
already_subscribed.decrease();
} }
} }

@ -107,7 +107,7 @@ async fn ws_loop(
select! { select! {
r = stream.next().fuse() => match r { r = stream.next().fuse() => match r {
Some(msg) => { Some(msg) => {
log_debug!("GOT MESSAGE {:?}", msg); //log_debug!("GOT MESSAGE {:?}", msg);
if let WsMessage::Binary(b) = msg { if let WsMessage::Binary(b) = msg {
receiver.send(ConnectionCommand::Msg(serde_bare::from_slice::<ProtocolMessage>(&b)?)).await receiver.send(ConnectionCommand::Msg(serde_bare::from_slice::<ProtocolMessage>(&b)?)).await
.map_err(|_e| NetError::IoError)?; .map_err(|_e| NetError::IoError)?;
@ -120,7 +120,7 @@ async fn ws_loop(
}, },
s = sender.next().fuse() => match s { s = sender.next().fuse() => match s {
Some(msg) => { Some(msg) => {
log_debug!("SENDING MESSAGE {:?}", msg); //log_debug!("SENDING MESSAGE {:?}", msg);
match msg { match msg {
ConnectionCommand::Msg(m) => { ConnectionCommand::Msg(m) => {

@ -68,11 +68,13 @@ pub struct ServerConfig {
#[async_trait::async_trait] #[async_trait::async_trait]
pub trait ILocalBroker: Send + Sync + EActor { pub trait ILocalBroker: Send + Sync + EActor {
async fn deliver(&mut self, event: Event, overlay: OverlayId, user: UserId); async fn deliver(&mut self, event: Event, overlay: OverlayId, user: UserId);
async fn user_disconnected(&mut self, user_id: UserId);
} }
pub static BROKER: Lazy<Arc<RwLock<Broker>>> = Lazy::new(|| Arc::new(RwLock::new(Broker::new()))); pub static BROKER: Lazy<Arc<RwLock<Broker>>> = Lazy::new(|| Arc::new(RwLock::new(Broker::new())));
pub struct Broker<'a> { pub struct Broker {
direct_connections: HashMap<BindAddress, DirectConnection>, direct_connections: HashMap<BindAddress, DirectConnection>,
/// tuple of optional userId and peer key in montgomery form. userId is always None on the server side. /// tuple of optional userId and peer key in montgomery form. userId is always None on the server side.
peers: HashMap<(Option<PubKey>, X25519PubKey), BrokerPeerInfo>, peers: HashMap<(Option<PubKey>, X25519PubKey), BrokerPeerInfo>,
@ -85,17 +87,15 @@ pub struct Broker<'a> {
shutdown: Option<Receiver<ProtocolError>>, shutdown: Option<Receiver<ProtocolError>>,
shutdown_sender: Sender<ProtocolError>, shutdown_sender: Sender<ProtocolError>,
closing: bool, closing: bool,
server_broker: Option<Box<dyn IServerBroker + Send + Sync + 'a>>, server_broker: Option<Box<dyn IServerBroker + Send + Sync>>,
disconnections_sender: Sender<String>,
disconnections_receiver: Option<Receiver<String>>,
//local_broker: Option<Box<dyn ILocalBroker + Send + Sync + 'a>>, //local_broker: Option<Box<dyn ILocalBroker + Send + Sync + 'a>>,
local_broker: Option<Arc<RwLock<dyn ILocalBroker + 'a>>>, local_broker: Option<Arc<RwLock<dyn ILocalBroker>>>,
users_peers: HashMap<UserId, HashSet<X25519PubKey>>, users_peers: HashMap<UserId, HashSet<X25519PubKey>>,
} }
impl<'a> Broker<'a> { impl Broker {
// pub fn init_local_broker( // pub fn init_local_broker(
// &mut self, // &mut self,
// base_path: Option<PathBuf>, // base_path: Option<PathBuf>,
@ -132,12 +132,12 @@ impl<'a> Broker<'a> {
.ok_or(ProtocolError::BrokerError) .ok_or(ProtocolError::BrokerError)
} }
pub fn set_server_broker(&mut self, broker: impl IServerBroker + 'a) { pub fn set_server_broker(&mut self, broker: impl IServerBroker + 'static) {
//log_debug!("set_server_broker"); //log_debug!("set_server_broker");
self.server_broker = Some(Box::new(broker)); self.server_broker = Some(Box::new(broker));
} }
pub fn set_local_broker(&mut self, broker: Arc<RwLock<dyn ILocalBroker + 'a>>) { pub fn set_local_broker(&mut self, broker: Arc<RwLock<dyn ILocalBroker>>) {
//log_debug!("set_local_broker"); //log_debug!("set_local_broker");
self.local_broker = Some(broker); self.local_broker = Some(broker);
} }
@ -166,7 +166,7 @@ impl<'a> Broker<'a> {
pub fn get_server_broker( pub fn get_server_broker(
&self, &self,
) -> Result<&Box<dyn IServerBroker + Send + Sync + 'a>, ProtocolError> { ) -> Result<&Box<dyn IServerBroker + Send + Sync>, ProtocolError> {
//log_debug!("GET STORAGE {:?}", self.server_storage); //log_debug!("GET STORAGE {:?}", self.server_storage);
self.server_broker self.server_broker
.as_ref() .as_ref()
@ -175,7 +175,7 @@ impl<'a> Broker<'a> {
pub fn get_server_broker_mut( pub fn get_server_broker_mut(
&mut self, &mut self,
) -> Result<&mut Box<dyn IServerBroker + Send + Sync + 'a>, ProtocolError> { ) -> Result<&mut Box<dyn IServerBroker + Send + Sync>, ProtocolError> {
//log_debug!("GET STORAGE {:?}", self.server_storage); //log_debug!("GET STORAGE {:?}", self.server_storage);
self.server_broker self.server_broker
.as_mut() .as_mut()
@ -183,9 +183,11 @@ impl<'a> Broker<'a> {
} }
//Option<Arc<RwLock<dyn ILocalBroker>>>, //Option<Arc<RwLock<dyn ILocalBroker>>>,
pub fn get_local_broker(&self) -> Result<Arc<RwLock<dyn ILocalBroker + 'a>>, NgError> { pub(crate) fn get_local_broker(&self) -> Result<Arc<RwLock<dyn ILocalBroker>>, ProtocolError> {
Ok(Arc::clone( Ok(Arc::clone(
self.local_broker.as_ref().ok_or(NgError::BrokerError)?, self.local_broker
.as_ref()
.ok_or(ProtocolError::BrokerError)?,
)) ))
} }
@ -297,102 +299,6 @@ impl<'a> Broker<'a> {
} }
} }
pub async fn get_block_from_store_with_block_id(
&mut self,
nuri: String,
id: BlockId,
include_children: bool,
) -> Result<Receiver<Block>, ProtocolError> {
// TODO
let (mut tx, rx) = mpsc::unbounded::<Block>();
//log_debug!("cur {}", std::env::current_dir().unwrap().display());
//Err(ProtocolError::AccessDenied)
// let f = std::fs::File::open(
// "../ng-repo/tests/e4e4b57524ce29df826055c368894e912ab03af46f61f6270b4c8796bc6f4221.ng",
// )
// .expect("open of block.ng");
// let mut reader = BufReader::new(f);
// let mut block_buffer: Vec<u8> = Vec::new();
// reader
// .read_to_end(&mut block_buffer)
// .expect("read of test.ng");
let block = serde_bare::from_slice::<Block>(&crate::tests::file::TEST).unwrap();
tx.send(block).await;
Ok(rx)
}
pub async fn get_object_from_store_with_object_ref(
&mut self,
nuri: String,
obj_ref: ObjectRef,
) -> Result<ObjectContent, ProtocolError> {
unimplemented!();
// let blockstream = self
// .get_block_from_store_with_block_id(nuri, obj_ref.id, true)
// .await?;
// let store = Box::new(HashMapBlockStorage::from_block_stream(blockstream).await);
// Object::load(obj_ref.id, Some(obj_ref.key), &store)
// .map_err(|e| match e {
// ObjectParseError::MissingBlocks(_missing) => ProtocolError::MissingBlocks,
// _ => ProtocolError::ObjectParseError,
// })?
// .content()
// .map_err(|_| ProtocolError::ObjectParseError)
}
// pub async fn doc_sync_branch(&mut self, anuri: String) -> (Receiver<Commit>, Sender<Commit>) {
// let obj_ref = ObjectRef {
// id: ObjectId::Blake3Digest32([
// 228, 228, 181, 117, 36, 206, 41, 223, 130, 96, 85, 195, 104, 137, 78, 145, 42, 176,
// 58, 244, 111, 97, 246, 39, 11, 76, 135, 150, 188, 111, 66, 33,
// ]),
// key: SymKey::ChaCha20Key([
// 100, 243, 39, 242, 203, 131, 102, 50, 9, 54, 248, 113, 4, 160, 28, 45, 73, 56, 217,
// 112, 95, 150, 144, 137, 9, 57, 106, 5, 39, 202, 146, 94,
// ]),
// };
// let refs = vec![obj_ref.clone()];
// let metadata = vec![5u8; 55];
// let (member_privkey, member_pubkey) = generate_keypair();
// let overlay = OverlayId::nil();
// let commit = Commit::new(
// &member_privkey,
// &member_pubkey,
// overlay,
// PubKey::nil(),
// QuorumType::NoSigning,
// vec![],
// vec![],
// vec![],
// vec![],
// refs,
// vec![],
// metadata,
// obj_ref.clone(),
// )
// .unwrap();
// let (tx, rx) = mpsc::unbounded::<Commit>();
// async fn send(mut tx: Sender<Commit>, commit: Commit) -> ResultSend<()> {
// while let Ok(_) = tx.send(commit.clone()).await {
// log_debug!("sending");
// sleep!(std::time::Duration::from_secs(3));
// }
// log_debug!("end of sending");
// Ok(())
// }
// spawn_and_log_error(send(tx.clone(), commit));
// (rx, tx.clone())
// }
pub fn reconnecting(&mut self, peer_id: X25519PrivKey, user: Option<PubKey>) { pub fn reconnecting(&mut self, peer_id: X25519PrivKey, user: Option<PubKey>) {
let peerinfo = self.peers.get_mut(&(user, peer_id)); let peerinfo = self.peers.get_mut(&(user, peer_id));
match peerinfo { match peerinfo {
@ -466,7 +372,7 @@ impl<'a> Broker<'a> {
let (shutdown_sender, shutdown_receiver) = mpsc::unbounded::<ProtocolError>(); let (shutdown_sender, shutdown_receiver) = mpsc::unbounded::<ProtocolError>();
let mut random_buf = [0u8; 4]; let mut random_buf = [0u8; 4];
getrandom::getrandom(&mut random_buf).unwrap(); getrandom::getrandom(&mut random_buf).unwrap();
let (disconnections_sender, disconnections_receiver) = mpsc::unbounded::<String>();
Broker { Broker {
anonymous_connections: HashMap::new(), anonymous_connections: HashMap::new(),
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@ -479,8 +385,6 @@ impl<'a> Broker<'a> {
peers: HashMap::new(), peers: HashMap::new(),
closing: false, closing: false,
server_broker: None, server_broker: None,
disconnections_sender,
disconnections_receiver: Some(disconnections_receiver),
local_broker: None, local_broker: None,
users_peers: HashMap::new(), users_peers: HashMap::new(),
} }
@ -892,7 +796,7 @@ impl<'a> Broker<'a> {
peer_pubkey: PubKey, peer_pubkey: PubKey,
remote_peer_id: [u8; 32], remote_peer_id: [u8; 32],
config: StartConfig, config: StartConfig,
mut disconnections_sender: Sender<String>, local_broker: Arc<async_std::sync::RwLock<dyn ILocalBroker>>,
) -> ResultSend<()> { ) -> ResultSend<()> {
async move { async move {
let res = join.next().await; let res = join.next().await;
@ -917,7 +821,7 @@ impl<'a> Broker<'a> {
// if all attempts fail : // if all attempts fail :
if let Some(user) = config.get_user() { if let Some(user) = config.get_user() {
disconnections_sender.send(user.to_string()).await; local_broker.write().await.user_disconnected(user).await;
} }
} else { } else {
log_info!("REMOVED"); log_info!("REMOVED");
@ -938,7 +842,7 @@ impl<'a> Broker<'a> {
peer_pubk, peer_pubk,
*remote_peer_id_dh.slice(), *remote_peer_id_dh.slice(),
config, config,
self.disconnections_sender.clone(), self.get_local_broker()?,
)); ));
Ok(()) Ok(())
} }
@ -1007,10 +911,6 @@ impl<'a> Broker<'a> {
Ok(()) Ok(())
} }
pub fn take_disconnections_receiver(&mut self) -> Option<Receiver<String>> {
self.disconnections_receiver.take()
}
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)) { if let Some(peer) = self.peers.get_mut(&(user, peer_id)) {
match &mut peer.connected { match &mut peer.connected {

@ -340,7 +340,6 @@ pub async fn wallet_read_file(js_file: JsValue) -> Result<JsValue, String> {
#[wasm_bindgen] #[wasm_bindgen]
pub async fn wallet_was_opened( pub async fn wallet_was_opened(
js_opened_wallet: JsValue, //SensitiveWallet js_opened_wallet: JsValue, //SensitiveWallet
in_memory: bool,
) -> Result<JsValue, String> { ) -> Result<JsValue, String> {
let mut opened_wallet = serde_wasm_bindgen::from_value::<SensitiveWallet>(js_opened_wallet) let mut opened_wallet = serde_wasm_bindgen::from_value::<SensitiveWallet>(js_opened_wallet)
.map_err(|_| "Deserialization error of SensitiveWallet".to_string())?; .map_err(|_| "Deserialization error of SensitiveWallet".to_string())?;
@ -523,16 +522,22 @@ pub async fn app_request_stream(
//let xx = JsValue::from(json!(commit).to_string()); //let xx = JsValue::from(json!(commit).to_string());
//let _ = callback.call1(&this, &xx); //let _ = callback.call1(&this, &xx);
let this = JsValue::null(); let this = JsValue::null();
let jsval: JsValue = callback.call1(&this, &xx).unwrap(); match callback.call1(&this, &xx) {
let promise_res: Result<js_sys::Promise, JsValue> = jsval.dyn_into(); Ok(jsval) => {
match promise_res { let promise_res: Result<js_sys::Promise, JsValue> = jsval.dyn_into();
Ok(promise) => { match promise_res {
JsFuture::from(promise).await; Ok(promise) => {
JsFuture::from(promise).await;
}
Err(_) => {}
}
}
Err(e) => {
log_err!("JS callback for app_request_stream failed with {:?}", e);
} }
Err(_) => {}
} }
} }
log_debug!("END OF LOOP"); log_info!("END OF LOOP");
Ok(()) Ok(())
} }
@ -619,19 +624,16 @@ pub async fn doc_fetch_private_subscribe() -> Result<JsValue, String> {
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
#[wasm_bindgen] #[wasm_bindgen]
pub async fn disconnections_subscribe(callback: &js_sys::Function) -> Result<JsValue, JsValue> { pub async fn disconnections_subscribe(callback: &js_sys::Function) -> Result<JsValue, JsValue> {
init_local_broker_with_lazy(&INIT_LOCAL_BROKER).await;
let vec: Vec<u8> = vec![2; 10]; let vec: Vec<u8> = vec![2; 10];
let view = unsafe { Uint8Array::view(&vec) }; let view = unsafe { Uint8Array::view(&vec) };
let x = JsValue::from(Uint8Array::new(view.as_ref())); let x = JsValue::from(Uint8Array::new(view.as_ref()));
let mut reader; let mut reader;
{ {
reader = BROKER reader = nextgraph::local_broker::take_disconnections_receiver()
.write()
.await .await
.take_disconnections_receiver() .map_err(|e: NgError| false)?;
.ok_or(false)?;
} }
async fn inner_task( async fn inner_task(
mut reader: Receiver<String>, mut reader: Receiver<String>,
callback: js_sys::Function, callback: js_sys::Function,
@ -639,13 +641,22 @@ pub async fn disconnections_subscribe(callback: &js_sys::Function) -> Result<JsV
while let Some(user_id) = reader.next().await { while let Some(user_id) = reader.next().await {
let this = JsValue::null(); let this = JsValue::null();
let xx = serde_wasm_bindgen::to_value(&user_id).unwrap(); let xx = serde_wasm_bindgen::to_value(&user_id).unwrap();
let jsval: JsValue = callback.call1(&this, &xx).unwrap(); match callback.call1(&this, &xx) {
let promise_res: Result<js_sys::Promise, JsValue> = jsval.dyn_into(); Ok(jsval) => {
match promise_res { let promise_res: Result<js_sys::Promise, JsValue> = jsval.dyn_into();
Ok(promise) => { match promise_res {
JsFuture::from(promise).await; Ok(promise) => {
JsFuture::from(promise).await;
}
Err(_) => {}
}
}
Err(e) => {
log_err!(
"JS callback for disconnections_subscribe failed with {:?}",
e
);
} }
Err(_) => {}
} }
} }
log_debug!("END OF disconnections reader"); log_debug!("END OF disconnections reader");

@ -41,7 +41,9 @@ impl AppRequestCommandV0 {
AppFetchContentV0::Subscribe => { AppFetchContentV0::Subscribe => {
let (_, branch_id, _) = let (_, branch_id, _) =
Self::open_for_target(verifier, &nuri.target, false).await?; Self::open_for_target(verifier, &nuri.target, false).await?;
Ok(verifier.create_branch_subscription(branch_id).await?) Ok(verifier
.create_branch_subscription(branch_id, false)
.await?)
} }
_ => unimplemented!(), _ => unimplemented!(),
}, },

@ -172,10 +172,10 @@ pub enum AppFetchContentV0 {
impl AppFetchContentV0 { impl AppFetchContentV0 {
pub fn get_or_subscribe(subscribe: bool) -> Self { pub fn get_or_subscribe(subscribe: bool) -> Self {
if subscribe { if !subscribe {
AppFetchContentV0::Subscribe
} else {
AppFetchContentV0::Get AppFetchContentV0::Get
} else {
AppFetchContentV0::Subscribe
} }
} }
} }

@ -154,19 +154,25 @@ impl Verifier {
} }
pub(crate) async fn push_app_response(&mut self, branch: &BranchId, response: AppResponse) { pub(crate) async fn push_app_response(&mut self, branch: &BranchId, response: AppResponse) {
// log_info!( log_info!(
// "push_app_response {} {:?}", "push_app_response {} {:?}",
// branch, branch,
// self.branch_subscriptions self.branch_subscriptions
// ); );
if let Some(sender) = self.branch_subscriptions.get_mut(branch) { if let Some(sender) = self.branch_subscriptions.get_mut(branch) {
let _ = sender.send(response).await; if sender.is_closed() {
log_info!("closed so removed");
self.branch_subscriptions.remove(branch);
} else {
let _ = sender.send(response).await;
}
} }
} }
pub(crate) async fn create_branch_subscription( pub(crate) async fn create_branch_subscription(
&mut self, &mut self,
branch: BranchId, branch: BranchId,
resub: bool,
) -> Result<(Receiver<AppResponse>, CancelFn), VerifierError> { ) -> Result<(Receiver<AppResponse>, CancelFn), VerifierError> {
// async fn send(mut tx: Sender<AppResponse>, msg: AppResponse) -> ResultSend<()> { // async fn send(mut tx: Sender<AppResponse>, msg: AppResponse) -> ResultSend<()> {
// while let Ok(_) = tx.send(msg.clone()).await { // while let Ok(_) = tx.send(msg.clone()).await {
@ -179,24 +185,33 @@ impl Verifier {
// spawn_and_log_error(send(tx.clone(), commit)); // spawn_and_log_error(send(tx.clone(), commit));
//log_info!("#### create_branch_subscription {}", branch); //log_info!("#### create_branch_subscription {}", branch);
let (tx, rx) = mpsc::unbounded::<AppResponse>(); let (tx, rx) = mpsc::unbounded::<AppResponse>();
log_info!("SUBSCRIBE");
if let Some(returned) = self.branch_subscriptions.insert(branch, tx.clone()) { if let Some(returned) = self.branch_subscriptions.insert(branch, tx.clone()) {
log_info!("RESUBSCRIBE");
if !returned.is_closed() { if !returned.is_closed() {
return Err(VerifierError::DoubleBranchSubscription); log_info!("FORCE CLOSE");
returned.close_channel();
//return Err(VerifierError::DoubleBranchSubscription);
} }
} }
//let tx = self.branch_subscriptions.entry(branch).or_insert_with(|| {}); if !resub {
for file in self //let tx = self.branch_subscriptions.entry(branch).or_insert_with(|| {});
.user_storage for file in self
.as_ref() .user_storage
.unwrap() .as_ref()
.branch_get_all_files(&branch)? .unwrap()
{ .branch_get_all_files(&branch)?
self.push_app_response(&branch, AppResponse::V0(AppResponseV0::File(file))) {
.await; self.push_app_response(&branch, AppResponse::V0(AppResponseV0::File(file)))
.await;
}
} }
let fnonce = Box::new(move || { let fnonce = Box::new(move || {
tx.close_channel(); log_info!("CLOSE_CHANNEL");
if !tx.is_closed() {
tx.close_channel();
}
}); });
Ok((rx, fnonce)) Ok((rx, fnonce))
} }
@ -729,18 +744,59 @@ impl Verifier {
Ok(()) Ok(())
} }
pub(crate) async fn open_branch<'a>( pub fn connection_lost(&mut self) {
self.connected_server_id = None;
// for (_, repo) in self.repos.iter_mut() {
// repo.opened_branches = HashMap::new();
// }
}
pub async fn connection_opened(&mut self, peer: DirectPeerId) -> Result<(), NgError> {
self.connected_server_id = Some(peer);
if let Err(e) = self.bootstrap().await {
self.connected_server_id = None;
return Err(e);
}
let res = self.send_outbox().await;
log_info!("SENDING EVENTS FROM OUTBOX RETURNED: {:?}", res);
let mut branches = vec![];
{
for (id, repo) in self.repos.iter() {
for (branch, publisher) in repo.opened_branches.iter() {
branches.push((*id, *branch, *publisher));
}
}
}
let user = self.config.user_priv_key.to_pub();
let broker = BROKER.read().await;
for (repo, branch, publisher) in branches {
let _ = self
.open_branch_(&repo, &branch, publisher, &broker, &user, &peer, true)
.await;
// discarding error.
}
Ok(())
}
pub(crate) async fn open_branch(
&mut self, &mut self,
repo_id: &RepoId, repo_id: &RepoId,
branch: &BranchId, branch: &BranchId,
as_publisher: bool, as_publisher: bool,
) -> Result<(), NgError> { ) -> Result<(), NgError> {
let remote = match self.connected_server_id.as_ref() {
Some(r) => r.clone(),
None => {
let repo = self.repos.get_mut(repo_id).ok_or(NgError::RepoNotFound)?;
repo.opened_branches.insert(*branch, as_publisher);
return Ok(());
}
};
let user = self.config.user_priv_key.to_pub(); let user = self.config.user_priv_key.to_pub();
let remote = self
.connected_server_id
.as_ref()
.ok_or(NgError::NotConnected)?
.clone();
self.open_branch_( self.open_branch_(
repo_id, repo_id,
branch, branch,
@ -748,6 +804,7 @@ impl Verifier {
&BROKER.read().await, &BROKER.read().await,
&user, &user,
&remote, &remote,
false,
) )
.await .await
} }
@ -757,7 +814,10 @@ impl Verifier {
let broker = BROKER.read().await; let broker = BROKER.read().await;
let user = self.config.user_priv_key.to_pub(); let user = self.config.user_priv_key.to_pub();
let remote = self.connected_server_id.to_owned().unwrap(); let remote = self
.connected_server_id
.to_owned()
.ok_or(NgError::NotConnected)?;
let msg = BlocksPut::V0(BlocksPutV0 { let msg = BlocksPut::V0(BlocksPutV0 {
blocks, blocks,
@ -776,7 +836,10 @@ impl Verifier {
let broker = BROKER.read().await; let broker = BROKER.read().await;
let user = self.config.user_priv_key.to_pub(); let user = self.config.user_priv_key.to_pub();
let remote = self.connected_server_id.to_owned().unwrap(); let remote = self
.connected_server_id
.to_owned()
.ok_or(NgError::NotConnected)?;
let msg = BlocksExist::V0(BlocksExistV0 { let msg = BlocksExist::V0(BlocksExistV0 {
blocks, blocks,
@ -797,16 +860,21 @@ impl Verifier {
repo_id: &RepoId, repo_id: &RepoId,
branch: &BranchId, branch: &BranchId,
as_publisher: bool, as_publisher: bool,
broker: &RwLockReadGuard<'a, Broker<'a>>, broker: &RwLockReadGuard<'static, Broker>,
user: &UserId, user: &UserId,
remote: &DirectPeerId, remote: &DirectPeerId,
force: bool,
) -> Result<(), NgError> { ) -> Result<(), NgError> {
let (need_open, mut need_sub, overlay) = { let (need_open, mut need_sub, overlay) = {
let repo = self.repos.get(repo_id).ok_or(NgError::RepoNotFound)?; let repo = self.repos.get(repo_id).ok_or(NgError::RepoNotFound)?;
let overlay = repo.store.overlay_for_read_on_client_protocol(); let overlay = repo.store.overlay_for_read_on_client_protocol();
match repo.opened_branches.get(branch) { if force {
Some(val) => (false, as_publisher && !val, overlay), (true, true, overlay)
None => (repo.opened_branches.len() == 0, true, overlay), } else {
match repo.opened_branches.get(branch) {
Some(val) => (false, as_publisher && !val, overlay),
None => (repo.opened_branches.len() == 0, true, overlay),
}
} }
}; };
//log_info!("need_open {} need_sub {}", need_open, need_sub); //log_info!("need_open {} need_sub {}", need_open, need_sub);
@ -922,10 +990,10 @@ impl Verifier {
Ok(()) Ok(())
} }
async fn send_event<'a>( async fn send_event(
&mut self, &mut self,
event: Event, event: Event,
broker: &RwLockReadGuard<'a, Broker<'a>>, broker: &RwLockReadGuard<'static, Broker>,
user: &UserId, user: &UserId,
remote: &DirectPeerId, remote: &DirectPeerId,
overlay: OverlayId, overlay: OverlayId,
@ -937,7 +1005,7 @@ impl Verifier {
.ok_or(NgError::TopicNotFound)? .ok_or(NgError::TopicNotFound)?
.to_owned(); .to_owned();
self.open_branch_(&repo_id, &branch_id, true, broker, user, remote) self.open_branch_(&repo_id, &branch_id, true, broker, user, remote, false)
.await?; .await?;
let _ = broker let _ = broker
@ -1122,9 +1190,9 @@ impl Verifier {
Ok(()) Ok(())
} }
async fn do_sync_req_if_needed<'a>( async fn do_sync_req_if_needed(
&mut self, &mut self,
broker: &RwLockReadGuard<'a, Broker<'a>>, broker: &RwLockReadGuard<'static, Broker>,
user: &UserId, user: &UserId,
remote: &DirectPeerId, remote: &DirectPeerId,
branch_id: &BranchId, branch_id: &BranchId,
@ -1183,9 +1251,9 @@ impl Verifier {
Ok(()) Ok(())
} }
async fn do_sync_req<'a>( async fn do_sync_req(
&mut self, &mut self,
broker: &RwLockReadGuard<'a, Broker<'a>>, broker: &RwLockReadGuard<'static, Broker>,
user: &UserId, user: &UserId,
remote: &DirectPeerId, remote: &DirectPeerId,
topic: &TopicId, topic: &TopicId,
@ -1217,7 +1285,7 @@ impl Verifier {
async fn load_store_from_read_cap<'a>( async fn load_store_from_read_cap<'a>(
&mut self, &mut self,
broker: &RwLockReadGuard<'a, Broker<'a>>, broker: &RwLockReadGuard<'static, Broker>,
user: &UserId, user: &UserId,
remote: &DirectPeerId, remote: &DirectPeerId,
store: Arc<Store>, store: Arc<Store>,
@ -1300,11 +1368,11 @@ impl Verifier {
Ok(()) Ok(())
} }
async fn get_commit<'a>( async fn get_commit(
commit_ref: ObjectRef, commit_ref: ObjectRef,
topic_id: Option<TopicId>, topic_id: Option<TopicId>,
overlay: &OverlayId, overlay: &OverlayId,
broker: &RwLockReadGuard<'a, Broker<'a>>, broker: &RwLockReadGuard<'static, Broker>,
user: &UserId, user: &UserId,
remote: &DirectPeerId, remote: &DirectPeerId,
) -> Result<Commit, NgError> { ) -> Result<Commit, NgError> {
@ -1347,10 +1415,13 @@ impl Verifier {
let broker = BROKER.read().await; let broker = BROKER.read().await;
let user = self.config.user_priv_key.to_pub(); let user = self.config.user_priv_key.to_pub();
let remote = self.connected_server_id.to_owned().unwrap(); let remote = self.connected_server_id.to_owned();
match repo.store.has(id) { match repo.store.has(id) {
Err(StorageError::NotFound) => { Err(StorageError::NotFound) => {
if remote.is_none() {
return Err(NgError::NotFound);
}
let msg = BlocksGet::V0(BlocksGetV0 { let msg = BlocksGet::V0(BlocksGetV0 {
ids: vec![*id], ids: vec![*id],
topic: None, topic: None,
@ -1358,7 +1429,7 @@ impl Verifier {
overlay: Some(overlay), overlay: Some(overlay),
}); });
match broker match broker
.request::<BlocksGet, Block>(&user, &remote, msg) .request::<BlocksGet, Block>(&user, remote.as_ref().unwrap(), msg)
.await .await
{ {
Ok(SoS::Stream(blockstream)) => Ok(Some(blockstream)), Ok(SoS::Stream(blockstream)) => Ok(Some(blockstream)),
@ -1375,7 +1446,10 @@ impl Verifier {
if self.need_bootstrap() { if self.need_bootstrap() {
let broker = BROKER.read().await; let broker = BROKER.read().await;
let user = self.config.user_priv_key.to_pub(); let user = self.config.user_priv_key.to_pub();
let remote = self.connected_server_id.to_owned().unwrap(); let remote = self
.connected_server_id
.to_owned()
.ok_or(NgError::NotConnected)?;
let private_store_id = self.config.private_store_id.to_owned().unwrap(); let private_store_id = self.config.private_store_id.to_owned().unwrap();
let private_store = self.create_private_store_from_credentials()?; let private_store = self.create_private_store_from_credentials()?;

Loading…
Cancel
Save