From 43f50d88f145bd7084701e1e9ada54eeab2c6fad Mon Sep 17 00:00:00 2001 From: Niko PLP Date: Fri, 7 Jul 2023 17:33:18 +0300 Subject: [PATCH] add_user, list_users from CLI --- Cargo.lock | 45 +- ng-app/src-tauri/src/lib.rs | 20 +- ng-sdk-js/src/lib.rs | 28 +- ng-wallet/src/lib.rs | 42 +- ngaccount/src/main.rs | 2 +- ngcli/Cargo.toml | 16 +- ngcli/src/main.rs | 977 +++++++++++-------------- ngcli/src/old.rs | 587 +++++++++++++++ ngd/src/cli.rs | 4 +- ngd/src/main.rs | 3 +- p2p-broker/src/broker_store/account.rs | 19 +- p2p-broker/src/server_ws.rs | 11 +- p2p-broker/src/storage.rs | 19 +- p2p-broker/src/types.rs | 10 +- p2p-client-ws/Cargo.toml | 1 - p2p-client-ws/src/lib.rs | 38 - p2p-client-ws/src/remote_ws.rs | 31 +- p2p-client-ws/src/remote_ws_wasm.rs | 25 +- p2p-net/src/actor.rs | 9 +- p2p-net/src/actors/add_user.rs | 109 +++ p2p-net/src/actors/del_user.rs | 86 +++ p2p-net/src/actors/list_users.rs | 91 +++ p2p-net/src/actors/mod.rs | 9 + p2p-net/src/actors/start.rs | 18 +- p2p-net/src/broker.rs | 171 +++-- p2p-net/src/broker_storage.rs | 10 +- p2p-net/src/connection.rs | 286 ++++++-- p2p-net/src/errors.rs | 184 ++--- p2p-net/src/types.rs | 683 +++++++---------- p2p-repo/src/kcv_store.rs | 7 + p2p-repo/src/types.rs | 24 +- stores-lmdb/Cargo.toml | 2 +- stores-lmdb/src/kcv_store.rs | 67 ++ 33 files changed, 2276 insertions(+), 1358 deletions(-) create mode 100644 ngcli/src/old.rs create mode 100644 p2p-net/src/actors/add_user.rs create mode 100644 p2p-net/src/actors/del_user.rs create mode 100644 p2p-net/src/actors/list_users.rs diff --git a/Cargo.lock b/Cargo.lock index 5fc933c..d046a71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -764,6 +764,7 @@ dependencies = [ "anstyle", "bitflags", "clap_lex", + "once_cell", "strsim", ] @@ -2816,18 +2817,28 @@ dependencies = [ name = "ngcli" version = "0.1.0" dependencies = [ + "anyhow", "assert_cmd", "async-std", + "base64-url", + "blake3", + "clap", "ed25519-dalek", - "fastbloom-rs", + "env_logger", "futures", - "p2p-broker", + "getrandom 0.2.10", + "log", "p2p-client-ws", "p2p-net", "p2p-repo", "rand 0.7.3", + "serde", + "serde_bare", + "serde_bytes", + "serde_json", "stores-lmdb", "tempfile", + "zeroize", ] [[package]] @@ -3135,7 +3146,6 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-test", "ws_stream_wasm", - "xactor", ] [[package]] @@ -3854,7 +3864,7 @@ dependencies = [ [[package]] name = "rkv" version = "0.18.0" -source = "git+https://git.nextgraph.org/NextGraph/rkv.git?rev=8f5ad79c0c93138b1bdc0a1254a7c6b4d357a5d9#8f5ad79c0c93138b1bdc0a1254a7c6b4d357a5d9" +source = "git+https://git.nextgraph.org/NextGraph/rkv.git?rev=c746abb443b7bb4541ebbef2b71e8d0f9eb39f6a#c746abb443b7bb4541ebbef2b71e8d0f9eb39f6a" dependencies = [ "arrayref", "bincode", @@ -5922,33 +5932,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "xactor" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5297548253bd91993bedcc70ae166e64b3fab5ded1965055b2a450777870ff" -dependencies = [ - "anyhow", - "async-std", - "async-trait", - "fnv", - "futures", - "once_cell", - "slab", - "xactor-derive", -] - -[[package]] -name = "xactor-derive" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce65a6e78fedf06f36b8e7b3154175226fad605971f535062339c05f5caecbed" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "xorfilter-rs" version = "0.5.1" diff --git a/ng-app/src-tauri/src/lib.rs b/ng-app/src-tauri/src/lib.rs index d409547..1efac7a 100644 --- a/ng-app/src-tauri/src/lib.rs +++ b/ng-app/src-tauri/src/lib.rs @@ -31,13 +31,13 @@ pub type SetupHook = Box Result<(), Box Result<(), ()> { - log_info!("test is {}", BROKER.read().await.test()); + log_debug!("test is {}", BROKER.read().await.test()); Ok(()) } #[tauri::command(rename_all = "snake_case")] async fn wallet_gen_shuffle_for_pazzle_opening(pazzle_length: u8) -> Result { - log_info!( + log_debug!( "wallet_gen_shuffle_for_pazzle_opening from rust {}", pazzle_length ); @@ -46,7 +46,7 @@ async fn wallet_gen_shuffle_for_pazzle_opening(pazzle_length: u8) -> Result Result, ()> { - log_info!("wallet_gen_shuffle_for_pin from rust"); + log_debug!("wallet_gen_shuffle_for_pin from rust"); Ok(gen_shuffle_for_pin()) } @@ -56,13 +56,13 @@ async fn wallet_open_wallet_with_pazzle( pazzle: Vec, pin: [u8; 4], ) -> Result { - log_info!("wallet_open_wallet_with_pazzle from rust {:?}", pazzle); + log_debug!("wallet_open_wallet_with_pazzle from rust {:?}", pazzle); open_wallet_with_pazzle(wallet, pazzle, pin).map_err(|e| e.to_string()) } #[tauri::command(rename_all = "snake_case")] async fn wallet_create_wallet(mut params: CreateWalletV0) -> Result { - //log_info!("wallet_create_wallet from rust {:?}", params); + //log_debug!("wallet_create_wallet from rust {:?}", params); params.result_with_wallet_file = false; let local_save = params.local_save; let res = create_wallet_v0(params).await.map_err(|e| e.to_string()); @@ -78,13 +78,13 @@ async fn wallet_create_wallet(mut params: CreateWalletV0) -> Result Result { - log_info!("{:?}", payload); + log_debug!("{:?}", payload); payload.encode().ok_or(()) } #[tauri::command(rename_all = "snake_case")] async fn doc_sync_branch(nuri: &str, stream_id: &str, app: tauri::AppHandle) -> Result<(), ()> { - log_info!("doc_sync_branch {} {}", nuri, stream_id); + log_debug!("doc_sync_branch {} {}", nuri, stream_id); let main_window = app.get_window("main").unwrap(); let mut reader; @@ -107,7 +107,7 @@ async fn doc_sync_branch(nuri: &str, stream_id: &str, app: tauri::AppHandle) -> BROKER.write().await.tauri_stream_cancel(stream_id); - log_info!("END OF LOOP"); + log_debug!("END OF LOOP"); Ok(()) } @@ -118,7 +118,7 @@ async fn doc_sync_branch(nuri: &str, stream_id: &str, app: tauri::AppHandle) -> #[tauri::command(rename_all = "snake_case")] async fn cancel_doc_sync_branch(stream_id: &str) -> Result<(), ()> { - log_info!("cancel stream {}", stream_id); + log_debug!("cancel stream {}", stream_id); BROKER .write() .await @@ -131,7 +131,7 @@ async fn doc_get_file_from_store_with_object_ref( nuri: &str, obj_ref: ObjectRef, ) -> Result { - log_info!( + log_debug!( "doc_get_file_from_store_with_object_ref {} {:?}", nuri, obj_ref diff --git a/ng-sdk-js/src/lib.rs b/ng-sdk-js/src/lib.rs index ca07e8d..b09f314 100644 --- a/ng-sdk-js/src/lib.rs +++ b/ng-sdk-js/src/lib.rs @@ -148,11 +148,11 @@ pub fn client_info() -> ClientInfoV0 { #[cfg(target_arch = "wasm32")] #[wasm_bindgen] pub fn encode_create_account(payload: JsValue) -> JsValue { - log_info!("{:?}", payload); + log_debug!("{:?}", payload); let create_account = serde_wasm_bindgen::from_value::(payload).unwrap(); - log_info!("create_account {:?}", create_account); + log_debug!("create_account {:?}", create_account); let res = create_account.encode(); - log_info!("res {:?}", res); + log_debug!("res {:?}", res); serde_wasm_bindgen::to_value(&res).unwrap() } @@ -182,7 +182,7 @@ pub fn client_info() -> ClientInfoV0 { let ua = client_details(); let bowser = Bowser::parse(ua); - //log_info!("{:?}", bowser); + //log_debug!("{:?}", bowser); let details_string = client_details2(bowser, env!("CARGO_PKG_VERSION").to_string()); @@ -200,9 +200,9 @@ pub fn client_info() -> ClientInfoV0 { #[cfg(target_arch = "wasm32")] #[wasm_bindgen] pub async fn test() { - log_info!("test is {}", BROKER.read().await.test()); + log_debug!("test is {}", BROKER.read().await.test()); let client_info = client_info(); - log_info!("{:?}", client_info); + log_debug!("{:?}", client_info); } #[cfg(target_arch = "wasm32")] @@ -213,7 +213,7 @@ pub async fn doc_get_file_from_store_with_object_ref( ) -> Result { let obj_ref = serde_wasm_bindgen::from_value::(obj_ref_js).unwrap(); - log_info!( + log_debug!( "doc_get_file {} {:?} {}", nuri, obj_ref.id, @@ -271,14 +271,14 @@ pub async fn doc_sync_branch(anuri: String, callback: &js_sys::Function) -> JsVa Err(_) => {} } } - log_info!("END OF LOOP"); + log_debug!("END OF LOOP"); Ok(()) } spawn_and_log_error(inner_task(reader, anuri, callback.clone())); let cb = Closure::once(move || { - log_info!("close channel"); + log_debug!("close channel"); sender.close_channel() }); //Closure::wrap(Box::new(move |sender| sender.close_channel()) as Box)>); @@ -299,7 +299,7 @@ pub async fn probe() { WS_PORT, ) .await; - log_info!("broker.probe : {:?}", res); + log_debug!("broker.probe : {:?}", res); Broker::join_shutdown_with_timeout(std::time::Duration::from_secs(5)).await; } @@ -315,12 +315,12 @@ pub async fn start() { //let pub_key = PubKey::Ed25519PubKey(keys.1); let keys = generate_keypair(); let x_from_ed = keys.1.to_dh_from_ed(); - log_info!("Pub from X {}", x_from_ed); + log_debug!("Pub from X {}", x_from_ed); let (client_priv, client) = generate_keypair(); let (user_priv, user) = generate_keypair(); - log_info!("start connecting"); + log_debug!("start connecting"); let res = BROKER .write() @@ -340,7 +340,7 @@ pub async fn start() { }), ) .await; - log_info!("broker.connect : {:?}", res); + log_debug!("broker.connect : {:?}", res); if res.is_err() { return Ok(()); //panic!("Cannot connect"); @@ -352,7 +352,7 @@ pub async fn start() { async fn timer_close(remote_peer_id: DirectPeerId, user: Option) -> ResultSend<()> { async move { sleep!(std::time::Duration::from_secs(3)); - log_info!("timeout"); + log_debug!("timeout"); BROKER .write() .await diff --git a/ng-wallet/src/lib.rs b/ng-wallet/src/lib.rs index b964e27..95cee2c 100644 --- a/ng-wallet/src/lib.rs +++ b/ng-wallet/src/lib.rs @@ -213,7 +213,7 @@ pub fn open_wallet_with_pazzle( )?; pazzle_key.zeroize(); - log_info!( + log_debug!( "opening of wallet with pazzle took: {} ms", opening_pazzle.elapsed().as_millis() ); @@ -298,16 +298,16 @@ pub fn display_pazzle(pazzle: &Vec) -> Vec { pub fn gen_shuffle_for_pazzle_opening(pazzle_length: u8) -> ShuffledPazzle { let mut rng = rand::thread_rng(); let mut category_indices: Vec = (0..pazzle_length).collect(); - //log_info!("{:?}", category_indices); + //log_debug!("{:?}", category_indices); category_indices.shuffle(&mut rng); - //log_info!("{:?}", category_indices); + //log_debug!("{:?}", category_indices); let mut emoji_indices: Vec> = Vec::with_capacity(pazzle_length.into()); for _ in 0..pazzle_length { let mut idx: Vec = (0..15).collect(); - //log_info!("{:?}", idx); + //log_debug!("{:?}", idx); idx.shuffle(&mut rng); - //log_info!("{:?}", idx); + //log_debug!("{:?}", idx); emoji_indices.push(idx) } ShuffledPazzle { @@ -319,9 +319,9 @@ pub fn gen_shuffle_for_pazzle_opening(pazzle_length: u8) -> ShuffledPazzle { pub fn gen_shuffle_for_pin() -> Vec { let mut rng = rand::thread_rng(); let mut digits: Vec = (0..10).collect(); - //log_info!("{:?}", digits); + //log_debug!("{:?}", digits); digits.shuffle(&mut rng); - //log_info!("{:?}", digits); + //log_debug!("{:?}", digits); digits } @@ -335,7 +335,7 @@ pub fn gen_shuffle_for_pin() -> Vec { // for i in &mut mnemonic { // *i = choices.chars().nth(ran.gen_range(0, 72)).unwrap(); // } -// log_info!("{}", mnemonic.iter().collect::()); +// log_debug!("{}", mnemonic.iter().collect::()); // } /// creates a Wallet from a pin, a security text and image (with option to send the bootstrap and wallet to nextgraph.one) @@ -559,7 +559,7 @@ pub async fn create_wallet_v0( // sig, // }); - log_info!( + log_debug!( "creating of wallet took: {} ms", creating_pazzle.elapsed().as_millis() ); @@ -600,11 +600,11 @@ mod test { #[test] fn test_gen_shuffle() { let shuffle = gen_shuffle_for_pazzle_opening(9); - log_info!("{:?}", shuffle); + log_debug!("{:?}", shuffle); let shuffle = gen_shuffle_for_pazzle_opening(12); - log_info!("{:?}", shuffle); + log_debug!("{:?}", shuffle); let shuffle = gen_shuffle_for_pazzle_opening(15); - log_info!("{:?}", shuffle); + log_debug!("{:?}", shuffle); let digits = gen_shuffle_for_pin(); let digits = gen_shuffle_for_pin(); } @@ -636,26 +636,26 @@ mod test { .await .expect("create_wallet_v0"); - log_info!( + log_debug!( "creation of wallet took: {} ms", creation.elapsed().as_millis() ); - log_info!("-----------------------------"); + log_debug!("-----------------------------"); let mut file = File::create("tests/wallet.ngw").expect("open wallet write file"); let ser_wallet = to_vec(&NgFile::V0(NgFileV0::Wallet(res.wallet.clone()))).unwrap(); file.write_all(&ser_wallet); - log_info!( + log_debug!( "wallet id: {:?}", base64_url::encode(&res.wallet.id().slice()) ); - log_info!("pazzle {:?}", display_pazzle(&res.pazzle)); - log_info!("mnemonic {:?}", display_mnemonic(&res.mnemonic)); - log_info!("pin {:?}", pin); + log_debug!("pazzle {:?}", display_pazzle(&res.pazzle)); + log_debug!("mnemonic {:?}", display_mnemonic(&res.mnemonic)); + log_debug!("pin {:?}", pin); if let Wallet::V0(v0) = &res.wallet { - log_info!("security text: {:?}", v0.content.security_txt); + log_debug!("security text: {:?}", v0.content.security_txt); let mut file = File::create("tests/generated_security_image.jpg").expect("open write file"); @@ -678,7 +678,7 @@ mod test { .expect("open with mnemonic"); //log_debug!("encrypted part {:?}", w); - log_info!( + log_debug!( "opening of wallet with mnemonic took: {} ms", opening_mnemonic.elapsed().as_millis() ); @@ -687,7 +687,7 @@ mod test { let opening_pazzle = Instant::now(); let w = open_wallet_with_pazzle(Wallet::V0(v0.clone()), res.pazzle.clone(), pin) .expect("open with pazzle"); - log_info!( + log_debug!( "opening of wallet with pazzle took: {} ms", opening_pazzle.elapsed().as_millis() ); diff --git a/ngaccount/src/main.rs b/ngaccount/src/main.rs index b339571..311d58b 100644 --- a/ngaccount/src/main.rs +++ b/ngaccount/src/main.rs @@ -75,7 +75,7 @@ async fn main() -> anyhow::Result<()> { let addr: Vec<&str> = server_address.split(',').collect(); if addr.len() != 3 { return Err(anyhow!( - "NG_ACCOUNT_SERVER is invalid. format is IP,PORT,PEERID" + "NG_ACCOUNT_SERVER is invalid. format is IP,PORT,PEER_ID" )); } let ip: IP = addr[0].into(); diff --git a/ngcli/Cargo.toml b/ngcli/Cargo.toml index 400c4b7..87774a4 100644 --- a/ngcli/Cargo.toml +++ b/ngcli/Cargo.toml @@ -8,15 +8,25 @@ description = "CLI command-line interpreter of NextGraph" repository = "https://git.nextgraph.org/NextGraph/nextgraph-rs" [dependencies] -p2p-repo = { path = "../p2p-repo" } +p2p-repo = { path = "../p2p-repo", features = ["server_log_output"] } p2p-net = { path = "../p2p-net" } p2p-client-ws = { path = "../p2p-client-ws" } -p2p-broker = { path = "../p2p-broker" } stores-lmdb = { path = "../stores-lmdb" } async-std = { version = "1.12.0", features = ["attributes"] } futures = "0.3.24" tempfile = "3" -fastbloom-rs = "0.5.3" rand = "0.7" ed25519-dalek = "1.0.1" assert_cmd = "2.0.5" +clap = { version = "4.3.4", features = ["env","string","cargo"] } +log = "0.4" +env_logger = "0.10" +anyhow = "1.0.71" +serde_json = "1.0" +zeroize = { version = "1.6.0" } +base64-url = "2.0.0" +getrandom = "0.2.7" +blake3 = "1.3.1" +serde = { version = "1.0", features = ["derive"] } +serde_bare = "0.5.0" +serde_bytes = "0.11.7" \ No newline at end of file diff --git a/ngcli/src/main.rs b/ngcli/src/main.rs index 159eabe..31bbb87 100644 --- a/ngcli/src/main.rs +++ b/ngcli/src/main.rs @@ -10,618 +10,465 @@ // according to those terms. use ed25519_dalek::*; -use fastbloom_rs::{BloomFilter as Filter, FilterBuilder, Membership}; + use futures::{future, pin_mut, stream, SinkExt, StreamExt}; -use p2p_broker::broker_store::config::ConfigMode; +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}; +use serde_json::{from_str, to_string_pretty}; use std::collections::HashMap; +use std::fs::{read_to_string, write}; +use std::io::ErrorKind; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use std::path::PathBuf; +use std::str::FromStr; use stores_lmdb::kcv_store::LmdbKCVStore; use stores_lmdb::repo_store::LmdbRepoStore; +use zeroize::Zeroize; +use p2p_client_ws::remote_ws::ConnectionWebSocket; +use p2p_net::broker::BROKER; use p2p_net::errors::*; use p2p_net::types::*; use p2p_repo::log::*; use p2p_repo::types::*; -use p2p_repo::utils::{generate_keypair, now_timestamp}; +use p2p_repo::utils::{decode_key, generate_keypair, now_timestamp}; + +use clap::{arg, command, value_parser, ArgAction, Command}; -fn block_size() -> usize { - store_max_value_size() - //store_valid_value_size(0) +/// CliConfig Version 0 +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct CliConfigV0 { + pub ip: IpAddr, + pub port: u16, + pub peer_id: PubKey, + pub user: Option, } -async fn test_sync(cnx: &mut impl BrokerConnection, user_pub_key: PubKey, userpriv_key: PrivKey) { - fn add_obj( - content: ObjectContent, - deps: Vec, - expiry: Option, - repo_pubkey: PubKey, - repo_secret: SymKey, - store: &mut impl RepoStore, - ) -> ObjectRef { - let max_object_size = 4000; - let obj = Object::new( - content, - deps, - expiry, - max_object_size, - repo_pubkey, - repo_secret, - ); - //log_debug!(">>> add_obj"); - log_debug!(" id: {}", obj.id()); - //log_debug!(" deps: {:?}", obj.deps()); - obj.save(store).unwrap(); - obj.reference().unwrap() - } +/// Cli config +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum CliConfig { + V0(CliConfigV0), +} - fn add_commit( - branch: ObjectRef, - author_privkey: PrivKey, - author_pubkey: PubKey, - seq: u32, - deps: Vec, - acks: Vec, - body_ref: ObjectRef, - repo_pubkey: PubKey, - repo_secret: SymKey, - store: &mut impl RepoStore, - ) -> 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 obj_ref = ObjectRef { - id: ObjectId::Blake3Digest32([1; 32]), - key: SymKey::ChaCha20Key([2; 32]), - }; - let refs = vec![obj_ref]; - let metadata = vec![5u8; 55]; - let expiry = None; - - let commit = Commit::new( - author_privkey, - author_pubkey, - seq, - branch, - deps, - acks, - refs, - metadata, - body_ref, - expiry, - ) - .unwrap(); - //log_debug!("commit: {}", commit.id().unwrap()); - add_obj( - ObjectContent::Commit(commit), - obj_deps, - expiry, - repo_pubkey, - repo_secret, - store, - ) +impl CliConfig { + fn new_v0(ip: IpAddr, port: u16, peer_id: PubKey) -> Self { + CliConfig::V0(CliConfigV0 { + ip, + port, + peer_id, + user: None, + }) } +} - fn add_body_branch( - branch: Branch, - repo_pubkey: PubKey, - repo_secret: SymKey, - store: &mut impl RepoStore, - ) -> ObjectRef { - let deps = vec![]; - let expiry = None; - let body = CommitBody::Branch(branch); - //log_debug!("body: {:?}", body); - add_obj( - ObjectContent::CommitBody(body), - deps, - expiry, - repo_pubkey, - repo_secret, - store, - ) - } +fn gen_client_keys(key: Option<[u8; 32]>) -> [[u8; 32]; 4] { + let key = match key { + None => { + let mut master_key = [0u8; 32]; + log_warn!("gen_client_keys: No key provided, generating one"); + getrandom::getrandom(&mut master_key).expect("getrandom failed"); + master_key + } + Some(k) => k, + }; + let peerid: [u8; 32]; + let wallet: [u8; 32]; + let sig: [u8; 32]; - fn add_body_trans( - deps: Vec, - repo_pubkey: PubKey, - repo_secret: SymKey, - store: &mut impl RepoStore, - ) -> 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, - ) + peerid = blake3::derive_key("NextGraph Client BLAKE3 key PeerId privkey", &key); + wallet = blake3::derive_key("NextGraph Client BLAKE3 key wallet encryption", &key); + sig = blake3::derive_key("NextGraph Client BLAKE3 key config signature", &key); + + [key, peerid, wallet, sig] +} + +#[async_std::main] +async fn main() -> Result<(), ProtocolError> { + let matches = command!() + .arg(arg!( + -v --verbose ... "Increase the logging output. once : info, twice : debug, 3 times : trace" + )) + .arg(arg!(-b --base [PATH] "Base path for client home folder containing all persistent files, config, and key") + .required(false) + .value_parser(value_parser!(PathBuf)) + .default_value(".ng")) + .arg( + arg!( + -k --key "Master key of the client. Should be a base64-url encoded serde serialization of a [u8; 32]. + If not provided, a new key will be generated for you" + ) + .required(false) + .env("NG_CLIENT_KEY"), + ) + .arg( + arg!( + -u --user "Client ID to use to connect to the server. Should be a base64-url encoded serde + serialization of a [u8; 32] representing the user private key" + ) + .required(false) + .env("NG_CLIENT_USER"), + ) + .arg( + arg!( + -s --server "Server to connect to. IP can be IpV4 or IPv6, followed by a + comma and port as u16 and another comma and PEER_ID should be a base64-url encoded serde serialization of a [u8; 32]" + ) + .required(false) + .env("NG_CLIENT_SERVER"), + ) + .arg(arg!( + --save_key "Saves to disk the provided or automatically generated key. Only use if file storage is secure. + Alternatives are passing the key at every start with --key or NG_CLIENT_KEY env var." + ).long("save-key").required(false)) + .arg(arg!( + --save_config "Saves to disk the provided config of the and server ." + ).long("save-config").required(false)) + .subcommand( + Command::new("admin") + .about("admin users can administrate their broker (add user, create invite links)") + .subcommand_required(true) + .subcommand( + Command::new("add-user") + .about("add a user to the server, so it can connect to it") + .arg(arg!([USER_ID] "userId of the user to add. should be a base64-url encoded serde serialization of its pubkey [u8; 32]").required(true)) + .arg(arg!(-a --admin "make this user admin as well").required(false))) + .subcommand( + Command::new("list-users") + .about("list all users registered in the broker") + .arg(arg!(-a --admin "only lists admin users. otherwise, lists only non admin users").required(false))) + ) + .subcommand( + Command::new("gen-user") + .about("Generates a new user public key and private key to be used for authentication.") + ) + .get_matches(); + + if std::env::var("RUST_LOG").is_err() { + match matches.get_one::("verbose").unwrap() { + 0 => std::env::set_var("RUST_LOG", "warn"), + 1 => std::env::set_var("RUST_LOG", "info"), + 2 => std::env::set_var("RUST_LOG", "debug"), + 3 => std::env::set_var("RUST_LOG", "trace"), + _ => std::env::set_var("RUST_LOG", "trace"), + } } + env_logger::init(); - 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()); - //log_debug!("body: {:?}", body); - add_obj( - ObjectContent::CommitBody(body), - deps, - expiry, - repo_pubkey, - repo_secret, - store, - ) + if let Some(matches) = matches.subcommand_matches("gen-user") { + let (privkey, pubkey) = generate_keypair(); + println!("Your UserId is: {pubkey}"); + println!("Your Private key is: {privkey}"); + return Ok(()); } - let mut store = HashMapRepoStore::new(); - let mut rng = OsRng {}; - - // repo - - let repo_keypair: Keypair = Keypair::generate(&mut rng); - // log_debug!( - // "repo private key: ({}) {:?}", - // repo_keypair.secret.as_bytes().len(), - // repo_keypair.secret.as_bytes() - // ); - // log_debug!( - // "repo public key: ({}) {:?}", - // repo_keypair.public.as_bytes().len(), - // repo_keypair.public.as_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]); - - let repolink = RepoLink::V0(RepoLinkV0 { - id: repo_pubkey, - secret: repo_secret, - peers: vec![], - }); + let base = matches.get_one::("base").unwrap(); + log_debug!("base {:?}", base); - // branch - - let branch_keypair: Keypair = Keypair::generate(&mut rng); - //log_debug!("branch public key: {:?}", branch_keypair.public.as_bytes()); - let branch_pubkey = PubKey::Ed25519PubKey(branch_keypair.public.to_bytes()); - - let member_keypair: Keypair = Keypair::generate(&mut rng); - //log_debug!("member public key: {:?}", member_keypair.public.as_bytes()); - let member_privkey = PrivKey::Ed25519PrivKey(member_keypair.secret.to_bytes()); - 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, - branch_pubkey, - secret, - members, - quorum, - ack_delay, - tags, - metadata, - ); - //log_debug!("branch: {:?}", branch); - - log_debug!("branch deps/acks:"); - log_debug!(""); - log_debug!(" br"); - log_debug!(" / \\"); - log_debug!(" t1 t2"); - log_debug!(" / \\ / \\"); - log_debug!(" a3 t4<--t5-->(t1)"); - log_debug!(" / \\"); - log_debug!(" a6 a7"); - log_debug!(""); - - // commit bodies - - let branch_body = add_body_branch( - branch.clone(), - repo_pubkey.clone(), - repo_secret.clone(), - &mut store, - ); - let ack_body = add_body_ack(vec![], repo_pubkey, repo_secret, &mut store); - let trans_body = add_body_trans(vec![], repo_pubkey, repo_secret, &mut store); - - // create & add commits to store - - log_debug!(">> br"); - let br = add_commit( - branch_body, - member_privkey, - member_pubkey, - 0, - vec![], - vec![], - branch_body, - repo_pubkey, - repo_secret, - &mut store, - ); - - log_debug!(">> t1"); - let t1 = add_commit( - branch_body, - member_privkey, - member_pubkey, - 1, - vec![br], - vec![], - trans_body, - repo_pubkey, - repo_secret, - &mut store, - ); - - log_debug!(">> t2"); - let t2 = add_commit( - branch_body, - member_privkey, - member_pubkey, - 2, - vec![br], - vec![], - trans_body, - repo_pubkey, - repo_secret, - &mut store, - ); - - log_debug!(">> a3"); - let a3 = add_commit( - branch_body, - member_privkey, - member_pubkey, - 3, - vec![t1], - vec![], - ack_body, - repo_pubkey, - repo_secret, - &mut store, - ); - - log_debug!(">> t4"); - let t4 = add_commit( - branch_body, - member_privkey, - member_pubkey, - 4, - vec![t2], - vec![t1], - trans_body, - repo_pubkey, - repo_secret, - &mut store, - ); - - log_debug!(">> t5"); - let t5 = add_commit( - branch_body, - member_privkey, - member_pubkey, - 5, - vec![t1, t2], - vec![t4], - trans_body, - repo_pubkey, - repo_secret, - &mut store, - ); - - log_debug!(">> a6"); - let a6 = add_commit( - branch_body, - member_privkey, - member_pubkey, - 6, - vec![t4], - vec![], - ack_body, - repo_pubkey, - repo_secret, - &mut store, - ); - - log_debug!(">> a7"); - let a7 = add_commit( - branch_body, - member_privkey, - member_pubkey, - 7, - vec![t4], - vec![], - ack_body, - repo_pubkey, - repo_secret, - &mut store, - ); - - let mut public_overlay_cnx = cnx - .overlay_connect(&repolink, true) - .await - .expect("overlay_connect failed"); - - // Sending everything to the broker - for (v) in store.get_all() { - //log_debug!("SENDING {}", k); - let _ = public_overlay_cnx - .put_block(&v) - .await - .expect("put_block failed"); + let mut path = base.clone(); + path.push("client"); + if !path.is_absolute() { + path = std::env::current_dir().unwrap().join(path); } - // Now emptying the local store of the client, and adding only 1 commit into it (br) - // we also have received an commit (t5) but we don't know what to do with it... - let mut store = HashMapRepoStore::new(); - - let br = add_commit( - branch_body, - member_privkey, - member_pubkey, - 0, - vec![], - vec![], - branch_body, - repo_pubkey, - repo_secret, - &mut store, - ); - - let t5 = add_commit( - branch_body, - member_privkey, - member_pubkey, - 5, - vec![t1, t2], - vec![t4], - trans_body, - repo_pubkey, - repo_secret, - &mut store, - ); - - log_debug!("LOCAL STORE HAS {} BLOCKS", store.get_len()); - - // Let's pretend that we know that the head of the branch in the broker is at commits a6 and a7. - // normally it would be the pub/sub that notifies us of those heads. - // now we want to synchronize with the broker. - - let mut filter = Filter::new(FilterBuilder::new(10, 0.01)); - for commit_ref in [br, t5] { - match commit_ref.id { - ObjectId::Blake3Digest32(d) => filter.add(&d), - } + log_debug!("cur {}", std::env::current_dir().unwrap().display()); + log_debug!("home {}", path.to_str().unwrap()); + std::fs::create_dir_all(path.clone()).unwrap(); + + // reading key from file, if any + let mut key_path = path.clone(); + key_path.push("key"); + let key_from_file: Option<[u8; 32]>; + let res = |key_path| -> Result<[u8; 32], &str> { + let mut file = read_to_string(key_path).map_err(|_| "")?; + let first_line = file.lines().nth(0).ok_or("empty file")?; + let res = decode_key(first_line.trim()).map_err(|_| "invalid file"); + file.zeroize(); + res + }(&key_path); + + if res.is_err() && res.unwrap_err().len() > 0 { + log_err!( + "provided key file is incorrect. {}. cannot start", + res.unwrap_err() + ); + return Err(ProtocolError::InvalidValue); } - let cfg = filter.config(); - - let known_commits = BloomFilter { - k: cfg.hashes, - f: filter.get_u8_array().to_vec(), + key_from_file = res.ok(); + + let mut keys: [[u8; 32]; 4] = match matches.get_one::("key") { + Some(key_string) => { + if key_from_file.is_some() { + log_err!("provided --key option or NG_CLIENT_KEY var env will not be used as a key file is already present"); + //key_string.as_mut().zeroize(); + gen_client_keys(key_from_file) + } else { + let res = decode_key(key_string.as_str()).map_err(|_| { + log_err!("provided key is invalid. cannot start"); + ProtocolError::InvalidValue + })?; + if matches.get_flag("save_key") { + let mut master_key = base64_url::encode(&res); + write(key_path.clone(), &master_key).map_err(|e| { + log_err!("cannot save key to file. {}.cannot start", e.to_string()); + ProtocolError::InvalidValue + })?; + master_key.zeroize(); + log_info!("The key has been saved to {}", key_path.to_str().unwrap()); + } + //key_string.as_mut().zeroize(); + gen_client_keys(Some(res)) + } + } + None => { + if key_from_file.is_some() { + gen_client_keys(key_from_file) + } else { + let res = gen_client_keys(None); + let mut master_key = base64_url::encode(&res[0]); + if matches.get_flag("save_key") { + write(key_path.clone(), &master_key).map_err(|e| { + log_err!("cannot save key to file. {}.cannot start", e.to_string()); + ProtocolError::InvalidValue + })?; + log_info!("The key has been saved to {}", key_path.to_str().unwrap()); + } else { + // on purpose we don't log the key, just print it out to stdout, as it should not be saved in logger's files + println!("YOUR GENERATED KEY IS: {}", master_key); + log_err!("At your request, the key wasn't saved. If you want to save it to disk, use ---save-key"); + log_err!("provide it again to the next start of ngcli with --key option or NG_CLIENT_KEY env variable"); + } + master_key.zeroize(); + res + } + } }; - let known_heads = [br.id]; - - let remote_heads = [a6.id, a7.id]; - - let mut synced_blocks_stream = public_overlay_cnx - .sync_branch(remote_heads.to_vec(), known_heads.to_vec(), known_commits) - .await - .expect("sync_branch failed"); + key_from_file.and_then(|mut key| { + key.zeroize(); + None::<()> + }); - let mut i = 0; - while let Some(b) = synced_blocks_stream.next().await { - log_debug!("GOT BLOCK {}", b.id()); - store.put(&b); - i += 1; + // reading config from file, if any + let mut config_path = path.clone(); + config_path.push("config.json"); + let mut config: Option; + let res = |config_path| -> Result { + let file = read_to_string(config_path).map_err(|_| "".to_string())?; + from_str(&file).map_err(|e| e.to_string()) + }(&config_path); + + if res.is_err() && res.as_ref().unwrap_err().len() > 0 { + log_err!( + "provided config file is incorrect. {}. cannot start", + res.unwrap_err() + ); + return Err(ProtocolError::InvalidValue); + } + config = res.ok(); + + if let Some(server) = matches.get_one::("server") { + let addr: Vec<&str> = server.split(',').collect(); + if addr.len() != 3 { + log_err!( + "NG_CLIENT_SERVER or the --server option is invalid. format is IP,PORT,PEER_ID. cannot start" + ); + return Err(ProtocolError::InvalidValue); + } + let ip = IpAddr::from_str(addr[0]).map_err(|_| { + log_err!("NG_CLIENT_SERVER or the --server option is invalid. format is IP,PORT,PEER_ID. The first part is not an IP address. cannot start"); + ProtocolError::InvalidValue + })?; + + let port = match from_str::(addr[1]) { + Err(_) => { + log_err!("NG_CLIENT_SERVER or the --server option is invalid. format is IP,PORT,PEER_ID. The port is invalid. It should be a number. cannot start"); + return Err(ProtocolError::InvalidValue); + } + Ok(val) => val, + }; + let peer_id: PubKey = addr[2].try_into().map_err(|_| { + log_err!( + "NG_CLIENT_SERVER or the --server option is invalid. format is IP,PORT,PEER_ID. + The PEER_ID is invalid. It should be a base64-url encoded serde serialization of a [u8; 32]. cannot start" + ); + ProtocolError::InvalidValue + })?; + if config.is_some() { + log_warn!("Overwriting the config found in file with new server parameters provided on command line!"); + let CliConfig::V0(c) = config.as_mut().unwrap(); + c.ip = ip; + c.port = port; + c.peer_id = peer_id; + } else { + config = Some(CliConfig::new_v0(ip, port, peer_id)); + } } - log_debug!("SYNCED {} BLOCKS", i); - - log_debug!("LOCAL STORE HAS {} BLOCKS", store.get_len()); + if config.is_none() { + log_err!( + "No config found for the server to connect to. The config file is missing. + You must provide NG_CLIENT_SERVER or the --server option. cannot start" + ); + return Err(ProtocolError::InvalidValue); + } - // now the client can verify the DAG and each commit. Then update its list of heads. -} + if let Some(user) = matches.get_one::("user") { + let privkey: PrivKey = user.as_str().try_into().map_err(|_| { + log_err!( + "NG_CLIENT_USER or the --user option is invalid. It should be a base64-url encoded + serde serialization of a [u8; 32] of a private key for a user. cannot start" + ); + ProtocolError::InvalidValue + })?; + if config.is_some() { + let CliConfig::V0(c) = config.as_mut().unwrap(); + if c.user.is_some() { + log_warn!("Overwriting the config found in file with new user parameter provided on command line!"); + } + c.user = Some(privkey); + } else { + panic!("should not happen. no config and no server params. cannot set user"); + } + } -async fn test( - cnx: &mut impl BrokerConnection, - pub_key: PubKey, - priv_key: PrivKey, -) -> Result<(), ProtocolError> { - cnx.add_user(PubKey::Ed25519PubKey([1; 32]), priv_key) - .await?; + let CliConfig::V0(config_v0) = config.as_ref().unwrap(); + if config_v0.user.is_none() { + log_err!( + "No config found for the user. The config file is missing. + You must provide NG_CLIENT_USER or the --user option. cannot start" + ); + return Err(ProtocolError::InvalidValue); + } - cnx.add_user(pub_key, priv_key).await?; - //.expect("add_user 2 (myself) failed"); + if matches.get_flag("save_config") { + // saves the config to file + let json_string = to_string_pretty(&config).unwrap(); + write(config_path.clone(), json_string).map_err(|e| { + log_err!( + "cannot save config to file. {}. cannot start", + e.to_string() + ); + ProtocolError::InvalidValue + })?; + log_info!( + "The config file has been saved to {}", + config_path.to_str().unwrap() + ); + } - assert_eq!( - cnx.add_user(PubKey::Ed25519PubKey([1; 32]), priv_key) + async fn do_admin_call< + A: Into + + Into + + std::fmt::Debug + + Sync + + Send + + 'static, + >( + privk: [u8; 32], + config_v0: &CliConfigV0, + cmd: A, + ) -> Result { + let peer_privk = PrivKey::Ed25519PrivKey(privk); + let peer_pubk = peer_privk.to_pub(); + BROKER + .write() + .await + .admin( + Box::new(ConnectionWebSocket {}), + peer_privk, + peer_pubk, + config_v0.peer_id, + config_v0.user.as_ref().unwrap().to_pub(), + config_v0.user.as_ref().unwrap().clone(), + BindAddress { + port: config_v0.port, + ip: (&config_v0.ip).into(), + }, + cmd, + ) .await - .err() - .unwrap(), - ProtocolError::UserAlreadyExists - ); - - let repo = RepoLink::V0(RepoLinkV0 { - id: PubKey::Ed25519PubKey([1; 32]), - secret: SymKey::ChaCha20Key([0; 32]), - peers: vec![], - }); - let mut public_overlay_cnx = cnx.overlay_connect(&repo, true).await?; - - log_debug!("put_block"); - - let my_block_id = public_overlay_cnx - .put_block(&Block::new( - vec![], - ObjectDeps::ObjectIdList(vec![]), - None, - vec![27; 150], - None, - )) - .await?; - - log_debug!("added block_id to store {}", my_block_id); - - let object_id = public_overlay_cnx - .put_object( - ObjectContent::File(File::V0(FileV0 { - content_type: vec![], - metadata: vec![], - content: vec![48; 69000], - })), - vec![], - None, - block_size(), - repo.id(), - repo.secret(), - ) - .await?; - - log_debug!("added object_id to store {}", object_id); - - let mut my_block_stream = public_overlay_cnx - .get_block(my_block_id, true, None) - .await?; - //.expect("get_block failed"); - - while let Some(b) = my_block_stream.next().await { - log_debug!("GOT BLOCK {}", b.id()); } - let mut my_object_stream = public_overlay_cnx.get_block(object_id, true, None).await?; - //.expect("get_block for object failed"); - - while let Some(b) = my_object_stream.next().await { - log_debug!("GOT BLOCK {}", b.id()); + //log_debug!("{:?}", config); + match matches.subcommand() { + Some(("admin", sub_matches)) => match sub_matches.subcommand() { + Some(("add-user", sub2_matches)) => { + log_debug!("add-user"); + let res = do_admin_call( + keys[1], + config_v0, + AddUser::V0(AddUserV0 { + user: sub2_matches + .get_one::("USER_ID") + .unwrap() + .as_str() + .try_into() + .map_err(|_| { + log_err!("supplied USER_ID is invalid"); + ProtocolError::InvalidValue + })?, + is_admin: sub2_matches.get_flag("admin"), + }), + ) + .await; + match &res { + Err(e) => log_err!("An error occurred: {e}"), + Ok(_) => println!("User added successfully"), + } + return res.map(|_| ()); + } + Some(("list-users", sub2_matches)) => { + log_debug!("list-users"); + let admins = sub2_matches.get_flag("admin"); + let res = + do_admin_call(keys[1], config_v0, ListUsers::V0(ListUsersV0 { admins })).await; + match &res { + Err(e) => log_err!("An error occurred: {e}"), + Ok(AdminResponseContentV0::Users(list)) => { + println!( + "Found {} {}users", + list.len(), + if admins { "admin " } else { "" } + ); + for user in list { + println!("{user}"); + } + } + _ => { + log_err!("Invalid response"); + return Err(ProtocolError::InvalidValue); + } + } + return res.map(|_| ()); + } + _ => panic!("shouldn't happen"), + }, + _ => println!("Nothing to do."), } - let object = public_overlay_cnx.get_object(object_id, None).await?; - //.expect("get_object failed"); - - log_debug!("GOT OBJECT with ID {}", object.id()); - - // let object_id = public_overlay_cnx - // .copy_object(object_id, Some(now_timestamp() + 60)) - // .await - // .expect("copy_object failed"); - - // log_debug!("COPIED OBJECT to OBJECT ID {}", object_id); - - public_overlay_cnx.delete_object(object_id).await?; - //.expect("delete_object failed"); - - let res = public_overlay_cnx - .get_object(object_id, None) - .await - .unwrap_err(); - - log_debug!("result from get object after delete: {}", res); - assert_eq!(res, ProtocolError::NotFound); - - //TODO test pin/unpin - - // TEST BRANCH SYNC - - test_sync(cnx, pub_key, priv_key).await; - Ok(()) } -async fn test_local_connection() { - log_debug!("===== TESTING LOCAL API ====="); - - let root = tempfile::Builder::new().prefix("ngcli").tempdir().unwrap(); - let master_key: [u8; 32] = [0; 32]; - std::fs::create_dir_all(root.path()).unwrap(); - log_debug!("{}", root.path().to_str().unwrap()); - let store = LmdbKCVStore::open(root.path(), master_key); - - //let mut server = BrokerServer::new(store, ConfigMode::Local).expect("starting broker"); +// #[cfg(test)] +// mod test { - let (priv_key, pub_key) = generate_keypair(); +// #[async_std::test] +// pub async fn test_local_cnx() {} - // let mut cnx = server.local_connection(pub_key); +// //test_local_connection().await; - // test(&mut cnx, pub_key, priv_key).await; -} - -async fn test_remote_connection(url: &str) { - log_debug!("===== TESTING REMOTE API ====="); - - let (priv_key, pub_key) = generate_keypair(); +// //test_remote_connection("ws://127.0.0.1:3012").await; - // open cnx - - // test(&mut cnx, pub_key, priv_key).await; -} +// use async_std::task; +// use p2p_broker::server_ws::*; +// use p2p_net::utils::gen_dh_keys; +// use p2p_net::WS_PORT; +// use p2p_repo::log::*; +// use p2p_repo::types::PubKey; -#[async_std::main] -async fn main() -> std::io::Result<()> { - log_debug!("Starting nextgraph CLI..."); - - //test_local_connection().await; - - //test_remote_connection("ws://127.0.0.1:3012").await; - - Ok(()) -} - -#[cfg(test)] -mod test { - - use crate::{test_local_connection, test_remote_connection}; - - #[async_std::test] - pub async fn test_local_cnx() {} - - use async_std::task; - use p2p_broker::server_ws::*; - use p2p_net::utils::gen_dh_keys; - use p2p_net::WS_PORT; - use p2p_repo::log::*; - use p2p_repo::types::PubKey; - - #[async_std::test] - pub async fn test_remote_cnx() -> Result<(), Box> { - let keys = gen_dh_keys(); - // log_debug!("Public key of node: {:?}", keys.1); - // log_debug!("Private key of node: {:?}", keys.0.as_slice()); - - log_debug!("Public key of node: {}", keys.1); - log_debug!("Private key of node: {}", keys.0); - - let thr = task::spawn(run_server_accept_one("127.0.0.1", WS_PORT, keys.0, pubkey)); - - // time for the server to start - std::thread::sleep(std::time::Duration::from_secs(2)); - - test_remote_connection("ws://127.0.0.1:3012"); - - thr.await; - - Ok(()) - } -} +// //#[async_std::test] +// } diff --git a/ngcli/src/old.rs b/ngcli/src/old.rs new file mode 100644 index 0000000..46961a2 --- /dev/null +++ b/ngcli/src/old.rs @@ -0,0 +1,587 @@ +fn block_size() -> usize { + store_max_value_size() + //store_valid_value_size(0) +} + +async fn test_sync(cnx: &mut impl BrokerConnection, user_pub_key: PubKey, userpriv_key: PrivKey) { + fn add_obj( + content: ObjectContent, + deps: Vec, + expiry: Option, + repo_pubkey: PubKey, + repo_secret: SymKey, + store: &mut impl RepoStore, + ) -> ObjectRef { + let max_object_size = 4000; + let obj = Object::new( + content, + deps, + expiry, + max_object_size, + repo_pubkey, + repo_secret, + ); + //log_debug!(">>> add_obj"); + log_debug!(" id: {}", obj.id()); + //log_debug!(" deps: {:?}", obj.deps()); + obj.save(store).unwrap(); + obj.reference().unwrap() + } + + fn add_commit( + branch: ObjectRef, + author_privkey: PrivKey, + author_pubkey: PubKey, + seq: u32, + deps: Vec, + acks: Vec, + body_ref: ObjectRef, + repo_pubkey: PubKey, + repo_secret: SymKey, + store: &mut impl RepoStore, + ) -> 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 obj_ref = ObjectRef { + id: ObjectId::Blake3Digest32([1; 32]), + key: SymKey::ChaCha20Key([2; 32]), + }; + let refs = vec![obj_ref]; + let metadata = vec![5u8; 55]; + let expiry = None; + + let commit = Commit::new( + author_privkey, + author_pubkey, + seq, + branch, + deps, + acks, + refs, + metadata, + body_ref, + expiry, + ) + .unwrap(); + //log_debug!("commit: {}", commit.id().unwrap()); + add_obj( + ObjectContent::Commit(commit), + obj_deps, + expiry, + repo_pubkey, + repo_secret, + store, + ) + } + + fn add_body_branch( + branch: Branch, + repo_pubkey: PubKey, + repo_secret: SymKey, + store: &mut impl RepoStore, + ) -> ObjectRef { + let deps = vec![]; + let expiry = None; + let body = CommitBody::Branch(branch); + //log_debug!("body: {:?}", body); + add_obj( + ObjectContent::CommitBody(body), + deps, + expiry, + repo_pubkey, + repo_secret, + store, + ) + } + + fn add_body_trans( + deps: Vec, + repo_pubkey: PubKey, + repo_secret: SymKey, + store: &mut impl RepoStore, + ) -> 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()); + //log_debug!("body: {:?}", body); + add_obj( + ObjectContent::CommitBody(body), + deps, + expiry, + repo_pubkey, + repo_secret, + store, + ) + } + + let mut store = HashMapRepoStore::new(); + let mut rng = OsRng {}; + + // repo + + let repo_keypair: Keypair = Keypair::generate(&mut rng); + // log_debug!( + // "repo private key: ({}) {:?}", + // repo_keypair.secret.as_bytes().len(), + // repo_keypair.secret.as_bytes() + // ); + // log_debug!( + // "repo public key: ({}) {:?}", + // repo_keypair.public.as_bytes().len(), + // repo_keypair.public.as_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]); + + let repolink = RepoLink::V0(RepoLinkV0 { + id: repo_pubkey, + secret: repo_secret, + peers: vec![], + }); + + // branch + + let branch_keypair: Keypair = Keypair::generate(&mut rng); + //log_debug!("branch public key: {:?}", branch_keypair.public.as_bytes()); + let branch_pubkey = PubKey::Ed25519PubKey(branch_keypair.public.to_bytes()); + + let member_keypair: Keypair = Keypair::generate(&mut rng); + //log_debug!("member public key: {:?}", member_keypair.public.as_bytes()); + let member_privkey = PrivKey::Ed25519PrivKey(member_keypair.secret.to_bytes()); + 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, + branch_pubkey, + secret, + members, + quorum, + ack_delay, + tags, + metadata, + ); + //log_debug!("branch: {:?}", branch); + + log_debug!("branch deps/acks:"); + log_debug!(""); + log_debug!(" br"); + log_debug!(" / \\"); + log_debug!(" t1 t2"); + log_debug!(" / \\ / \\"); + log_debug!(" a3 t4<--t5-->(t1)"); + log_debug!(" / \\"); + log_debug!(" a6 a7"); + log_debug!(""); + + // commit bodies + + let branch_body = add_body_branch( + branch.clone(), + repo_pubkey.clone(), + repo_secret.clone(), + &mut store, + ); + let ack_body = add_body_ack(vec![], repo_pubkey, repo_secret, &mut store); + let trans_body = add_body_trans(vec![], repo_pubkey, repo_secret, &mut store); + + // create & add commits to store + + log_debug!(">> br"); + let br = add_commit( + branch_body, + member_privkey, + member_pubkey, + 0, + vec![], + vec![], + branch_body, + repo_pubkey, + repo_secret, + &mut store, + ); + + log_debug!(">> t1"); + let t1 = add_commit( + branch_body, + member_privkey, + member_pubkey, + 1, + vec![br], + vec![], + trans_body, + repo_pubkey, + repo_secret, + &mut store, + ); + + log_debug!(">> t2"); + let t2 = add_commit( + branch_body, + member_privkey, + member_pubkey, + 2, + vec![br], + vec![], + trans_body, + repo_pubkey, + repo_secret, + &mut store, + ); + + log_debug!(">> a3"); + let a3 = add_commit( + branch_body, + member_privkey, + member_pubkey, + 3, + vec![t1], + vec![], + ack_body, + repo_pubkey, + repo_secret, + &mut store, + ); + + log_debug!(">> t4"); + let t4 = add_commit( + branch_body, + member_privkey, + member_pubkey, + 4, + vec![t2], + vec![t1], + trans_body, + repo_pubkey, + repo_secret, + &mut store, + ); + + log_debug!(">> t5"); + let t5 = add_commit( + branch_body, + member_privkey, + member_pubkey, + 5, + vec![t1, t2], + vec![t4], + trans_body, + repo_pubkey, + repo_secret, + &mut store, + ); + + log_debug!(">> a6"); + let a6 = add_commit( + branch_body, + member_privkey, + member_pubkey, + 6, + vec![t4], + vec![], + ack_body, + repo_pubkey, + repo_secret, + &mut store, + ); + + log_debug!(">> a7"); + let a7 = add_commit( + branch_body, + member_privkey, + member_pubkey, + 7, + vec![t4], + vec![], + ack_body, + repo_pubkey, + repo_secret, + &mut store, + ); + + let mut public_overlay_cnx = cnx + .overlay_connect(&repolink, true) + .await + .expect("overlay_connect failed"); + + // Sending everything to the broker + for (v) in store.get_all() { + //log_debug!("SENDING {}", k); + let _ = public_overlay_cnx + .put_block(&v) + .await + .expect("put_block failed"); + } + + // Now emptying the local store of the client, and adding only 1 commit into it (br) + // we also have received an commit (t5) but we don't know what to do with it... + let mut store = HashMapRepoStore::new(); + + let br = add_commit( + branch_body, + member_privkey, + member_pubkey, + 0, + vec![], + vec![], + branch_body, + repo_pubkey, + repo_secret, + &mut store, + ); + + let t5 = add_commit( + branch_body, + member_privkey, + member_pubkey, + 5, + vec![t1, t2], + vec![t4], + trans_body, + repo_pubkey, + repo_secret, + &mut store, + ); + + log_debug!("LOCAL STORE HAS {} BLOCKS", store.get_len()); + + // Let's pretend that we know that the head of the branch in the broker is at commits a6 and a7. + // normally it would be the pub/sub that notifies us of those heads. + // now we want to synchronize with the broker. + + let mut filter = Filter::new(FilterBuilder::new(10, 0.01)); + for commit_ref in [br, t5] { + match commit_ref.id { + ObjectId::Blake3Digest32(d) => filter.add(&d), + } + } + let cfg = filter.config(); + + let known_commits = BloomFilter { + k: cfg.hashes, + f: filter.get_u8_array().to_vec(), + }; + + let known_heads = [br.id]; + + let remote_heads = [a6.id, a7.id]; + + let mut synced_blocks_stream = public_overlay_cnx + .sync_branch(remote_heads.to_vec(), known_heads.to_vec(), known_commits) + .await + .expect("sync_branch failed"); + + let mut i = 0; + while let Some(b) = synced_blocks_stream.next().await { + log_debug!("GOT BLOCK {}", b.id()); + store.put(&b); + i += 1; + } + + log_debug!("SYNCED {} BLOCKS", i); + + log_debug!("LOCAL STORE HAS {} BLOCKS", store.get_len()); + + // now the client can verify the DAG and each commit. Then update its list of heads. +} + +async fn test( + cnx: &mut impl BrokerConnection, + pub_key: PubKey, + priv_key: PrivKey, +) -> Result<(), ProtocolError> { + cnx.add_user(PubKey::Ed25519PubKey([1; 32]), priv_key) + .await?; + + cnx.add_user(pub_key, priv_key).await?; + //.expect("add_user 2 (myself) failed"); + + assert_eq!( + cnx.add_user(PubKey::Ed25519PubKey([1; 32]), priv_key) + .await + .err() + .unwrap(), + ProtocolError::UserAlreadyExists + ); + + let repo = RepoLink::V0(RepoLinkV0 { + id: PubKey::Ed25519PubKey([1; 32]), + secret: SymKey::ChaCha20Key([0; 32]), + peers: vec![], + }); + let mut public_overlay_cnx = cnx.overlay_connect(&repo, true).await?; + + log_debug!("put_block"); + + let my_block_id = public_overlay_cnx + .put_block(&Block::new( + vec![], + ObjectDeps::ObjectIdList(vec![]), + None, + vec![27; 150], + None, + )) + .await?; + + log_debug!("added block_id to store {}", my_block_id); + + let object_id = public_overlay_cnx + .put_object( + ObjectContent::File(File::V0(FileV0 { + content_type: vec![], + metadata: vec![], + content: vec![48; 69000], + })), + vec![], + None, + block_size(), + repo.id(), + repo.secret(), + ) + .await?; + + log_debug!("added object_id to store {}", object_id); + + let mut my_block_stream = public_overlay_cnx + .get_block(my_block_id, true, None) + .await?; + //.expect("get_block failed"); + + while let Some(b) = my_block_stream.next().await { + log_debug!("GOT BLOCK {}", b.id()); + } + + let mut my_object_stream = public_overlay_cnx.get_block(object_id, true, None).await?; + //.expect("get_block for object failed"); + + while let Some(b) = my_object_stream.next().await { + log_debug!("GOT BLOCK {}", b.id()); + } + + let object = public_overlay_cnx.get_object(object_id, None).await?; + //.expect("get_object failed"); + + log_debug!("GOT OBJECT with ID {}", object.id()); + + // let object_id = public_overlay_cnx + // .copy_object(object_id, Some(now_timestamp() + 60)) + // .await + // .expect("copy_object failed"); + + // log_debug!("COPIED OBJECT to OBJECT ID {}", object_id); + + public_overlay_cnx.delete_object(object_id).await?; + //.expect("delete_object failed"); + + let res = public_overlay_cnx + .get_object(object_id, None) + .await + .unwrap_err(); + + log_debug!("result from get object after delete: {}", res); + assert_eq!(res, ProtocolError::NotFound); + + //TODO test pin/unpin + + // TEST BRANCH SYNC + + test_sync(cnx, pub_key, priv_key).await; + + Ok(()) +} + +async fn test_local_connection() { + log_debug!("===== TESTING LOCAL API ====="); + + let root = tempfile::Builder::new().prefix("ngcli").tempdir().unwrap(); + let master_key: [u8; 32] = [0; 32]; + std::fs::create_dir_all(root.path()).unwrap(); + log_debug!("{}", root.path().to_str().unwrap()); + let store = LmdbKCVStore::open(root.path(), master_key); + + //let mut server = BrokerServer::new(store, ConfigMode::Local).expect("starting broker"); + + let (priv_key, pub_key) = generate_keypair(); + + // let mut cnx = server.local_connection(pub_key); + + // test(&mut cnx, pub_key, priv_key).await; +} + +async fn test_remote_connection(url: &str) { + log_debug!("===== TESTING REMOTE API ====="); + + let (priv_key, pub_key) = generate_keypair(); + + // open cnx + + // test(&mut cnx, pub_key, priv_key).await; +} + +#[cfg(test)] +mod test { + + use crate::{test_local_connection, test_remote_connection}; + + #[async_std::test] + pub async fn test_local_cnx() {} + + use async_std::task; + use p2p_broker::server_ws::*; + use p2p_net::utils::gen_dh_keys; + use p2p_net::WS_PORT; + use p2p_repo::log::*; + use p2p_repo::types::PubKey; + + #[async_std::test] + pub async fn test_remote_cnx() -> Result<(), Box> { + let keys = gen_dh_keys(); + // log_debug!("Public key of node: {:?}", keys.1); + // log_debug!("Private key of node: {:?}", keys.0.as_slice()); + + log_debug!("Public key of node: {}", keys.1); + log_debug!("Private key of node: {}", keys.0); + + let thr = task::spawn(run_server_accept_one("127.0.0.1", WS_PORT, keys.0, pubkey)); + + // time for the server to start + std::thread::sleep(std::time::Duration::from_secs(2)); + + test_remote_connection("ws://127.0.0.1:3012"); + + thr.await; + + Ok(()) + } +} diff --git a/ngd/src/cli.rs b/ngd/src/cli.rs index b547060..1de91ed 100644 --- a/ngd/src/cli.rs +++ b/ngd/src/cli.rs @@ -31,7 +31,7 @@ pub(crate) struct Cli { #[arg(short, long, env = "NG_SERVER_KEY")] pub key: Option, - /// Saves to disk the provided or automatically generated key. Only used if file storage is secure. Alternatives are passing the key at every start with --key or NG_SERVER_KEY env var. + /// Saves to disk the provided or automatically generated key. Only use if file storage is secure. Alternatives are passing the key at every start with --key or NG_SERVER_KEY env var. #[arg(long)] pub save_key: bool, @@ -47,7 +47,7 @@ pub(crate) struct Cli { #[arg(long, requires("core"))] pub core_with_clients: bool, - /// Quick config to forward all requests to another BROKER. format is "[DOMAIN/IP:PORT]@PEERID". An IPv6 should be encased in square brackets [IPv6] and the whole option should be between double quotes. Port defaults to 80 for IPs and 443 for domains + /// Quick config to forward all requests to another BROKER. format is "[DOMAIN/IP:PORT]@PEER_ID". An IPv6 should be encased in square brackets [IPv6] and the whole option should be between double quotes. Port defaults to 80 for IPs and 443 for domains #[arg( short, long, diff --git a/ngd/src/main.rs b/ngd/src/main.rs index d70f032..2448ed3 100644 --- a/ngd/src/main.rs +++ b/ngd/src/main.rs @@ -450,6 +450,7 @@ async fn main_inner() -> Result<(), ()> { || args.public.is_some() || args.dynamic.is_some() || args.domain.is_some() + || args.domain_private.is_some() { // QUICK CONFIG @@ -869,7 +870,7 @@ async fn main_inner() -> Result<(), ()> { return Err(()); } let pub_key_array = decode_key(parts[1]) - .map_err(|_| log_err!("The PEERID provided in the --forward option is invalid"))?; + .map_err(|_| log_err!("The PEER_ID provided in the --forward option is invalid"))?; let peer_id = PubKey::Ed25519PubKey(pub_key_array); let server_type = if parts[0].len() > 0 { diff --git a/p2p-broker/src/broker_store/account.rs b/p2p-broker/src/broker_store/account.rs index ae7dc18..f24d24d 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::store::*; use p2p_repo::types::Timestamp; -use serde_bare::to_vec; +use serde_bare::{from_slice, to_vec}; pub struct Account<'a> { /// User ID @@ -65,7 +65,7 @@ impl<'a> Account<'a> { store, }; if acc.exists() { - return Err(StorageError::BackendError); + return Err(StorageError::AlreadyExists); } store.put( Self::PREFIX, @@ -75,6 +75,21 @@ impl<'a> Account<'a> { )?; Ok(acc) } + pub fn get_all_users( + admins: bool, + store: &'a dyn KCVStore, + ) -> Result, StorageError> { + let size = to_vec(&UserId::nil())?.len(); + let mut res: Vec = vec![]; + for user in store.get_all_keys_and_values(Self::PREFIX, size, Some(Self::ADMIN))? { + let admin: bool = from_slice(&user.1)?; + if admin == admins { + let id: UserId = from_slice(&user.0[1..user.0.len() - 1])?; + res.push(id); + } + } + Ok(res) + } pub fn exists(&self) -> bool { self.store .get( diff --git a/p2p-broker/src/server_ws.rs b/p2p-broker/src/server_ws.rs index 475f475..ad3423d 100644 --- a/p2p-broker/src/server_ws.rs +++ b/p2p-broker/src/server_ws.rs @@ -595,7 +595,7 @@ pub async fn run_server_accept_one( let tcp = connections.next().await.unwrap()?; { - BROKER.write().await.set_my_peer_id(peer_pub_key); + //BROKER.write().await.set_my_peer_id(peer_pub_key); } accept(tcp, peer_priv_key).await; @@ -779,12 +779,17 @@ pub async fn run_server_v0( .map_err(|e| log_err!("Error while opening broker storage: {:?}", e))?; let mut broker = BROKER.write().await; - broker.set_my_peer_id(peer_id); broker.set_storage(broker_storage); LISTENERS_INFO .set(broker.set_listeners(listener_infos)) .unwrap(); - broker.set_overlays_configs(config.overlays_configs); + let server_config = ServerConfig { + overlays_configs: config.overlays_configs, + registration: config.registration, + admin_user: config.admin_user, + peer_id, + }; + broker.set_server_config(server_config); } // Actually starting the listeners diff --git a/p2p-broker/src/storage.rs b/p2p-broker/src/storage.rs index b7878fd..e7a3a3d 100644 --- a/p2p-broker/src/storage.rs +++ b/p2p-broker/src/storage.rs @@ -11,18 +11,21 @@ use std::path::PathBuf; +use crate::broker_store::account::Account; use crate::broker_store::invitation::Invitation; use crate::broker_store::wallet::Wallet; use crate::types::*; use p2p_net::broker_storage::*; +use p2p_net::errors::ProtocolError; use p2p_net::types::{BootstrapContentV0, InvitationCode, InvitationV0}; use p2p_repo::kcv_store::KCVStore; use p2p_repo::log::*; use p2p_repo::store::StorageError; -use p2p_repo::types::SymKey; +use p2p_repo::types::{PubKey, SymKey}; use stores_lmdb::kcv_store::LmdbKCVStore; use stores_lmdb::repo_store::LmdbRepoStore; +#[derive(Debug)] pub struct LmdbBrokerStorage { wallet_storage: LmdbKCVStore, accounts_storage: LmdbKCVStore, @@ -94,5 +97,17 @@ impl LmdbBrokerStorage { } impl BrokerStorage for LmdbBrokerStorage { - fn get_user(&self) {} + fn get_user(&self, user_id: PubKey) -> Result { + log_debug!("get_user {user_id}"); + Ok(Account::open(&user_id, &self.accounts_storage)?.is_admin()?) + } + fn add_user(&self, user_id: PubKey, is_admin: bool) -> Result<(), ProtocolError> { + log_debug!("add_user {user_id} is admin {is_admin}"); + Account::create(&user_id, is_admin, &self.accounts_storage)?; + Ok(()) + } + fn list_users(&self, admins: bool) -> Result, ProtocolError> { + log_debug!("list_users that are admin == {admins}"); + Ok(Account::get_all_users(admins, &self.accounts_storage)?) + } } diff --git a/p2p-broker/src/types.rs b/p2p-broker/src/types.rs index 4a5274f..757df4a 100644 --- a/p2p-broker/src/types.rs +++ b/p2p-broker/src/types.rs @@ -6,18 +6,10 @@ // at your option. All files in the project carrying such // notice may not be copied, modified, or distributed except // according to those terms. -use p2p_net::types::{BrokerOverlayConfigV0, ListenerV0}; +use p2p_net::types::{BrokerOverlayConfigV0, ListenerV0, RegistrationConfig}; use p2p_repo::types::{PrivKey, PubKey}; use serde::{Deserialize, Serialize}; -/// Registration config -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum RegistrationConfig { - Closed, - Invitation, - Open, -} - /// DaemonConfig Version 0 #[derive(Clone, Debug, Serialize, Deserialize)] pub struct DaemonConfigV0 { diff --git a/p2p-client-ws/Cargo.toml b/p2p-client-ws/Cargo.toml index 565178e..7a674fd 100644 --- a/p2p-client-ws/Cargo.toml +++ b/p2p-client-ws/Cargo.toml @@ -33,5 +33,4 @@ features = ["js"] [target.'cfg(not(target_arch = "wasm32"))'.dependencies] getrandom = "0.2.7" -xactor = "0.7.11" async-tungstenite = { git = "https://git.nextgraph.org/NextGraph/async-tungstenite.git", branch = "nextgraph", features = ["async-std-runtime"] } diff --git a/p2p-client-ws/src/lib.rs b/p2p-client-ws/src/lib.rs index 561f1ff..784ed1e 100644 --- a/p2p-client-ws/src/lib.rs +++ b/p2p-client-ws/src/lib.rs @@ -6,44 +6,6 @@ // notice may not be copied, modified, or distributed except // according to those terms. -#[macro_export] -macro_rules! before { - ( $self:expr, $request_id:ident, $addr:ident, $receiver:ident ) => { - let mut actor = BrokerMessageActor::new(); - let $receiver = actor.receiver(); - let mut $addr = actor - .start() - .await - .map_err(|_e| ProtocolError::ActorError)?; - - let $request_id = $addr.actor_id(); - //log_debug!("actor ID {}", $request_id); - - { - let mut map = $self.actors.write().expect("RwLock poisoned"); - map.insert($request_id, $addr.downgrade()); - } - }; -} - -macro_rules! after { - ( $self:expr, $request_id:ident, $addr:ident, $receiver:ident, $reply:ident ) => { - //log_debug!("waiting for reply"); - - $addr.wait_for_stop().await; // TODO add timeout and close connection if there's no reply - let r = $receiver.await; - if r.is_err() { - return Err(ProtocolError::Closing); - } - let $reply = r.unwrap(); - //log_debug!("reply arrived {:?}", $reply); - { - let mut map = $self.actors.write().expect("RwLock poisoned"); - map.remove(&$request_id); - } - }; -} - #[cfg(not(target_arch = "wasm32"))] pub mod remote_ws; diff --git a/p2p-client-ws/src/remote_ws.rs b/p2p-client-ws/src/remote_ws.rs index f3f1e58..9e82e37 100644 --- a/p2p-client-ws/src/remote_ws.rs +++ b/p2p-client-ws/src/remote_ws.rs @@ -166,7 +166,7 @@ async fn close_ws( code: u16, reason: &str, ) -> Result<(), NetError> { - log_info!("close_ws {:?}", code); + log_debug!("close_ws {:?}", code); let cmd = if code == 1000 { ConnectionCommand::Close @@ -177,7 +177,7 @@ async fn close_ws( } else { ConnectionCommand::Error(NetError::try_from(code - 4949).unwrap()) }; - log_info!("sending to read loop {:?}", cmd); + log_debug!("sending to read loop {:?}", cmd); let _ = futures::SinkExt::send(receiver, cmd).await; stream @@ -206,11 +206,11 @@ async fn ws_loop( select! { r = stream.next().fuse() => match r { Some(Ok(msg)) => { - //log_info!("GOT MESSAGE {:?}", msg); + //log_debug!("GOT MESSAGE {:?}", msg); if msg.is_close() { if let Message::Close(Some(cf)) = msg { - log_info!("CLOSE from remote with closeframe: {}",cf.reason); + log_debug!("CLOSE from remote with closeframe: {}",cf.reason); let last_command = match cf.code { CloseCode::Normal => ConnectionCommand::Close, @@ -229,7 +229,7 @@ async fn ws_loop( } else { let _ = futures::SinkExt::send(receiver, ConnectionCommand::Close).await; - log_info!("CLOSE from remote"); + log_debug!("CLOSE from remote"); } return Ok(ProtocolError::Closing); } else { @@ -237,12 +237,12 @@ async fn ws_loop( .map_err(|_e| NetError::IoError)?; } }, - Some(Err(e)) => {log_info!("GOT ERROR {:?}",e);return Err(NetError::WsError);}, + Some(Err(e)) => {log_debug!("GOT ERROR {:?}",e);return Err(NetError::WsError);}, None => break }, s = sender.next().fuse() => match s { Some(msg) => { - //log_info!("SENDING MESSAGE {:?}", msg); + //log_debug!("SENDING MESSAGE {:?}", msg); match msg { ConnectionCommand::Msg(m) => { futures::SinkExt::send(&mut stream,Message::binary(serde_bare::to_vec(&m)?)).await.map_err(|_e| NetError::IoError)?; @@ -255,6 +255,9 @@ async fn ws_loop( }, ConnectionCommand::Close => { break; + }, + ConnectionCommand::ReEnter => { + //do nothing. loop } } }, @@ -267,7 +270,7 @@ async fn ws_loop( match inner_loop(&mut ws, sender, &mut receiver).await { Ok(proto_err) => { if proto_err == ProtocolError::Closing { - log_info!("ProtocolError::Closing"); + log_debug!("ProtocolError::Closing"); let _ = ws.close(None).await; } else if proto_err == ProtocolError::NoError { close_ws(&mut ws, &mut receiver, 1000, "").await?; @@ -316,12 +319,12 @@ mod test { let keys = generate_keypair(); let x_from_ed = keys.1.to_dh_from_ed(); - log_info!("Pub from X {}", x_from_ed); + log_debug!("Pub from X {}", x_from_ed); let (client_priv, client) = generate_keypair(); let (user_priv, user) = generate_keypair(); - log_info!("start connecting"); + log_debug!("start connecting"); { let res = BROKER .write() @@ -341,7 +344,7 @@ mod test { }), ) .await; - log_info!("broker.connect : {:?}", res); + log_debug!("broker.connect : {:?}", res); res.expect("assume the connection succeeds"); } @@ -350,7 +353,7 @@ mod test { async fn timer_close(remote_peer_id: DirectPeerId, user: Option) -> ResultSend<()> { async move { sleep!(std::time::Duration::from_secs(3)); - log_info!("timeout"); + log_debug!("timeout"); BROKER .write() .await @@ -370,7 +373,7 @@ mod test { #[async_std::test] pub async fn probe() -> Result<(), NgError> { - log_info!("start probe"); + log_debug!("start probe"); { let res = BROKER .write() @@ -381,7 +384,7 @@ mod test { WS_PORT, ) .await; - log_info!("broker.probe : {:?}", res); + log_debug!("broker.probe : {:?}", res); res.expect("assume the probe succeeds"); } diff --git a/p2p-client-ws/src/remote_ws_wasm.rs b/p2p-client-ws/src/remote_ws_wasm.rs index d371387..ac5fc8a 100644 --- a/p2p-client-ws/src/remote_ws_wasm.rs +++ b/p2p-client-ws/src/remote_ws_wasm.rs @@ -47,7 +47,7 @@ impl IConnect for ConnectionWebSocket { let mut cnx = ConnectionBase::new(ConnectionDir::Client, TransportProtocol::WS); let (mut ws, wsio) = WsMeta::connect(url, None).await.map_err(|e| { - //log_info!("{:?}", e); + //log_debug!("{:?}", e); NetError::ConnectionError })?; @@ -71,7 +71,7 @@ impl IConnect for ConnectionWebSocket { let url = format!("ws://{}:{}", ip, port); let (mut ws, wsio) = WsMeta::connect(url, None).await.map_err(|e| { - //log_info!("{:?}", e); + //log_debug!("{:?}", e); ProtocolError::ConnectionError })?; @@ -107,7 +107,7 @@ async fn ws_loop( select! { r = stream.next().fuse() => match r { Some(msg) => { - log_info!("GOT MESSAGE {:?}", msg); + log_debug!("GOT MESSAGE {:?}", msg); if let WsMessage::Binary(b) = msg { receiver.send(ConnectionCommand::Msg(serde_bare::from_slice::(&b)?)).await .map_err(|_e| NetError::IoError)?; @@ -120,11 +120,11 @@ async fn ws_loop( }, s = sender.next().fuse() => match s { Some(msg) => { - log_info!("SENDING MESSAGE {:?}", msg); + log_debug!("SENDING MESSAGE {:?}", msg); match msg { ConnectionCommand::Msg(m) => { - stream.send(WsMessage::Binary(serde_bare::to_vec(&m)?)).await.map_err(|e| { log_info!("{:?}",e); return NetError::IoError;})?; + stream.send(WsMessage::Binary(serde_bare::to_vec(&m)?)).await.map_err(|e| { log_debug!("{:?}",e); return NetError::IoError;})?; }, ConnectionCommand::Error(e) => { @@ -135,6 +135,9 @@ async fn ws_loop( }, ConnectionCommand::Close => { break; + }, + ConnectionCommand::ReEnter => { + //do nothing. loop } } }, @@ -144,7 +147,7 @@ async fn ws_loop( } Ok(ProtocolError::NoError) } - log_info!("START of WS loop"); + log_debug!("START of WS loop"); let mut events = ws .observe(ObserveConfig::default()) //.observe(Filter::Pointer(WsEvent::is_closed).into()) @@ -154,9 +157,9 @@ async fn ws_loop( Ok(proto_err) => { if proto_err == ProtocolError::NoError { let _ = ws.close_code(1000).await; //.map_err(|_e| NetError::WsError)?; - log_info!("CLOSED GRACEFULLY"); + log_debug!("CLOSED GRACEFULLY"); } else { - log_info!("PROTOCOL ERR"); + log_debug!("PROTOCOL ERR"); let mut code = proto_err.clone() as u16; if code > 949 { code = ProtocolError::OtherError as u16; @@ -172,12 +175,12 @@ async fn ws_loop( .await; //.map_err(|_e| NetError::WsError)?; //return Err(Box::new(e)); - log_info!("ERR {:?}", e); + log_debug!("ERR {:?}", e); } } let last_event = events.next().await; - log_info!("WS closed {:?}", last_event.clone()); + log_debug!("WS closed {:?}", last_event.clone()); let last_command = match last_event { None => ConnectionCommand::Close, Some(WsEvent::Open) => ConnectionCommand::Error(NetError::WsError), // this should never happen @@ -210,6 +213,6 @@ async fn ws_loop( .await .map_err(|_e| NetError::IoError)?; - log_info!("END of WS loop"); + log_debug!("END of WS loop"); Ok(()) } diff --git a/p2p-net/src/actor.rs b/p2p-net/src/actor.rs index aee3c37..106b306 100644 --- a/p2p-net/src/actor.rs +++ b/p2p-net/src/actor.rs @@ -120,7 +120,7 @@ impl< let mut receiver = self.receiver.take().unwrap(); match receiver.next().await { Some(ConnectionCommand::Msg(msg)) => { - if let ProtocolMessage::BrokerMessage(ref bm) = msg { + if let ProtocolMessage::ClientMessage(ref bm) = msg { if bm.result() == Into::::into(ProtocolError::PartialContent) && TypeId::of::() != TypeId::of::<()>() { @@ -140,7 +140,7 @@ impl< while let Some(ConnectionCommand::Msg(msg)) = actor_receiver.next().await { - if let ProtocolMessage::BrokerMessage(ref bm) = msg { + if let ProtocolMessage::ClientMessage(ref bm) = msg { if bm.result() == Into::::into(ProtocolError::EndOfStream) { @@ -155,7 +155,7 @@ impl< break; } } else { - // todo deal with error (not a brokermessage) + // todo deal with error (not a ClientMessage) break; } } @@ -177,6 +177,9 @@ impl< let response: B = msg.try_into()?; Ok(SoS::::Single(response)) } + Some(ConnectionCommand::ProtocolError(e)) => Err(e), + Some(ConnectionCommand::Error(e)) => Err(e.into()), + Some(ConnectionCommand::Close) => Err(ProtocolError::Closing), _ => Err(ProtocolError::ActorError), } } diff --git a/p2p-net/src/actors/add_user.rs b/p2p-net/src/actors/add_user.rs new file mode 100644 index 0000000..5c5ccb8 --- /dev/null +++ b/p2p-net/src/actors/add_user.rs @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2022-2023 Niko Bonnieure, Par le Peuple, NextGraph.org developers + * All rights reserved. + * Licensed under the Apache License, Version 2.0 + * + * or the MIT license , + * 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 p2p_repo::log::*; +use p2p_repo::types::PubKey; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; + +use super::StartProtocol; + +/// Add user account +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +pub struct AddUserV0 { + /// User pub key + pub user: PubKey, + /// should the newly added user be an admin of the server + pub is_admin: bool, +} + +/// Add user account +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +pub enum AddUser { + V0(AddUserV0), +} + +impl AddUser { + pub fn user(&self) -> PubKey { + match self { + AddUser::V0(o) => o.user, + } + } + pub fn is_admin(&self) -> bool { + match self { + AddUser::V0(o) => o.is_admin, + } + } + pub fn get_actor(&self) -> Box { + Actor::::new_responder() + } +} + +impl TryFrom for AddUser { + type Error = ProtocolError; + fn try_from(msg: ProtocolMessage) -> Result { + if let ProtocolMessage::Start(StartProtocol::Admin(AdminRequest::V0(AdminRequestV0 { + content: AdminRequestContentV0::AddUser(a), + .. + }))) = msg + { + Ok(a) + } else { + log_debug!("INVALID {:?}", msg); + Err(ProtocolError::InvalidValue) + } + } +} + +impl From for ProtocolMessage { + fn from(msg: AddUser) -> ProtocolMessage { + unimplemented!(); + } +} + +impl From for AdminRequestContentV0 { + fn from(msg: AddUser) -> AdminRequestContentV0 { + AdminRequestContentV0::AddUser(msg) + } +} + +impl Actor<'_, AddUser, AdminResponse> {} + +#[async_trait::async_trait] +impl EActor for Actor<'_, AddUser, AdminResponse> { + async fn respond( + &mut self, + msg: ProtocolMessage, + fsm: Arc>, + ) -> Result<(), ProtocolError> { + let req = AddUser::try_from(msg)?; + let broker = BROKER.read().await; + let mut is_admin = req.is_admin(); + if let Some(ServerConfig { + admin_user: Some(admin_user), + .. + }) = broker.get_config() + { + if *admin_user == req.user() { + is_admin = true; + } + } + let res = broker.get_storage()?.add_user(req.user(), is_admin); + let response: AdminResponseV0 = res.into(); + fsm.lock().await.send(response.into()).await?; + Ok(()) + } +} diff --git a/p2p-net/src/actors/del_user.rs b/p2p-net/src/actors/del_user.rs new file mode 100644 index 0000000..0048f14 --- /dev/null +++ b/p2p-net/src/actors/del_user.rs @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2022-2023 Niko Bonnieure, Par le Peuple, NextGraph.org developers + * All rights reserved. + * Licensed under the Apache License, Version 2.0 + * + * or the MIT license , + * 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::BROKER; +use crate::connection::NoiseFSM; +use crate::types::*; +use crate::{actor::*, errors::ProtocolError, types::ProtocolMessage}; +use async_std::sync::Mutex; +use p2p_repo::types::PubKey; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; + +use super::StartProtocol; + +/// Delete user account V0 +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +pub struct DelUserV0 { + /// User pub key + pub user: PubKey, +} + +/// Delete user account +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +pub enum DelUser { + V0(DelUserV0), +} + +impl DelUser { + pub fn user(&self) -> PubKey { + match self { + DelUser::V0(o) => o.user, + } + } + pub fn get_actor(&self) -> Box { + Actor::::new_responder() + } +} + +impl TryFrom for DelUser { + type Error = ProtocolError; + fn try_from(msg: ProtocolMessage) -> Result { + if let ProtocolMessage::Start(StartProtocol::Admin(AdminRequest::V0(AdminRequestV0 { + content: AdminRequestContentV0::DelUser(a), + .. + }))) = msg + { + Ok(a) + } else { + Err(ProtocolError::InvalidValue) + } + } +} + +impl From for ProtocolMessage { + fn from(msg: DelUser) -> ProtocolMessage { + unimplemented!(); + } +} + +impl Actor<'_, DelUser, AdminResponse> {} + +#[async_trait::async_trait] +impl EActor for Actor<'_, DelUser, AdminResponse> { + async fn respond( + &mut self, + msg: ProtocolMessage, + fsm: Arc>, + ) -> Result<(), ProtocolError> { + //let req = DelUser::try_from(msg)?; + let res = AdminResponseV0 { + id: 0, + result: 0, + content: AdminResponseContentV0::EmptyResponse, + padding: vec![], + }; + fsm.lock().await.send(res.into()).await?; + Ok(()) + } +} diff --git a/p2p-net/src/actors/list_users.rs b/p2p-net/src/actors/list_users.rs new file mode 100644 index 0000000..0f52fcc --- /dev/null +++ b/p2p-net/src/actors/list_users.rs @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2022-2023 Niko Bonnieure, Par le Peuple, NextGraph.org developers + * All rights reserved. + * Licensed under the Apache License, Version 2.0 + * + * or the MIT license , + * 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::BROKER; +use crate::connection::NoiseFSM; +use crate::types::*; +use crate::{actor::*, errors::ProtocolError, types::ProtocolMessage}; + +use async_std::sync::Mutex; +use p2p_repo::log::*; +use p2p_repo::types::PubKey; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; + +use super::StartProtocol; + +/// List users registered on this broker +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +pub struct ListUsersV0 { + /// should list only the admins. if false, admin users will be excluded + pub admins: bool, +} + +/// List users registered on this broker +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +pub enum ListUsers { + V0(ListUsersV0), +} + +impl ListUsers { + pub fn admins(&self) -> bool { + match self { + Self::V0(o) => o.admins, + } + } + pub fn get_actor(&self) -> Box { + Actor::::new_responder() + } +} + +impl TryFrom for ListUsers { + type Error = ProtocolError; + fn try_from(msg: ProtocolMessage) -> Result { + if let ProtocolMessage::Start(StartProtocol::Admin(AdminRequest::V0(AdminRequestV0 { + content: AdminRequestContentV0::ListUsers(a), + .. + }))) = msg + { + Ok(a) + } else { + //log_debug!("INVALID {:?}", msg); + Err(ProtocolError::InvalidValue) + } + } +} + +impl From for ProtocolMessage { + fn from(msg: ListUsers) -> ProtocolMessage { + unimplemented!(); + } +} + +impl From for AdminRequestContentV0 { + fn from(msg: ListUsers) -> AdminRequestContentV0 { + AdminRequestContentV0::ListUsers(msg) + } +} + +impl Actor<'_, ListUsers, AdminResponse> {} + +#[async_trait::async_trait] +impl EActor for Actor<'_, ListUsers, AdminResponse> { + async fn respond( + &mut self, + msg: ProtocolMessage, + fsm: Arc>, + ) -> Result<(), ProtocolError> { + let req = ListUsers::try_from(msg)?; + let res = BROKER.read().await.get_storage()?.list_users(req.admins()); + let response: AdminResponseV0 = res.into(); + fsm.lock().await.send(response.into()).await?; + Ok(()) + } +} diff --git a/p2p-net/src/actors/mod.rs b/p2p-net/src/actors/mod.rs index 4a5268c..b77c748 100644 --- a/p2p-net/src/actors/mod.rs +++ b/p2p-net/src/actors/mod.rs @@ -6,3 +6,12 @@ pub use start::*; pub mod probe; pub use probe::*; + +pub mod add_user; +pub use add_user::*; + +pub mod del_user; +pub use del_user::*; + +pub mod list_users; +pub use list_users::*; diff --git a/p2p-net/src/actors/start.rs b/p2p-net/src/actors/start.rs index 1fdf44b..fb2d6c0 100644 --- a/p2p-net/src/actors/start.rs +++ b/p2p-net/src/actors/start.rs @@ -11,7 +11,7 @@ use crate::actors::noise::Noise; use crate::connection::NoiseFSM; -use crate::types::ExtResponse; +use crate::types::{AdminRequest, ExtResponse}; use crate::{actor::*, errors::ProtocolError, types::ProtocolMessage}; use async_std::sync::Mutex; use serde::{Deserialize, Serialize}; @@ -26,6 +26,8 @@ use std::sync::Arc; pub enum StartProtocol { Client(ClientHello), Ext(ExtHello), + //Core(CoreHello), + Admin(AdminRequest), } impl StartProtocol { @@ -33,16 +35,24 @@ impl StartProtocol { match self { StartProtocol::Client(a) => a.type_id(), StartProtocol::Ext(a) => a.type_id(), + StartProtocol::Admin(a) => a.type_id(), } } pub fn get_actor(&self) -> Box { match self { StartProtocol::Client(a) => a.get_actor(), StartProtocol::Ext(a) => a.get_actor(), + StartProtocol::Admin(a) => a.get_actor(), } } } +impl From for ProtocolMessage { + fn from(msg: StartProtocol) -> ProtocolMessage { + ProtocolMessage::Start(msg) + } +} + /// External Hello (finalizes the Noise handshake and send first ExtRequest) #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ExtHello { @@ -59,12 +69,6 @@ impl ExtHello { } } -// impl BrokerRequest for ExtHello { -// fn send(&self) -> ProtocolMessage { -// ProtocolMessage::Start(StartProtocol::Ext(self.clone())) -// } -// } - impl From for ProtocolMessage { fn from(msg: ExtHello) -> ProtocolMessage { ProtocolMessage::Start(StartProtocol::Ext(msg)) diff --git a/p2p-net/src/broker.rs b/p2p-net/src/broker.rs index 3678c85..2ba1cab 100644 --- a/p2p-net/src/broker.rs +++ b/p2p-net/src/broker.rs @@ -59,6 +59,14 @@ pub struct DirectConnection { cnx: ConnectionBase, } +#[derive(Debug)] +pub struct ServerConfig { + pub overlays_configs: Vec, + pub registration: RegistrationConfig, + pub admin_user: Option, + pub peer_id: PubKey, +} + pub static BROKER: Lazy>> = Lazy::new(|| Arc::new(RwLock::new(Broker::new()))); pub struct Broker<'a> { @@ -70,11 +78,10 @@ pub struct Broker<'a> { #[cfg(not(target_arch = "wasm32"))] listeners: HashMap, bind_addresses: HashMap, - overlays_configs: Vec, + config: Option, shutdown: Option>, shutdown_sender: Sender, closing: bool, - my_peer_id: Option, storage: Option>, test: u32, @@ -97,16 +104,19 @@ impl<'a> Broker<'a> { } } - pub fn set_my_peer_id(&mut self, id: PubKey) { - if self.my_peer_id.is_none() { - self.my_peer_id = Some(id) - } + pub fn get_config(&self) -> Option<&ServerConfig> { + self.config.as_ref() } pub fn set_storage(&mut self, storage: impl BrokerStorage + 'a) { + //log_debug!("set_storage"); self.storage = Some(Box::new(storage)); } + pub fn set_server_config(&mut self, config: ServerConfig) { + self.config = Some(config); + } + #[cfg(not(target_arch = "wasm32"))] pub fn set_listeners( &mut self, @@ -125,24 +135,29 @@ impl<'a> Broker<'a> { (copy_listeners, copy_bind_addresses) } + pub fn get_storage(&self) -> Result<&Box, ProtocolError> { + //log_debug!("GET STORAGE {:?}", self.storage); + self.storage.as_ref().ok_or(ProtocolError::BrokerError) + } + #[cfg(not(target_arch = "wasm32"))] pub fn authorize( &self, - remote_bind_address: &BindAddress, + bind_addresses: &(BindAddress, BindAddress), auth: Authorization, ) -> Result<(), ProtocolError> { + let listener_id = self + .bind_addresses + .get(&bind_addresses.0) + .ok_or(ProtocolError::BrokerError)?; + let listener = self + .listeners + .get(listener_id) + .ok_or(ProtocolError::BrokerError)?; match auth { Authorization::Discover => { - let listener_id = self - .bind_addresses - .get(remote_bind_address) - .ok_or(ProtocolError::BrokerError)?; - let listener = self - .listeners - .get(listener_id) - .ok_or(ProtocolError::BrokerError)?; if listener.config.discoverable - && remote_bind_address.ip.is_private() + && bind_addresses.1.ip.is_private() && listener.config.accept_forward_for.is_no() { Ok(()) @@ -153,14 +168,35 @@ impl<'a> Broker<'a> { Authorization::ExtMessage => Err(ProtocolError::AccessDenied), Authorization::Client(user) => Err(ProtocolError::AccessDenied), Authorization::Core => Err(ProtocolError::AccessDenied), - Authorization::Admin(_) => Err(ProtocolError::AccessDenied), + Authorization::Admin(admin_user) => { + if listener.config.accepts_client() { + if let Some(ServerConfig { + admin_user: Some(admin), + .. + }) = self.config + { + if admin == admin_user { + return Ok(()); + } + } + let found = self.get_storage()?.get_user(admin_user); + if found.is_ok() && found.unwrap() { + return Ok(()); + } + } + Err(ProtocolError::AccessDenied) + } Authorization::OverlayJoin(_) => Err(ProtocolError::AccessDenied), } } - pub fn set_overlays_configs(&mut self, overlays_configs: Vec) { - self.overlays_configs.extend(overlays_configs) - } + // pub fn add_user(&self, user: PubKey, is_admin: bool) -> Result<(), ProtocolError> { + // self.get_storage()?.add_user(user, is_admin) + // } + + // pub fn list_users(&self, admins: bool) -> Result, ProtocolError> { + // self.get_storage()?.list_users(admins) + // } pub async fn get_block_from_store_with_block_id( &mut self, @@ -171,7 +207,7 @@ impl<'a> Broker<'a> { // TODO let (mut tx, rx) = mpsc::unbounded::(); - //log_info!("cur {}", std::env::current_dir().unwrap().display()); + //log_debug!("cur {}", std::env::current_dir().unwrap().display()); //Err(ProtocolError::AccessDenied) // let f = std::fs::File::open( @@ -243,10 +279,10 @@ impl<'a> Broker<'a> { .unwrap(); async fn send(mut tx: Sender, commit: Commit) -> ResultSend<()> { while let Ok(_) = tx.send(commit.clone()).await { - log_info!("sending"); + log_debug!("sending"); sleep!(std::time::Duration::from_secs(3)); } - log_info!("end of sending"); + log_debug!("end of sending"); Ok(()) } spawn_and_log_error(send(tx.clone(), commit)); @@ -310,7 +346,7 @@ impl<'a> Broker<'a> { #[cfg(not(target_arch = "wasm32"))] listeners: HashMap::new(), bind_addresses: HashMap::new(), - overlays_configs: vec![], + config: None, shutdown: Some(shutdown_receiver), shutdown_sender, direct_connections: HashMap::new(), @@ -318,7 +354,6 @@ impl<'a> Broker<'a> { tauri_streams: HashMap::new(), closing: false, test: u32::from_be_bytes(random_buf), - my_peer_id: None, storage: None, } } @@ -346,7 +381,7 @@ impl<'a> Broker<'a> { async fn timer_shutdown(timeout: std::time::Duration) -> ResultSend<()> { async move { sleep!(timeout); - log_info!("timeout for shutdown"); + log_debug!("timeout for shutdown"); let _ = BROKER .write() .await @@ -435,11 +470,11 @@ impl<'a> Broker<'a> { match res { Some(Either::Right(remote_peer_id)) => { let res = join.next().await; - log_info!("SOCKET IS CLOSED {:?} peer_id: {:?}", res, remote_peer_id); + log_debug!("SOCKET IS CLOSED {:?} peer_id: {:?}", res, remote_peer_id); BROKER.write().await.remove_peer_id(remote_peer_id, None); } _ => { - log_info!( + log_debug!( "SOCKET IS CLOSED {:?} remote: {:?} local: {:?}", res, remote_bind_address, @@ -545,6 +580,45 @@ impl<'a> Broker<'a> { cnx.probe(ip, port).await } + pub async fn admin< + A: Into + + Into + + std::fmt::Debug + + Sync + + Send + + 'static, + >( + &mut self, + cnx: Box, + peer_privk: PrivKey, + peer_pubk: PubKey, + remote_peer_id: DirectPeerId, + user: PubKey, + user_priv: PrivKey, + addr: BindAddress, + request: A, + ) -> Result { + let config = StartConfig::Admin(AdminConfig { + user, + user_priv, + addr, + request: request.into(), + }); + let remote_peer_id_dh = remote_peer_id.to_dh_from_ed(); + + let mut connection = cnx + .open( + config.get_url(), + peer_privk.clone(), + peer_pubk, + remote_peer_id_dh, + config.clone(), + ) + .await?; + + connection.admin::().await + } + pub async fn connect( &mut self, cnx: Box, @@ -557,19 +631,22 @@ impl<'a> Broker<'a> { return Err(NetError::Closing); } - log_info!("CONNECTING"); + log_debug!("CONNECTING"); let remote_peer_id_dh = remote_peer_id.to_dh_from_ed(); - let already = self - .peers - .get(&(config.get_user(), *remote_peer_id_dh.slice())); - if already.is_some() { - match already.unwrap().connected { - PeerConnection::NONE => {} - _ => { - return Err(NetError::PeerAlreadyConnected); - } - }; + // checking if already connected + if config.is_keep_alive() { + let already = self + .peers + .get(&(config.get_user(), *remote_peer_id_dh.slice())); + if already.is_some() { + match already.unwrap().connected { + PeerConnection::NONE => {} + _ => { + return Err(NetError::PeerAlreadyConnected); + } + }; + } } let mut connection = cnx @@ -582,6 +659,10 @@ impl<'a> Broker<'a> { ) .await?; + if !config.is_keep_alive() { + return Ok(()); + } + let join = connection.take_shutdown(); let connected = match &config { @@ -618,7 +699,7 @@ impl<'a> Broker<'a> { ) -> ResultSend<()> { async move { let res = join.next().await; - log_info!("SOCKET IS CLOSED {:?} {:?}", res, remote_peer_id); + log_debug!("SOCKET IS CLOSED {:?} {:?}", res, remote_peer_id); if res.is_some() && res.as_ref().unwrap().is_left() && res.unwrap().unwrap_left() != NetError::Closing @@ -630,10 +711,10 @@ impl<'a> Broker<'a> { // let result = broker // .connect(cnx, ip, core, peer_pubk, peer_privk, remote_peer_id) // .await; - // log_info!("SOCKET RECONNECTION {:?} {:?}", result, &remote_peer_id); + // log_debug!("SOCKET RECONNECTION {:?} {:?}", result, &remote_peer_id); // TODO: deal with error and incremental backoff } else { - log_info!("REMOVED"); + log_debug!("REMOVED"); BROKER .write() .await @@ -690,13 +771,13 @@ impl<'a> Broker<'a> { pub fn print_status(&self) { self.peers.iter().for_each(|(peerId, peerInfo)| { - log_info!("PEER in BROKER {:?} {:?}", peerId, peerInfo); + log_debug!("PEER in BROKER {:?} {:?}", peerId, peerInfo); }); self.direct_connections.iter().for_each(|(ip, directCnx)| { - log_info!("direct_connection in BROKER {:?} {:?}", ip, directCnx) + log_debug!("direct_connection in BROKER {:?} {:?}", ip, directCnx) }); self.anonymous_connections.iter().for_each(|(binds, cb)| { - log_info!( + log_debug!( "ANONYMOUS remote {:?} local {:?} {:?}", binds.1, binds.0, diff --git a/p2p-net/src/broker_storage.rs b/p2p-net/src/broker_storage.rs index 76b6b43..4cf0bdf 100644 --- a/p2p-net/src/broker_storage.rs +++ b/p2p-net/src/broker_storage.rs @@ -9,9 +9,11 @@ * according to those terms. */ -use crate::types::*; -use p2p_repo::kcv_store::KCVStore; +use crate::{errors::ProtocolError, types::*}; +use p2p_repo::{kcv_store::KCVStore, types::PubKey}; -pub trait BrokerStorage: Send + Sync { - fn get_user(&self); +pub trait BrokerStorage: Send + Sync + std::fmt::Debug { + fn get_user(&self, user_id: PubKey) -> Result; + fn add_user(&self, user_id: PubKey, is_admin: bool) -> Result<(), ProtocolError>; + fn list_users(&self, admins: bool) -> Result, ProtocolError>; } diff --git a/p2p-net/src/connection.rs b/p2p-net/src/connection.rs index 40c2031..e80209d 100644 --- a/p2p-net/src/connection.rs +++ b/p2p-net/src/connection.rs @@ -11,6 +11,7 @@ //static NOISE_CONFIG: &'static str = "Noise_XK_25519_ChaChaPoly_BLAKE2b"; +use std::any::TypeId; use std::collections::HashMap; use std::fmt; use std::sync::Arc; @@ -45,6 +46,16 @@ pub enum ConnectionCommand { Error(NetError), ProtocolError(ProtocolError), Close, + ReEnter, +} + +impl ConnectionCommand { + pub fn is_re_enter(&self) -> bool { + match self { + Self::ReEnter => true, + _ => false, + } + } } #[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] @@ -96,7 +107,8 @@ pub enum FSMstate { Noise0, // unused Noise1, Noise2, - Noise3, // unused + Noise3, + AdminRequest, ExtRequest, ExtResponse, ClientHello, @@ -141,9 +153,10 @@ pub enum StepReply { Response(ProtocolMessage), NONE, CloseNow, + ReEnter, } -#[derive(PartialEq, Debug, Clone)] +#[derive(Debug, Clone)] pub struct ClientConfig { pub url: String, pub user: PubKey, @@ -153,19 +166,24 @@ pub struct ClientConfig { pub info: ClientInfo, } -#[derive(PartialEq, Debug, Clone)] +#[derive(Debug, Clone)] pub struct ExtConfig {} -#[derive(PartialEq, Debug, Clone)] +#[derive(Debug, Clone)] pub struct CoreConfig { pub addr: BindAddress, pub interface: String, } -#[derive(PartialEq, Debug, Clone)] -pub struct AdminConfig {} +#[derive(Debug, Clone)] +pub struct AdminConfig { + pub user: PubKey, + pub user_priv: PrivKey, + pub addr: BindAddress, + pub request: AdminRequestContentV0, +} -#[derive(PartialEq, Debug, Clone)] +#[derive(Debug, Clone)] pub enum StartConfig { Probe, Relay(BindAddress), @@ -179,6 +197,7 @@ impl StartConfig { pub fn get_url(&self) -> String { match self { Self::Client(config) => config.url.clone(), + Self::Admin(config) => format!("ws://{}:{}", config.addr.ip, config.addr.port), Self::Core(config) => format!("ws://{}:{}", config.addr.ip, config.addr.port), _ => unimplemented!(), } @@ -189,6 +208,12 @@ impl StartConfig { _ => None, } } + pub fn is_keep_alive(&self) -> bool { + match self { + StartConfig::Core(_) | StartConfig::Client(_) => true, + _ => false, + } + } } impl NoiseFSM { @@ -313,11 +338,33 @@ impl NoiseFSM { return Ok(StepReply::NONE); } + fn process_server_noise3(&mut self, noise: &Noise) -> Result<(), ProtocolError> { + let handshake = self.noise_handshake_state.as_mut().unwrap(); + + let _ = handshake + .read_message_vec(noise.data()) + .map_err(|e| ProtocolError::NoiseHandshakeFailed)?; + + if !handshake.completed() { + return Err(ProtocolError::NoiseHandshakeFailed); + } + let peer_id = handshake.get_rs().unwrap(); + self.remote = Some(PubKey::X25519PubKey(peer_id)); + + let ciphers = handshake.get_ciphers(); + self.noise_cipher_state_enc = Some(ciphers.1); + self.noise_cipher_state_dec = Some(ciphers.0); + + self.noise_handshake_state = None; + + Ok(()) + } + pub async fn step( &mut self, mut msg_opt: Option, ) -> Result { - if self.noise_cipher_state_dec.is_some() { + if self.noise_cipher_state_dec.is_some() && msg_opt.is_some() { if let Some(ProtocolMessage::Noise(noise)) = msg_opt.as_ref() { let new = self.decrypt(noise)?; msg_opt.replace(new); @@ -326,7 +373,11 @@ impl NoiseFSM { } } if msg_opt.is_some() { - log_debug!("RECEIVED: {:?}", msg_opt.as_ref().unwrap()); + log_debug!( + "RECEIVED: {:?} in state {:?}", + msg_opt.as_ref().unwrap(), + self.state + ); } match self.state { FSMstate::Closing => {} @@ -413,8 +464,7 @@ impl NoiseFSM { .authorize( &self .bind_addresses - .ok_or(ProtocolError::BrokerError)? - .1, + .ok_or(ProtocolError::BrokerError)?, Authorization::Discover, ) .is_ok() @@ -492,6 +542,7 @@ impl NoiseFSM { let ciphers = handshake.get_ciphers(); + let mut next_step = StepReply::NONE; match self.config.as_ref().unwrap() { StartConfig::Client(client_config) => { let noise3 = @@ -506,7 +557,10 @@ impl NoiseFSM { todo!(); } StartConfig::Admin(admin_config) => { - todo!(); + let noise = Noise::V0(NoiseV0 { data: payload }); + self.send(noise.into()).await?; + self.state = FSMstate::Noise3; + next_step = StepReply::ReEnter; } _ => return Err(ProtocolError::InvalidState), } @@ -516,7 +570,7 @@ impl NoiseFSM { self.noise_handshake_state = None; - return Ok(StepReply::NONE); + return Ok(next_step); } } } @@ -529,23 +583,7 @@ impl NoiseFSM { noise, ))) = msg { - let handshake = self.noise_handshake_state.as_mut().unwrap(); - - let _ = handshake - .read_message_vec(noise.data()) - .map_err(|e| ProtocolError::NoiseHandshakeFailed)?; - - if !handshake.completed() { - return Err(ProtocolError::NoiseHandshakeFailed); - } - let peer_id = handshake.get_rs().unwrap(); - self.remote = Some(PubKey::X25519PubKey(peer_id)); - - let ciphers = handshake.get_ciphers(); - self.noise_cipher_state_enc = Some(ciphers.1); - self.noise_cipher_state_dec = Some(ciphers.0); - - self.noise_handshake_state = None; + self.process_server_noise3(noise)?; let mut nonce_buf = [0u8; 32]; getrandom::getrandom(&mut nonce_buf).unwrap(); @@ -560,11 +598,94 @@ impl NoiseFSM { self.send(server_hello.into()).await?; return Ok(StepReply::NONE); + } else if let ProtocolMessage::Noise(noise) = msg { + self.process_server_noise3(noise)?; + + self.state = FSMstate::Noise3; + + return Ok(StepReply::NONE); + } + } + } + } + FSMstate::Noise3 => { + // CLIENT after Noise3, sending StartProtocol + if msg_opt.is_none() && !self.dir.is_server() { + match self.config.as_ref().unwrap() { + StartConfig::Client(_) => { + return Err(ProtocolError::InvalidState); + } + StartConfig::Ext(ext_config) => { + todo!(); + } + StartConfig::Core(core_config) => { + todo!(); + } + StartConfig::Admin(admin_config) => { + let ser = serde_bare::to_vec(&admin_config.request)?; + let sig = sign(&admin_config.user_priv, &admin_config.user, &ser)?; + let admin_req = AdminRequestV0 { + content: admin_config.request.clone(), + id: 0, + sig, + admin_user: admin_config.user, + padding: vec![], + }; + let protocol_start = StartProtocol::Admin(AdminRequest::V0(admin_req)); + + self.send(protocol_start.into()).await?; + self.state = FSMstate::AdminRequest; + + return Ok(StepReply::NONE); + } + _ => return Err(ProtocolError::InvalidState), + } + } else if self.dir.is_server() { + // SERVER after Noise3, receives StartProtocol + #[cfg(not(target_arch = "wasm32"))] + if let Some(ProtocolMessage::Start(start_msg)) = msg_opt.as_ref() { + match start_msg { + StartProtocol::Client(_) => { + return Err(ProtocolError::InvalidState); + } + StartProtocol::Ext(ext_config) => { + todo!(); + } + // StartProtocol::Core(core_config) => { + // todo!(); + // } + StartProtocol::Admin(AdminRequest::V0(req)) => { + BROKER.read().await.authorize( + &self.bind_addresses.ok_or(ProtocolError::BrokerError)?, + Authorization::Admin(req.admin_user), + )?; + + // PROCESS AdminRequest and send back AdminResponse + let ser = serde_bare::to_vec(&req.content)?; + + let verif = verify(&ser, req.sig, req.admin_user); + if verif.is_err() { + let result: ProtocolError = verif.unwrap_err().into(); + return Err(result); + } else { + self.state = FSMstate::Closing; + return Ok(StepReply::Responder(msg_opt.unwrap())); + } + } + _ => return Err(ProtocolError::InvalidState), } } } } - FSMstate::Noise3 => {} + FSMstate::AdminRequest => { + // CLIENT side receiving AdminResponse + if let Some(msg) = msg_opt { + if self.dir.is_server() || msg.type_id() != TypeId::of::() { + return Err(ProtocolError::InvalidState); + } + return Ok(StepReply::Response(msg)); + } + } FSMstate::ExtRequest => {} FSMstate::ExtResponse => {} FSMstate::ClientHello => { @@ -584,10 +705,10 @@ impl NoiseFSM { }; let ser = serde_bare::to_vec(&content)?; let sig = - sign(&client_config.client_priv, &client_config.client, &ser)?; + sign(&client_config.user_priv, &client_config.user, &ser)?; let client_auth = ClientAuth::V0(ClientAuthV0 { content, - /// Signature by client key + /// Signature by user key sig, }); @@ -613,7 +734,7 @@ impl NoiseFSM { let ser = serde_bare::to_vec(&client_auth.content_v0())?; let mut result = ProtocolError::NoError; - let verif = verify(&ser, client_auth.sig(), client_auth.client()); + let verif = verify(&ser, client_auth.sig(), client_auth.user()); if verif.is_err() { result = verif.unwrap_err().into(); } else { @@ -641,7 +762,7 @@ impl NoiseFSM { if (result.is_err()) { return Err(result); } - log_info!("AUTHENTICATION SUCCESSFUL ! waiting for requests on the server side"); + log_debug!("AUTHENTICATION SUCCESSFUL ! waiting for requests on the server side"); self.state = FSMstate::AuthResult; return Ok(StepReply::NONE); } @@ -661,7 +782,7 @@ impl NoiseFSM { self.state = FSMstate::AuthResult; - log_info!("AUTHENTICATION SUCCESSFUL ! waiting for requests on the client side"); + log_debug!("AUTHENTICATION SUCCESSFUL ! waiting for requests on the client side"); return Ok(StepReply::NONE); } @@ -671,6 +792,9 @@ impl NoiseFSM { } FSMstate::AuthResult => { if let Some(msg) = msg_opt { + if msg.type_id() != TypeId::of::() { + return Err(ProtocolError::AccessDenied); + } let id = msg.id(); if self.dir.is_server() && id > 0 || !self.dir.is_server() && id < 0 { return Ok(StepReply::Responder(msg)); @@ -773,6 +897,7 @@ impl ConnectionBase { } async fn read_loop( + mut receiver_tx: Sender, mut receiver: Receiver, mut sender: Sender, actors: Arc>>>, @@ -783,15 +908,29 @@ impl ConnectionBase { ConnectionCommand::Close | ConnectionCommand::Error(_) | ConnectionCommand::ProtocolError(_) => { - log_info!("EXIT READ LOOP because : {:?}", msg); + log_debug!("EXIT READ LOOP because : {:?}", msg); + let mut lock = actors.lock().await; + for actor in lock.values_mut() { + actor.send(msg.clone()).await; + } break; } - ConnectionCommand::Msg(proto_msg) => { + _ => { let res; - { - let mut locked_fsm = fsm.lock().await; - res = locked_fsm.step(Some(proto_msg)).await; + if let ConnectionCommand::Msg(proto_msg) = msg { + { + let mut locked_fsm = fsm.lock().await; + res = locked_fsm.step(Some(proto_msg)).await; + } + } else if msg.is_re_enter() { + { + let mut locked_fsm = fsm.lock().await; + res = locked_fsm.step(None).await; + } + } else { + panic!("shouldn't be here. ConnectionCommand is read_loop can only have 5 different variants") } + match res { Err(e) => { if sender @@ -806,6 +945,9 @@ impl ConnectionBase { let _ = sender.send(ConnectionCommand::Close).await; break; } + Ok(StepReply::ReEnter) => { + let _ = receiver_tx.send(ConnectionCommand::ReEnter).await; + } Ok(StepReply::NONE) => {} Ok(StepReply::Responder(responder)) => { let r = responder @@ -852,7 +994,11 @@ impl ConnectionBase { } } } - log_info!("END OF READ LOOP"); + log_debug!("END OF READ LOOP"); + let mut lock = actors.lock().await; + for actor in lock.drain() { + actor.1.close_channel(); + } Ok(()) } @@ -895,10 +1041,51 @@ impl ConnectionBase { // } pub async fn close(&mut self) { - log_info!("closing..."); + log_debug!("closing..."); self.send(ConnectionCommand::Close).await; } + pub async fn admin< + A: Into + + Into + + std::fmt::Debug + + Sync + + Send + + 'static, + >( + &mut self, + ) -> Result { + if !self.dir.is_server() { + let mut actor = Box::new(Actor::::new(0, true)); + self.actors.lock().await.insert(0, actor.get_receiver_tx()); + + let mut receiver = actor.detach_receiver(); + match receiver.next().await { + Some(ConnectionCommand::Msg(msg)) => { + self.fsm + .as_ref() + .unwrap() + .lock() + .await + .remove_actor(0) + .await; + let response: AdminResponse = msg.try_into()?; + self.close().await; + if response.result() == 0 { + return Ok(response.content_v0()); + } + Err(ProtocolError::try_from(response.result()).unwrap()) + } + Some(ConnectionCommand::ProtocolError(e)) => Err(e), + Some(ConnectionCommand::Error(e)) => Err(e.into()), + Some(ConnectionCommand::Close) => Err(ProtocolError::Closing), + _ => Err(ProtocolError::ActorError), + } + } else { + panic!("cannot call probe on a server-side connection"); + } + } + pub async fn probe(&mut self) -> Result, ProtocolError> { if !self.dir.is_server() { let config = StartConfig::Probe; @@ -984,7 +1171,7 @@ impl ConnectionBase { self.sender = Some(sender_rx); self.receiver = Some(receiver_tx.clone()); self.sender_tx = Some(sender_tx.clone()); - self.receiver_tx = Some(receiver_tx); + self.receiver_tx = Some(receiver_tx.clone()); let fsm = Arc::new(Mutex::new(NoiseFSM::new( bind_addresses, @@ -998,6 +1185,7 @@ impl ConnectionBase { self.fsm = Some(Arc::clone(&fsm)); spawn_and_log_error(Self::read_loop( + receiver_tx, receiver_rx, sender_tx, Arc::clone(&self.actors), @@ -1019,14 +1207,14 @@ mod test { #[async_std::test] pub async fn test_typeid() { - log_info!( + log_debug!( "{:?}", ClientHello::Noise3(Noise::V0(NoiseV0 { data: vec![] })).type_id() ); let a = Noise::V0(NoiseV0 { data: [].to_vec() }); - log_info!("{:?}", a.type_id()); - log_info!("{:?}", TypeId::of::()); - log_info!("{:?}", ClientHello::Local.type_id()); - log_info!("{:?}", TypeId::of::()); + log_debug!("{:?}", a.type_id()); + log_debug!("{:?}", TypeId::of::()); + log_debug!("{:?}", ClientHello::Local.type_id()); + log_debug!("{:?}", TypeId::of::()); } } diff --git a/p2p-net/src/errors.rs b/p2p-net/src/errors.rs index 50b68c2..73ceaea 100644 --- a/p2p-net/src/errors.rs +++ b/p2p-net/src/errors.rs @@ -9,11 +9,11 @@ // notice may not be copied, modified, or distributed except // according to those terms. -use crate::types::BrokerMessage; use core::fmt; use num_enum::IntoPrimitive; use num_enum::TryFromPrimitive; use p2p_repo::object::ObjectParseError; +use p2p_repo::store::StorageError; use p2p_repo::types::Block; use p2p_repo::types::ObjectId; use std::convert::From; @@ -62,11 +62,10 @@ pub enum ProtocolError { OverlayNotFound, BrokerError, NotFound, - StoreError, MissingBlocks, ObjectParseError, InvalidValue, - UserAlreadyExists, + AlreadyExists, RepoIdRequired, ConnectionError, @@ -74,6 +73,8 @@ pub enum ProtocolError { PeerAlreadyConnected, OtherError, + NetError, + StorageError, Closing, FsmNotReady, MustBeEncrypted, @@ -85,6 +86,35 @@ pub enum ProtocolError { InvalidNonce, } //MAX 949 ProtocolErrors +impl From for ProtocolError { + fn from(e: NetError) -> Self { + match e { + NetError::IoError => ProtocolError::IoError, + NetError::WsError => ProtocolError::WsError, + NetError::ConnectionError => ProtocolError::ConnectionError, + NetError::SerializationError => ProtocolError::SerializationError, + NetError::ProtocolError => ProtocolError::OtherError, + NetError::AccessDenied => ProtocolError::AccessDenied, + NetError::PeerAlreadyConnected => ProtocolError::PeerAlreadyConnected, + NetError::Closing => ProtocolError::Closing, + _ => ProtocolError::NetError, + } + } +} + +impl From for ProtocolError { + fn from(e: StorageError) -> Self { + match e { + StorageError::NotFound => ProtocolError::NotFound, + StorageError::InvalidValue => ProtocolError::InvalidValue, + StorageError::BackendError => ProtocolError::StorageError, + StorageError::SerializationError => ProtocolError::SerializationError, + StorageError::AlreadyExists => ProtocolError::AlreadyExists, + _ => ProtocolError::StorageError, + } + } +} + impl ProtocolError { pub fn is_stream(&self) -> bool { *self == ProtocolError::PartialContent || *self == ProtocolError::EndOfStream @@ -118,16 +148,6 @@ impl From for ProtocolError { } } -impl From for ProtocolError { - fn from(e: p2p_repo::store::StorageError) -> Self { - match e { - p2p_repo::store::StorageError::NotFound => ProtocolError::NotFound, - p2p_repo::store::StorageError::InvalidValue => ProtocolError::InvalidValue, - _ => ProtocolError::StoreError, - } - } -} - impl From for ProtocolError { fn from(e: serde_bare::error::Error) -> Self { ProtocolError::SerializationError @@ -140,72 +160,72 @@ impl From for NetError { } } -impl From for Result<(), ProtocolError> { - fn from(msg: BrokerMessage) -> Self { - if !msg.is_response() { - panic!("BrokerMessage is not a response"); - } - match msg.result() { - 0 => Ok(()), - err => Err(ProtocolError::try_from(err).unwrap()), - } - } -} - -impl From for Result { - fn from(msg: BrokerMessage) -> Self { - if !msg.is_response() { - panic!("BrokerMessage is not a response"); - } - match msg.result() { - 0 => Ok(msg.response_object_id()), - err => Err(ProtocolError::try_from(err).unwrap()), - } - } -} - -/// Option represents if a Block is available. cannot be returned here. call BrokerMessage.response_block() to get a reference to it. -impl From for Result, ProtocolError> { - fn from(msg: BrokerMessage) -> Self { - if !msg.is_response() { - panic!("BrokerMessage is not a response"); - } - //let partial: u16 = ProtocolError::PartialContent.into(); - let res = msg.result(); - if res == 0 || ProtocolError::try_from(res).unwrap().is_stream() { - if msg.is_overlay() { - match msg.response_block() { - Some(_) => Ok(Some(res)), - None => Ok(None), - } - } else { - Ok(None) - } - } else { - Err(ProtocolError::try_from(res).unwrap()) - } - } -} - -/// Option represents if a Block is available. returns a clone. -impl From for Result, ProtocolError> { - fn from(msg: BrokerMessage) -> Self { - if !msg.is_response() { - panic!("BrokerMessage is not a response"); - } - //let partial: u16 = ProtocolError::PartialContent.into(); - let res = msg.result(); - if res == 0 || ProtocolError::try_from(res).unwrap().is_stream() { - if msg.is_overlay() { - match msg.response_block() { - Some(b) => Ok(Some(b.clone())), - None => Ok(None), - } - } else { - Ok(None) - } - } else { - Err(ProtocolError::try_from(res).unwrap()) - } - } -} +// impl From for Result<(), ProtocolError> { +// fn from(msg: BrokerMessage) -> Self { +// if !msg.is_response() { +// panic!("BrokerMessage is not a response"); +// } +// match msg.result() { +// 0 => Ok(()), +// err => Err(ProtocolError::try_from(err).unwrap()), +// } +// } +// } + +// impl From for Result { +// fn from(msg: BrokerMessage) -> Self { +// if !msg.is_response() { +// panic!("BrokerMessage is not a response"); +// } +// match msg.result() { +// 0 => Ok(msg.response_object_id()), +// err => Err(ProtocolError::try_from(err).unwrap()), +// } +// } +// } + +// /// Option represents if a Block is available. cannot be returned here. call BrokerMessage.response_block() to get a reference to it. +// impl From for Result, ProtocolError> { +// fn from(msg: BrokerMessage) -> Self { +// if !msg.is_response() { +// panic!("BrokerMessage is not a response"); +// } +// //let partial: u16 = ProtocolError::PartialContent.into(); +// let res = msg.result(); +// if res == 0 || ProtocolError::try_from(res).unwrap().is_stream() { +// if msg.is_overlay() { +// match msg.response_block() { +// Some(_) => Ok(Some(res)), +// None => Ok(None), +// } +// } else { +// Ok(None) +// } +// } else { +// Err(ProtocolError::try_from(res).unwrap()) +// } +// } +// } + +// /// Option represents if a Block is available. returns a clone. +// impl From for Result, ProtocolError> { +// fn from(msg: BrokerMessage) -> Self { +// if !msg.is_response() { +// panic!("BrokerMessage is not a response"); +// } +// //let partial: u16 = ProtocolError::PartialContent.into(); +// let res = msg.result(); +// if res == 0 || ProtocolError::try_from(res).unwrap().is_stream() { +// if msg.is_overlay() { +// match msg.response_block() { +// Some(b) => Ok(Some(b.clone())), +// None => Ok(None), +// } +// } else { +// Ok(None) +// } +// } else { +// Err(ProtocolError::try_from(res).unwrap()) +// } +// } +// } diff --git a/p2p-net/src/types.rs b/p2p-net/src/types.rs index 52545a0..f72502c 100644 --- a/p2p-net/src/types.rs +++ b/p2p-net/src/types.rs @@ -1035,6 +1035,14 @@ impl BrokerOverlayConfigV0 { } } +/// Registration config +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum RegistrationConfig { + Closed, + Invitation, + Open, +} + // // COMMON TYPES FOR MESSAGES // @@ -1576,9 +1584,9 @@ pub enum EventResp { V0(EventRespV0), } -/// Content of OverlayRequestV0 +/// Content of CoreRequestV0 #[derive(Clone, Debug, Serialize, Deserialize)] -pub enum OverlayRequestContentV0 { +pub enum CoreRequestContentV0 { EventReq(EventReq), BranchHeadsReq(BranchHeadsReq), BranchSyncReq(BranchSyncReq), @@ -1586,23 +1594,23 @@ pub enum OverlayRequestContentV0 { /// Request sent to an overlay #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct OverlayRequestV0 { +pub struct CoreRequestV0 { /// Request ID pub id: i64, /// Request content - pub content: OverlayRequestContentV0, + pub content: CoreRequestContentV0, } /// Request sent to an overlay #[derive(Clone, Debug, Serialize, Deserialize)] -pub enum OverlayRequest { - V0(OverlayRequestV0), +pub enum CoreRequest { + V0(CoreRequestV0), } -/// Content of OverlayResponseV0 +/// Content of CoreResponseV0 #[derive(Clone, Debug, Serialize, Deserialize)] -pub enum OverlayResponseContentV0 { +pub enum CoreResponseContentV0 { EmptyResponse(()), Block(Block), EventResp(EventResp), @@ -1611,7 +1619,7 @@ pub enum OverlayResponseContentV0 { /// Request sent to an overlay #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct OverlayResponseV0 { +pub struct CoreResponseV0 { /// Request ID pub id: i64, @@ -1619,13 +1627,13 @@ pub struct OverlayResponseV0 { pub result: u16, /// Response content - pub content: OverlayResponseContentV0, + pub content: CoreResponseContentV0, } -/// Request sent to an OverlayRequest +/// Request sent to an CoreRequest #[derive(Clone, Debug, Serialize, Deserialize)] -pub enum OverlayResponse { - V0(OverlayResponseV0), +pub enum CoreResponse { + V0(CoreResponseV0), } /// Content of PeerAdvertV0 @@ -1682,9 +1690,9 @@ impl PeerAdvert { } } -/// Content of OverlayMessagePaddedV0 +/// Content of CoreMessagePaddedV0 #[derive(Clone, Debug, Serialize, Deserialize)] -pub enum OverlayMessageContentV0 { +pub enum CoreMessageContentV0 { OverlayConnect(OverlayConnect), OverlayDisconnect(OverlayDisconnect), PeerAdvert(PeerAdvert), @@ -1697,23 +1705,13 @@ pub enum OverlayMessageContentV0 { BlockSearchTopic(BlockSearchTopic), BlockSearchRandom(BlockSearchRandom), BlockResult(BlockResult), - OverlayRequest(OverlayRequest), - OverlayResponse(OverlayResponse), -} - -/// Padded content of OverlayMessageV0 -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct OverlayMessageContentPaddedV0 { - pub content: OverlayMessageContentV0, - - /// Optional padding - #[serde(with = "serde_bytes")] - pub padding: Vec, + CoreRequest(CoreRequest), + CoreResponse(CoreResponse), } /// Overlay message #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct OverlayMessageV0 { +pub struct CoreMessageV0 { /// Overlay ID pub overlay: OverlayId, @@ -1723,291 +1721,240 @@ pub struct OverlayMessageV0 { /// Padded content encrypted with ChaCha20 /// - overlay_secret: BLAKE3 derive_key ("NextGraph Overlay BLAKE3 key", /// repo_pubkey + repo_secret) - /// - key: BLAKE3 derive_key ("NextGraph OverlayMessage ChaCha20 key", + /// - key: BLAKE3 derive_key ("NextGraph CoreMessage ChaCha20 key", /// overlay_secret + session_id) /// - nonce: per-session message sequence number of sending peer - pub content: OverlayMessageContentPaddedV0, + pub content: CoreMessageContentV0, + + /// Optional padding + #[serde(with = "serde_bytes")] + pub padding: Vec, /// BLAKE3 MAC /// BLAKE3 keyed hash over the encrypted content - /// - key: BLAKE3 derive_key ("NextGraph OverlayMessage BLAKE3 key", + /// - key: BLAKE3 derive_key ("NextGraph CoreMessage BLAKE3 key", /// overlay_secret + session_id) pub mac: Digest, } /// Overlay message #[derive(Clone, Debug, Serialize, Deserialize)] -pub enum OverlayMessage { - V0(OverlayMessageV0), +pub enum CoreMessage { + V0(CoreMessageV0), } // -// BROKER PROTOCOL +// ADMIN PROTOCOL // -/// Content of AddUserV0 -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -pub struct AddUserContentV0 { - /// User pub key - pub user: PubKey, -} - -/// Add user account -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -pub struct AddUserV0 { - pub content: AddUserContentV0, - - /// Signature by admin key - pub sig: Sig, -} - -/// Add user account -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -pub enum AddUser { - V0(AddUserV0), +/// Content of `AdminRequestV0` +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum AdminRequestContentV0 { + AddUser(AddUser), + DelUser(DelUser), + ListUsers(ListUsers), } - -impl AddUser { - pub fn content_v0(&self) -> AddUserContentV0 { - match self { - AddUser::V0(o) => o.content, - } - } - pub fn sig(&self) -> Sig { +impl AdminRequestContentV0 { + pub fn type_id(&self) -> TypeId { match self { - AddUser::V0(o) => o.sig, + Self::AddUser(a) => a.type_id(), + Self::DelUser(a) => a.type_id(), + Self::ListUsers(a) => a.type_id(), } } - pub fn user(&self) -> PubKey { + pub fn get_actor(&self) -> Box { match self { - AddUser::V0(o) => o.content.user, + Self::AddUser(a) => a.get_actor(), + Self::DelUser(a) => a.get_actor(), + Self::ListUsers(a) => a.get_actor(), } } } -/// Content of DelUserV0 -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -pub struct DelUserContentV0 { - /// User pub key - pub user: PubKey, -} +/// Admin request +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct AdminRequestV0 { + /// Request ID + pub id: i64, -/// Delete user account -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -pub struct DelUserV0 { - pub content: DelUserContentV0, + /// Request content + pub content: AdminRequestContentV0, - /// Signature by admin key + /// Signature over content by admin key pub sig: Sig, -} - -/// Delete user account -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -pub enum DelUser { - V0(DelUserV0), -} -impl DelUser { - pub fn content_v0(&self) -> DelUserContentV0 { - match self { - DelUser::V0(o) => o.content, - } - } - pub fn sig(&self) -> Sig { - match self { - DelUser::V0(o) => o.sig, - } - } - pub fn user(&self) -> PubKey { - match self { - DelUser::V0(o) => o.content.user, - } - } -} + /// THe admin user requesting this operation + pub admin_user: PubKey, -/// Content of `AddClientV0` -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -pub struct AddClientContentV0 { - /// Client pub key - pub client: PubKey, + /// Optional padding + #[serde(with = "serde_bytes")] + pub padding: Vec, } -/// Add a client -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -pub struct AddClientV0 { - pub content: AddClientContentV0, - /// Signature by user key - pub sig: Sig, +impl AdminRequestV0 { + pub fn get_actor(&self) -> Box { + self.content.get_actor() + } } -/// Add a client -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -pub enum AddClient { - V0(AddClientV0), +/// Admin request +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum AdminRequest { + V0(AdminRequestV0), } -impl AddClient { - pub fn content_v0(&self) -> AddClientContentV0 { +impl AdminRequest { + pub fn id(&self) -> i64 { match self { - AddClient::V0(o) => o.content, + Self::V0(o) => o.id, } } - pub fn sig(&self) -> Sig { + pub fn set_id(&mut self, id: i64) { match self { - AddClient::V0(o) => o.sig, + Self::V0(v0) => { + v0.id = id; + } } } - pub fn client(&self) -> PubKey { + pub fn type_id(&self) -> TypeId { match self { - AddClient::V0(o) => o.content.client, + Self::V0(o) => o.content.type_id(), } } -} - -/// Content of `DelClientV0` -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -pub struct DelClientContentV0 { - /// Client pub key - pub client: PubKey, -} - -/// Remove a client -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -pub struct DelClientV0 { - pub content: DelClientContentV0, - - /// Signature by user key - pub sig: Sig, -} - -/// Remove a client -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -pub enum DelClient { - V0(DelClientV0), -} - -impl DelClient { - pub fn content_v0(&self) -> DelClientContentV0 { + pub fn sig(&self) -> Sig { match self { - DelClient::V0(o) => o.content, + Self::V0(o) => o.sig, } } - pub fn sig(&self) -> Sig { + pub fn admin_user(&self) -> PubKey { match self { - DelClient::V0(o) => o.sig, + Self::V0(o) => o.admin_user, } } - pub fn client(&self) -> PubKey { + pub fn get_actor(&self) -> Box { match self { - DelClient::V0(o) => o.content.client, + Self::V0(a) => a.get_actor(), } } } -/// Content of `BrokerRequestV0` -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum BrokerRequestContentV0 { - AddUser(AddUser), - DelUser(DelUser), - AddClient(AddClient), - DelClient(DelClient), -} -impl BrokerRequestContentV0 { - pub fn type_id(&self) -> TypeId { - match self { - BrokerRequestContentV0::AddUser(a) => a.type_id(), - BrokerRequestContentV0::DelUser(a) => a.type_id(), - BrokerRequestContentV0::AddClient(a) => a.type_id(), - BrokerRequestContentV0::DelClient(a) => a.type_id(), - } +impl From for ProtocolMessage { + fn from(msg: AdminRequest) -> ProtocolMessage { + ProtocolMessage::Start(StartProtocol::Admin(msg)) } } -/// Broker request +/// Content of `AdminResponseV0` #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct BrokerRequestV0 { +pub enum AdminResponseContentV0 { + EmptyResponse, + Users(Vec), +} + +/// Response to an `AdminRequest` V0 +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct AdminResponseV0 { /// Request ID pub id: i64, - /// Request content - pub content: BrokerRequestContentV0, + /// Result (including but not limited to Result) + pub result: u16, + + pub content: AdminResponseContentV0, + + /// Optional padding + #[serde(with = "serde_bytes")] + pub padding: Vec, } -/// Broker request +/// Response to an `AdminRequest` #[derive(Clone, Debug, Serialize, Deserialize)] -pub enum BrokerRequest { - V0(BrokerRequestV0), +pub enum AdminResponse { + V0(AdminResponseV0), } -impl BrokerRequest { - pub fn id(&self) -> i64 { - match self { - BrokerRequest::V0(o) => o.id, - } - } - pub fn set_id(&mut self, id: i64) { - match self { - BrokerRequest::V0(v0) => { - v0.id = id; - } +impl From> for AdminResponseV0 { + fn from(res: Result<(), ProtocolError>) -> AdminResponseV0 { + AdminResponseV0 { + id: 0, + result: res.map(|_| 0).unwrap_or_else(|e| e.into()), + content: AdminResponseContentV0::EmptyResponse, + padding: vec![], } } - pub fn type_id(&self) -> TypeId { - match self { - BrokerRequest::V0(o) => o.content.type_id(), - } - } - pub fn content_v0(&self) -> BrokerRequestContentV0 { - match self { - BrokerRequest::V0(o) => o.content.clone(), +} + +impl From, ProtocolError>> for AdminResponseV0 { + fn from(res: Result, ProtocolError>) -> AdminResponseV0 { + match res { + Err(e) => AdminResponseV0 { + id: 0, + result: e.into(), + content: AdminResponseContentV0::EmptyResponse, + padding: vec![], + }, + Ok(vec) => AdminResponseV0 { + id: 0, + result: 0, + content: AdminResponseContentV0::Users(vec), + padding: vec![], + }, } } } -/// Content of `BrokerResponseV0` -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum BrokerResponseContentV0 { - EmptyResponse(()), +impl From for ProtocolMessage { + fn from(msg: AdminResponseV0) -> ProtocolMessage { + ProtocolMessage::AdminResponse(AdminResponse::V0(msg)) + } } -/// Response to a `BrokerRequest` -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct BrokerResponseV0 { - /// Request ID - pub id: i64, - - /// Result (including but not limited to Result) - pub result: u16, - - pub content: BrokerResponseContentV0, +impl From for ProtocolMessage { + fn from(msg: AdminResponse) -> ProtocolMessage { + ProtocolMessage::AdminResponse(msg) + } } -/// Response to a `BrokerRequest` -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum BrokerResponse { - V0(BrokerResponseV0), +impl TryFrom for AdminResponse { + type Error = ProtocolError; + fn try_from(msg: ProtocolMessage) -> Result { + if let ProtocolMessage::AdminResponse(res) = msg { + Ok(res) + } else { + Err(ProtocolError::InvalidValue) + } + } } -impl BrokerResponse { +impl AdminResponse { pub fn id(&self) -> i64 { match self { - BrokerResponse::V0(o) => o.id, + Self::V0(o) => o.id, } } pub fn set_id(&mut self, id: i64) { match self { - BrokerResponse::V0(v0) => { + Self::V0(v0) => { v0.id = id; } } } pub fn result(&self) -> u16 { match self { - BrokerResponse::V0(o) => o.result, + Self::V0(o) => o.result, + } + } + pub fn content_v0(&self) -> AdminResponseContentV0 { + match self { + Self::V0(o) => o.content.clone(), } } } +// +// CLIENT PROTOCOL +// + /// Request to join an overlay #[derive(Clone, Debug, Serialize, Deserialize)] pub struct OverlayJoinV0 { @@ -2276,9 +2223,9 @@ pub enum TopicDisconnect { V0(TopicDisconnectV0), } -/// Content of `BrokerOverlayRequestV0` +/// Content of `ClientRequestV0` #[derive(Clone, Debug, Serialize, Deserialize)] -pub enum BrokerOverlayRequestContentV0 { +pub enum ClientRequestContentV0 { OverlayConnect(OverlayConnect), // FIXME remove OverlayStatusReq(OverlayStatusReq), OverlayJoin(OverlayJoin), @@ -2299,52 +2246,52 @@ pub enum BrokerOverlayRequestContentV0 { } /// Broker overlay request #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct BrokerOverlayRequestV0 { +pub struct ClientRequestV0 { /// Request ID pub id: i64, /// Request content - pub content: BrokerOverlayRequestContentV0, + pub content: ClientRequestContentV0, } /// Broker overlay request #[derive(Clone, Debug, Serialize, Deserialize)] -pub enum BrokerOverlayRequest { - V0(BrokerOverlayRequestV0), +pub enum ClientRequest { + V0(ClientRequestV0), } -impl BrokerOverlayRequest { +impl ClientRequest { pub fn id(&self) -> i64 { match self { - BrokerOverlayRequest::V0(o) => o.id, + ClientRequest::V0(o) => o.id, } } pub fn set_id(&mut self, id: i64) { match self { - BrokerOverlayRequest::V0(v0) => { + ClientRequest::V0(v0) => { v0.id = id; } } } - pub fn content_v0(&self) -> &BrokerOverlayRequestContentV0 { + pub fn content_v0(&self) -> &ClientRequestContentV0 { match self { - BrokerOverlayRequest::V0(o) => &o.content, + ClientRequest::V0(o) => &o.content, } } } -/// Content of `BrokerOverlayResponseV0` +/// Content of `ClientResponseV0` #[derive(Clone, Debug, Serialize, Deserialize)] -pub enum BrokerOverlayResponseContentV0 { - EmptyResponse(()), +pub enum ClientResponseContentV0 { + EmptyResponse, Block(Block), ObjectId(ObjectId), OverlayStatusResp(OverlayStatusResp), } -/// Response to a `BrokerOverlayRequest` +/// Response to a `ClientRequest` #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct BrokerOverlayResponseV0 { +pub struct ClientResponseV0 { /// Request ID pub id: i64, @@ -2352,112 +2299,113 @@ pub struct BrokerOverlayResponseV0 { pub result: u16, /// Response content - pub content: BrokerOverlayResponseContentV0, + pub content: ClientResponseContentV0, } -/// Response to a `BrokerOverlayRequest` +/// Response to a `ClientRequest` #[derive(Clone, Debug, Serialize, Deserialize)] -pub enum BrokerOverlayResponse { - V0(BrokerOverlayResponseV0), +pub enum ClientResponse { + V0(ClientResponseV0), } -impl BrokerOverlayResponse { +impl ClientResponse { pub fn id(&self) -> i64 { match self { - BrokerOverlayResponse::V0(o) => o.id, + ClientResponse::V0(o) => o.id, } } pub fn set_id(&mut self, id: i64) { match self { - BrokerOverlayResponse::V0(v0) => { + ClientResponse::V0(v0) => { v0.id = id; } } } pub fn result(&self) -> u16 { match self { - BrokerOverlayResponse::V0(o) => o.result, + ClientResponse::V0(o) => o.result, } } pub fn block(&self) -> Option<&Block> { match self { - BrokerOverlayResponse::V0(o) => match &o.content { - BrokerOverlayResponseContentV0::Block(b) => Some(b), + ClientResponse::V0(o) => match &o.content { + ClientResponseContentV0::Block(b) => Some(b), _ => panic!("this not a block response"), }, } } pub fn object_id(&self) -> ObjectId { match self { - BrokerOverlayResponse::V0(o) => match &o.content { - BrokerOverlayResponseContentV0::ObjectId(id) => id.clone(), - _ => panic!("this not an objectId reponse"), + ClientResponse::V0(o) => match &o.content { + ClientResponseContentV0::ObjectId(id) => id.clone(), + _ => panic!("this not an objectId response"), }, } } } -/// Content of `BrokerOverlayMessageV0` +/// Content of `ClientMessageV0` #[derive(Clone, Debug, Serialize, Deserialize)] -pub enum BrokerOverlayMessageContentV0 { - BrokerOverlayRequest(BrokerOverlayRequest), - BrokerOverlayResponse(BrokerOverlayResponse), +pub enum ClientMessageContentV0 { + ClientRequest(ClientRequest), + ClientResponse(ClientResponse), Event(Event), } /// Broker message for an overlay #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct BrokerOverlayMessageV0 { +pub struct ClientMessageV0 { pub overlay: OverlayId, - pub content: BrokerOverlayMessageContentV0, + pub content: ClientMessageContentV0, + /// Optional padding + #[serde(with = "serde_bytes")] + pub padding: Vec, } /// Broker message for an overlay #[derive(Clone, Debug, Serialize, Deserialize)] -pub enum BrokerOverlayMessage { - V0(BrokerOverlayMessageV0), +pub enum ClientMessage { + V0(ClientMessageV0), } -impl BrokerOverlayMessage { - pub fn content_v0(&self) -> &BrokerOverlayMessageContentV0 { +impl ClientMessage { + pub fn content_v0(&self) -> &ClientMessageContentV0 { match self { - BrokerOverlayMessage::V0(o) => &o.content, + ClientMessage::V0(o) => &o.content, } } - pub fn overlay_request(&self) -> &BrokerOverlayRequest { + pub fn overlay_request(&self) -> &ClientRequest { match self { - BrokerOverlayMessage::V0(o) => match &o.content { - BrokerOverlayMessageContentV0::BrokerOverlayRequest(r) => &r, + ClientMessage::V0(o) => match &o.content { + ClientMessageContentV0::ClientRequest(r) => &r, _ => panic!("not an overlay request"), }, } } pub fn overlay_id(&self) -> OverlayId { match self { - BrokerOverlayMessage::V0(o) => o.overlay, + ClientMessage::V0(o) => o.overlay, } } pub fn is_request(&self) -> bool { match self { - BrokerOverlayMessage::V0(o) => matches!( - o.content, - BrokerOverlayMessageContentV0::BrokerOverlayRequest { .. } - ), + ClientMessage::V0(o) => { + matches!(o.content, ClientMessageContentV0::ClientRequest { .. }) + } } } pub fn is_response(&self) -> bool { match self { - BrokerOverlayMessage::V0(o) => matches!( - o.content, - BrokerOverlayMessageContentV0::BrokerOverlayResponse { .. } - ), + ClientMessage::V0(o) => { + matches!(o.content, ClientMessageContentV0::ClientResponse { .. }) + } } } pub fn id(&self) -> i64 { match self { - BrokerOverlayMessage::V0(o) => match &o.content { - BrokerOverlayMessageContentV0::BrokerOverlayResponse(r) => r.id(), - BrokerOverlayMessageContentV0::BrokerOverlayRequest(r) => r.id(), - BrokerOverlayMessageContentV0::Event(_) => { + ClientMessage::V0(o) => match &o.content { + ClientMessageContentV0::ClientResponse(r) => r.id(), + ClientMessageContentV0::ClientRequest(r) => r.id(), + ClientMessageContentV0::Event(_) => { panic!("it is an event") } }, @@ -2465,10 +2413,10 @@ impl BrokerOverlayMessage { } pub fn set_id(&mut self, id: i64) { match self { - BrokerOverlayMessage::V0(o) => match &mut o.content { - BrokerOverlayMessageContentV0::BrokerOverlayResponse(ref mut r) => r.set_id(id), - BrokerOverlayMessageContentV0::BrokerOverlayRequest(ref mut r) => r.set_id(id), - BrokerOverlayMessageContentV0::Event(_) => { + 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(_) => { panic!("it is an event") } }, @@ -2476,12 +2424,12 @@ impl BrokerOverlayMessage { } pub fn result(&self) -> u16 { match self { - BrokerOverlayMessage::V0(o) => match &o.content { - BrokerOverlayMessageContentV0::BrokerOverlayResponse(r) => r.result(), - BrokerOverlayMessageContentV0::BrokerOverlayRequest(r) => { + ClientMessage::V0(o) => match &o.content { + ClientMessageContentV0::ClientResponse(r) => r.result(), + ClientMessageContentV0::ClientRequest(r) => { panic!("it is not a response"); } - BrokerOverlayMessageContentV0::Event(_) => { + ClientMessageContentV0::Event(_) => { panic!("it is not a response"); } }, @@ -2489,12 +2437,12 @@ impl BrokerOverlayMessage { } pub fn block<'a>(&self) -> Option<&Block> { match self { - BrokerOverlayMessage::V0(o) => match &o.content { - BrokerOverlayMessageContentV0::BrokerOverlayResponse(r) => r.block(), - BrokerOverlayMessageContentV0::BrokerOverlayRequest(r) => { + ClientMessage::V0(o) => match &o.content { + ClientMessageContentV0::ClientResponse(r) => r.block(), + ClientMessageContentV0::ClientRequest(r) => { panic!("it is not a response"); } - BrokerOverlayMessageContentV0::Event(_) => { + ClientMessageContentV0::Event(_) => { panic!("it is not a response"); } }, @@ -2502,158 +2450,15 @@ impl BrokerOverlayMessage { } pub fn object_id<'a>(&self) -> ObjectId { match self { - BrokerOverlayMessage::V0(o) => match &o.content { - BrokerOverlayMessageContentV0::BrokerOverlayResponse(r) => r.object_id(), - BrokerOverlayMessageContentV0::BrokerOverlayRequest(r) => { - panic!("it is not a response"); - } - BrokerOverlayMessageContentV0::Event(_) => { - panic!("it is not a response"); - } - }, - } - } -} - -/// Content of BrokerMessageV0 -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum BrokerMessageContentV0 { - BrokerRequest(BrokerRequest), - BrokerResponse(BrokerResponse), - BrokerOverlayMessage(BrokerOverlayMessage), -} - -/// Broker message -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct BrokerMessageV0 { - /// Message content - pub content: BrokerMessageContentV0, - - /// Optional padding - #[serde(with = "serde_bytes")] - pub padding: Vec, -} - -/// Broker message -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum BrokerMessage { - V0(BrokerMessageV0), - Close, //TODO: remove Close. -} - -impl BrokerMessage { - pub fn type_id(&self) -> TypeId { - match self { - BrokerMessage::V0(a) => match &a.content { - BrokerMessageContentV0::BrokerOverlayMessage(p) => p.type_id(), - BrokerMessageContentV0::BrokerResponse(p) => p.type_id(), - BrokerMessageContentV0::BrokerRequest(p) => p.type_id(), - }, - BrokerMessage::Close => TypeId::of::(), - } - } - pub fn is_close(&self) -> bool { - match self { - BrokerMessage::V0(o) => false, - BrokerMessage::Close => true, - } - } - /// Get the content - pub fn content(&self) -> BrokerMessageContentV0 { - match self { - BrokerMessage::V0(o) => o.content.clone(), - BrokerMessage::Close => panic!("Close not implemented"), - } - } - pub fn is_request(&self) -> bool { - match self { - BrokerMessage::V0(o) => match &o.content { - BrokerMessageContentV0::BrokerOverlayMessage(p) => p.is_request(), - BrokerMessageContentV0::BrokerResponse(_) => false, - BrokerMessageContentV0::BrokerRequest(_) => true, - }, - BrokerMessage::Close => panic!("Close not implemented"), - } - } - pub fn is_response(&self) -> bool { - match self { - BrokerMessage::V0(o) => match &o.content { - BrokerMessageContentV0::BrokerOverlayMessage(p) => p.is_response(), - BrokerMessageContentV0::BrokerResponse(_) => true, - BrokerMessageContentV0::BrokerRequest(_) => false, - }, - BrokerMessage::Close => panic!("Close not implemented"), - } - } - pub fn id(&self) -> i64 { - match self { - BrokerMessage::V0(o) => match &o.content { - BrokerMessageContentV0::BrokerOverlayMessage(p) => p.id(), - BrokerMessageContentV0::BrokerResponse(r) => r.id(), - BrokerMessageContentV0::BrokerRequest(r) => r.id(), - }, - BrokerMessage::Close => panic!("Close not implemented"), - } - } - pub fn set_id(&mut self, id: i64) { - match self { - BrokerMessage::V0(o) => match &mut o.content { - BrokerMessageContentV0::BrokerOverlayMessage(ref mut p) => p.set_id(id), - BrokerMessageContentV0::BrokerResponse(ref mut r) => r.set_id(id), - BrokerMessageContentV0::BrokerRequest(ref mut r) => r.set_id(id), - }, - BrokerMessage::Close => panic!("Close not implemented"), - } - } - pub fn result(&self) -> u16 { - match self { - BrokerMessage::V0(o) => match &o.content { - BrokerMessageContentV0::BrokerOverlayMessage(p) => p.result(), - BrokerMessageContentV0::BrokerResponse(r) => r.result(), - BrokerMessageContentV0::BrokerRequest(_) => { + ClientMessage::V0(o) => match &o.content { + ClientMessageContentV0::ClientResponse(r) => r.object_id(), + ClientMessageContentV0::ClientRequest(r) => { panic!("it is not a response"); } - }, - BrokerMessage::Close => panic!("Close not implemented"), - } - } - pub fn is_overlay(&self) -> bool { - match self { - BrokerMessage::V0(o) => match &o.content { - BrokerMessageContentV0::BrokerOverlayMessage(p) => true, - BrokerMessageContentV0::BrokerResponse(r) => false, - BrokerMessageContentV0::BrokerRequest(r) => false, - }, - BrokerMessage::Close => panic!("Close not implemented"), - } - } - pub fn response_block(&self) -> Option<&Block> { - match self { - BrokerMessage::V0(o) => match &o.content { - BrokerMessageContentV0::BrokerOverlayMessage(p) => p.block(), - BrokerMessageContentV0::BrokerResponse(r) => { - panic!("it doesn't have a response block. it is not an overlay response"); - } - BrokerMessageContentV0::BrokerRequest(_) => { - panic!("it is not a response"); - } - }, - BrokerMessage::Close => panic!("Close not implemented"), - } - } - - pub fn response_object_id(&self) -> ObjectId { - match self { - BrokerMessage::V0(o) => match &o.content { - BrokerMessageContentV0::BrokerOverlayMessage(p) => p.object_id(), - BrokerMessageContentV0::BrokerResponse(r) => { - panic!("it doesn't have a response ObjectId. it is not an overlay response"); - } - BrokerMessageContentV0::BrokerRequest(_) => { + ClientMessageContentV0::Event(_) => { panic!("it is not a response"); } }, - BrokerMessage::Close => panic!("Close not implemented"), } } } @@ -2878,7 +2683,10 @@ pub enum ProtocolMessage { AuthResult(AuthResult), ExtRequest(ExtRequest), ExtResponse(ExtResponse), - BrokerMessage(BrokerMessage), + //AdminRequest(AdminRequest), + AdminResponse(AdminResponse), + ClientMessage(ClientMessage), + CoreMessage(CoreMessage), } impl ProtocolMessage { @@ -2886,7 +2694,7 @@ impl ProtocolMessage { match self { ProtocolMessage::ExtRequest(ext_req) => ext_req.id(), ProtocolMessage::ExtResponse(ext_res) => ext_res.id(), - ProtocolMessage::BrokerMessage(broker_msg) => broker_msg.id(), + ProtocolMessage::ClientMessage(client_msg) => client_msg.id(), _ => 0, } } @@ -2894,7 +2702,7 @@ impl ProtocolMessage { match self { ProtocolMessage::ExtRequest(ext_req) => ext_req.set_id(id), ProtocolMessage::ExtResponse(ext_res) => ext_res.set_id(id), - ProtocolMessage::BrokerMessage(broker_msg) => broker_msg.set_id(id), + ProtocolMessage::ClientMessage(client_msg) => client_msg.set_id(id), _ => panic!("cannot set ID"), } } @@ -2907,7 +2715,10 @@ impl ProtocolMessage { ProtocolMessage::AuthResult(a) => a.type_id(), ProtocolMessage::ExtRequest(a) => a.type_id(), ProtocolMessage::ExtResponse(a) => a.type_id(), - ProtocolMessage::BrokerMessage(a) => a.type_id(), + ProtocolMessage::ClientMessage(a) => a.type_id(), + ProtocolMessage::CoreMessage(a) => a.type_id(), + //ProtocolMessage::AdminRequest(a) => a.type_id(), + ProtocolMessage::AdminResponse(a) => a.type_id(), ProtocolMessage::Probe(a) => a.type_id(), ProtocolMessage::ProbeResponse(a) => a.type_id(), ProtocolMessage::Relay(a) => a.type_id(), diff --git a/p2p-repo/src/kcv_store.rs b/p2p-repo/src/kcv_store.rs index b1d2bd9..3fc76d5 100644 --- a/p2p-repo/src/kcv_store.rs +++ b/p2p-repo/src/kcv_store.rs @@ -68,6 +68,13 @@ pub trait ReadTransaction { suffix: Option, value: &Vec, ) -> Result<(), StorageError>; + + fn get_all_keys_and_values( + &self, + prefix: u8, + key_size: usize, + suffix: Option, + ) -> Result, Vec)>, StorageError>; } pub trait KCVStore: ReadTransaction { diff --git a/p2p-repo/src/types.rs b/p2p-repo/src/types.rs index 6bcc87a..6ab13f1 100644 --- a/p2p-repo/src/types.rs +++ b/p2p-repo/src/types.rs @@ -120,6 +120,9 @@ impl PubKey { _ => panic!("can only convert an edward key to montgomery"), } } + pub fn nil() -> Self { + PubKey::Ed25519PubKey([0u8; 32]) + } } impl fmt::Display for PubKey { @@ -190,11 +193,26 @@ impl TryFrom<&[u8]> for PrivKey { } } +impl TryFrom<&str> for PrivKey { + type Error = NgError; + fn try_from(str: &str) -> Result { + let key = decode_key(str).map_err(|_| NgError::InvalidKey)?; + Ok(PrivKey::Ed25519PrivKey(key)) + } +} + impl fmt::Display for PrivKey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let priv_key_ser = serde_bare::to_vec(self).unwrap(); - let prix_key_encoded = base64_url::encode(&priv_key_ser); - write!(f, "{}", prix_key_encoded) + match self { + Self::Ed25519PrivKey(ed) => { + //let priv_key_ser = serde_bare::to_vec(ed).unwrap(); + let prix_key_encoded = base64_url::encode(ed); + write!(f, "{}", prix_key_encoded) + } + _ => { + unimplemented!(); + } + } } } diff --git a/stores-lmdb/Cargo.toml b/stores-lmdb/Cargo.toml index 8598fe4..b1136d0 100644 --- a/stores-lmdb/Cargo.toml +++ b/stores-lmdb/Cargo.toml @@ -16,5 +16,5 @@ hex = "0.4.3" [target.'cfg(not(target_arch = "wasm32"))'.dependencies.rkv] git = "https://git.nextgraph.org/NextGraph/rkv.git" -rev = "8f5ad79c0c93138b1bdc0a1254a7c6b4d357a5d9" +rev = "c746abb443b7bb4541ebbef2b71e8d0f9eb39f6a" features = [ "lmdb" ] diff --git a/stores-lmdb/src/kcv_store.rs b/stores-lmdb/src/kcv_store.rs index ee157a9..b1274c2 100644 --- a/stores-lmdb/src/kcv_store.rs +++ b/stores-lmdb/src/kcv_store.rs @@ -41,6 +41,14 @@ impl<'a> LmdbTransaction<'a> { } impl<'a> ReadTransaction for LmdbTransaction<'a> { + fn get_all_keys_and_values( + &self, + prefix: u8, + key_size: usize, + suffix: Option, + ) -> Result, Vec)>, StorageError> { + unimplemented!(); + } /// Load a single value property from the store. fn get(&self, prefix: u8, key: &Vec, suffix: Option) -> Result, StorageError> { let property = LmdbKCVStore::compute_property(prefix, key, suffix); @@ -219,7 +227,66 @@ pub struct LmdbKCVStore { path: String, } +fn compare(a: &[T], b: &[T]) -> std::cmp::Ordering { + let mut iter_b = b.iter(); + for v in a { + match iter_b.next() { + Some(w) => match v.cmp(w) { + std::cmp::Ordering::Equal => continue, + ord => return ord, + }, + None => break, + } + } + return a.len().cmp(&b.len()); +} + impl ReadTransaction for LmdbKCVStore { + fn get_all_keys_and_values( + &self, + prefix: u8, + key_size: usize, + suffix: Option, + ) -> Result, Vec)>, StorageError> { + let vec_key_start = vec![0u8; key_size]; + let vec_key_end = vec![255u8; key_size]; + let property_start = Self::compute_property(prefix, &vec_key_start, suffix); + let property_end = Self::compute_property(prefix, &vec_key_end, suffix); + let lock = self.environment.read().unwrap(); + let reader = lock.read().unwrap(); + let mut iter = self + .main_store + .iter_from(&reader, property_start) + .map_err(|e| StorageError::BackendError)?; + let mut vector: Vec<(Vec, Vec)> = vec![]; + while let res = iter.next() { + match res { + Some(Ok(val)) => { + match compare(val.0, property_end.as_slice()) { + std::cmp::Ordering::Less | std::cmp::Ordering::Equal => { + if suffix.is_some() { + if val.0.len() < (key_size + 2) + || val.0[1 + key_size] != suffix.unwrap() + { + continue; + } + } else if val.0.len() > (key_size + 1) { + continue; + } + vector.push((val.0.to_vec(), val.1.to_bytes().unwrap())); + } + _ => {} //, + } + } + Some(Err(_e)) => return Err(StorageError::BackendError), + None => { + break; + } + } + } + Ok(vector) + } + /// Load a single value property from the store. fn get(&self, prefix: u8, key: &Vec, suffix: Option) -> Result, StorageError> { let property = Self::compute_property(prefix, key, suffix);