From 293036d29b62588e80222b7953eff6d40e0929c7 Mon Sep 17 00:00:00 2001 From: magine Date: Mon, 1 Jan 2024 22:55:51 +0800 Subject: [PATCH] refactor!: add RpcHandler to export api across platform (#508) * Add protobuf api in js * fix: fix package upload error * Update package config and release action * Add protobuf api in rust * Setup protoc in github action * Generate tonic service client * Update rings_node service description * Remove tonic, enable serde in protobuf generated code * Upgrade proto-serde to proto-build-config * Fix rings_node serde derive * Remove tonic cause you cannot use it in wasm * Rename backend compose --- .github/workflows/auto-release.yml | 11 +- .github/workflows/qaci.yml | 12 + .gitignore | 8 +- Cargo.lock | 211 +++++++- Cargo.toml | 2 +- core/src/dht/stabilization.rs | 8 +- core/src/inspect.rs | 10 +- core/src/storage/persistence/idb.rs | 19 +- core/src/storage/persistence/kv.rs | 14 +- core/src/storage/persistence/mod.rs | 4 +- core/src/tests/default/test_stabilization.rs | 6 +- core/src/tests/wasm/test_idb_storage.rs | 2 +- core/src/utils.rs | 24 +- examples/ffi/rings.h | 99 ++++ examples/ffi/rings.py | 2 +- index.ts | 2 + node/Cargo.toml | 1 + node/bin/rings.rs | 60 +-- node/build.rs | 20 - node/src/backend/browser.rs | 24 +- node/src/backend/ffi.rs | 12 +- node/src/backend/mod.rs | 8 +- node/src/backend/native/extension.rs | 14 +- node/src/backend/native/mod.rs | 20 +- node/src/backend/native/service/mod.rs | 31 +- node/src/backend/native/service/tcp_proxy.rs | 29 +- node/src/backend/types.rs | 38 +- node/src/error.rs | 4 +- node/src/jsonrpc/handler.rs | 177 ------- node/src/jsonrpc/mod.rs | 13 - node/src/jsonrpc/server.rs | 491 ------------------- node/src/lib.rs | 2 +- node/src/native/cli.rs | 13 - node/src/native/config.rs | 23 +- node/src/native/endpoint/mod.rs | 167 +++++-- node/src/processor.rs | 61 +-- node/src/provider/browser/mod.rs | 1 - node/src/provider/browser/provider.rs | 32 +- node/src/provider/browser/utils.rs | 67 --- node/src/provider/ffi.rs | 43 +- node/src/provider/mod.rs | 58 +-- node/src/rpc_impl.rs | 319 ++++++++++++ node/src/seed.rs | 21 +- node/src/tests/wasm/browser.rs | 157 ++---- node/src/tests/wasm/processor.rs | 18 +- package.json | 22 +- rpc/Cargo.toml | 17 +- rpc/build.rs | 19 + rpc/src/client.rs | 17 - rpc/src/lib.rs | 1 + rpc/src/method.rs | 6 +- rpc/src/protos/build_config.yaml | 51 ++ rpc/src/protos/mod.rs | 56 +++ rpc/src/protos/rings_node.proto | 206 ++++++++ rpc/src/protos/rings_node.rs | 286 +++++++++++ rpc/src/protos/rings_node_handler.rs | 205 ++++++++ tsconfig.json | 10 + 57 files changed, 1895 insertions(+), 1359 deletions(-) create mode 100644 examples/ffi/rings.h create mode 100644 index.ts delete mode 100644 node/src/jsonrpc/handler.rs delete mode 100644 node/src/jsonrpc/mod.rs delete mode 100644 node/src/jsonrpc/server.rs delete mode 100644 node/src/provider/browser/utils.rs create mode 100644 node/src/rpc_impl.rs create mode 100644 rpc/build.rs create mode 100644 rpc/src/protos/build_config.yaml create mode 100644 rpc/src/protos/mod.rs create mode 100644 rpc/src/protos/rings_node.proto create mode 100644 rpc/src/protos/rings_node.rs create mode 100644 rpc/src/protos/rings_node_handler.rs create mode 100644 tsconfig.json diff --git a/.github/workflows/auto-release.yml b/.github/workflows/auto-release.yml index 91444e497..e7891a179 100644 --- a/.github/workflows/auto-release.yml +++ b/.github/workflows/auto-release.yml @@ -75,6 +75,9 @@ jobs: run: | rustup target add ${{ steps.target.outputs.target }} + - name: Setup protoc + uses: arduino/setup-protoc@v2.1.0 + - name: Setup rust toolchain run: | rustup show @@ -120,6 +123,9 @@ jobs: run: | rustup target add ${{ steps.target.outputs.target }} + - name: Setup protoc + uses: arduino/setup-protoc@v2.1.0 + - name: Setup rust toolchain run: | rustup show @@ -179,6 +185,9 @@ jobs: - name: Install wasm-pack run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + - name: Setup protoc + uses: arduino/setup-protoc@v2.1.0 + - name: Setup rust toolchain run: | rustup show @@ -194,7 +203,7 @@ jobs: - name: Build run: | - npm pack + npm install && npm pack mv ringsnetwork-rings-node-*.tgz rings-${{ needs.gen_version.outputs.version }}-${{ steps.target.outputs.target }}.tgz - uses: actions/upload-artifact@v3 diff --git a/.github/workflows/qaci.yml b/.github/workflows/qaci.yml index 022d9b4b9..7ff052719 100644 --- a/.github/workflows/qaci.yml +++ b/.github/workflows/qaci.yml @@ -24,6 +24,9 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Setup protoc + uses: arduino/setup-protoc@v2.1.0 + - name: Setup rust toolchain run: rustup show @@ -67,6 +70,9 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Setup protoc + uses: arduino/setup-protoc@v2.1.0 + - name: Setup rust toolchain run: rustup show @@ -97,6 +103,9 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Setup protoc + uses: arduino/setup-protoc@v2.1.0 + - name: Setup rust toolchain run: rustup show @@ -128,6 +137,9 @@ jobs: - name: Check typos uses: crate-ci/typos@master + - name: Setup protoc + uses: arduino/setup-protoc@v2.1.0 + - name: Setup rust toolchain run: | rustup install nightly diff --git a/.gitignore b/.gitignore index b2b177a27..868c9b614 100644 --- a/.gitignore +++ b/.gitignore @@ -29,9 +29,5 @@ config.yaml # wasm-pack related package-lock.json -*.d.ts -*.js -*.wasm - -# cffi related -rings.h +dist/ +node_modules/ diff --git a/Cargo.lock b/Cargo.lock index 5d517654c..2eb5a63dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -613,9 +613,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" dependencies = [ "serde", ] @@ -628,7 +628,7 @@ checksum = "4b922faaf31122819ec80c4047cc684c6979a087366c069611e33649bf98e18d" dependencies = [ "clap 3.2.25", "heck", - "indexmap", + "indexmap 1.9.3", "log", "proc-macro2", "quote", @@ -719,7 +719,7 @@ dependencies = [ "atty", "bitflags 1.3.2", "clap_lex 0.2.4", - "indexmap", + "indexmap 1.9.3", "strsim", "termcolor", "textwrap", @@ -899,7 +899,7 @@ dependencies = [ "cranelift-entity", "fxhash", "hashbrown 0.12.3", - "indexmap", + "indexmap 1.9.3", "log", "smallvec", ] @@ -1481,6 +1481,12 @@ dependencies = [ "syn 2.0.29", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.3.1" @@ -1585,6 +1591,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "flate2" version = "1.0.26" @@ -1808,7 +1820,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" dependencies = [ "fallible-iterator", - "indexmap", + "indexmap 1.9.3", "stable_deref_trait", ] @@ -1852,7 +1864,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -2170,6 +2182,16 @@ dependencies = [ "hashbrown 0.12.3", ] +[[package]] +name = "indexmap" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad227c3af19d4914570ad36d30409928b75967c298feb9ea1969db3a610bb14e" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", +] + [[package]] name = "inout" version = "0.1.3" @@ -2522,6 +2544,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + [[package]] name = "nix" version = "0.24.3" @@ -2678,7 +2706,7 @@ dependencies = [ "fnv", "futures-channel", "futures-util", - "indexmap", + "indexmap 1.9.3", "js-sys", "once_cell", "pin-project-lite", @@ -2879,6 +2907,16 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +[[package]] +name = "petgraph" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +dependencies = [ + "fixedbitset", + "indexmap 2.0.1", +] + [[package]] name = "pin-project" version = "1.1.0" @@ -2967,6 +3005,16 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "prettyplease" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +dependencies = [ + "proc-macro2", + "syn 1.0.109", +] + [[package]] name = "primeorder" version = "0.13.2" @@ -3032,6 +3080,93 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prost" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +dependencies = [ + "bytes", + "prost-derive 0.11.9", +] + +[[package]] +name = "prost" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" +dependencies = [ + "bytes", + "prost-derive 0.12.3", +] + +[[package]] +name = "prost-build" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" +dependencies = [ + "bytes", + "heck", + "itertools", + "lazy_static", + "log", + "multimap", + "petgraph", + "prettyplease", + "prost 0.11.9", + "prost-types", + "regex", + "syn 1.0.109", + "tempfile", + "which", +] + +[[package]] +name = "prost-build-config" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f7cb53ff633dc9e857237f4a06e853340cd503dbac9bccd7463168ffdedacc7" +dependencies = [ + "prost-build", + "serde", +] + +[[package]] +name = "prost-derive" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "prost-derive" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 2.0.29", +] + +[[package]] +name = "prost-types" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" +dependencies = [ + "prost 0.11.9", +] + [[package]] name = "ptr_meta" version = "0.1.4" @@ -3410,7 +3545,7 @@ dependencies = [ [[package]] name = "rings-core" -version = "0.4.2" +version = "0.5.0" dependencies = [ "arrayref", "async-channel", @@ -3472,7 +3607,7 @@ dependencies = [ [[package]] name = "rings-derive" -version = "0.4.2" +version = "0.5.0" dependencies = [ "proc-macro2", "quote", @@ -3483,7 +3618,7 @@ dependencies = [ [[package]] name = "rings-node" -version = "0.4.2" +version = "0.5.0" dependencies = [ "anyhow", "arrayref", @@ -3523,6 +3658,7 @@ dependencies = [ "serde-wasm-bindgen 0.6.1", "serde_json", "serde_yaml", + "strum", "thiserror", "tokio", "tokio-util", @@ -3542,23 +3678,28 @@ dependencies = [ [[package]] name = "rings-rpc" -version = "0.4.2" +version = "0.5.0" dependencies = [ + "async-trait", "base64 0.13.1", + "bytes", "http", "jsonrpc-core", "jsonrpc-pubsub", + "prost 0.12.3", + "prost-build-config", "reqwest", "reqwest-wasm", "rings-core", "serde", "serde_json", + "serde_yaml", "thiserror", ] [[package]] name = "rings-transport" -version = "0.4.2" +version = "0.5.0" dependencies = [ "async-trait", "bincode", @@ -3590,7 +3731,7 @@ dependencies = [ "bitvec", "bytecheck", "hashbrown 0.12.3", - "indexmap", + "indexmap 1.9.3", "ptr_meta", "rend", "rkyv_derive", @@ -3865,9 +4006,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.185" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be9b6f69f1dfd54c3b568ffa45c310d6973a5e5148fd40cf515acaf38cf5bc31" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] @@ -3896,9 +4037,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.185" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc59dfdcbad1437773485e0367fea4b090a2e0a16d9ffc46af47764536a298ec" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", @@ -3939,11 +4080,11 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.21" +version = "0.9.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9d684e3ec7de3bf5466b32bd75303ac16f0736426e5a4e0d6e489559ce1249c" +checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c" dependencies = [ - "indexmap", + "indexmap 2.0.1", "itoa", "ryu", "serde", @@ -4158,6 +4299,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" + [[package]] name = "stun" version = "0.4.4" @@ -4505,7 +4652,7 @@ version = "0.19.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" dependencies = [ - "indexmap", + "indexmap 1.9.3", "toml_datetime", "winnow", ] @@ -4992,7 +5139,7 @@ dependencies = [ "bytes", "cfg-if 1.0.0", "derivative", - "indexmap", + "indexmap 1.9.3", "js-sys", "more-asserts", "rustc-demangle", @@ -5076,7 +5223,7 @@ dependencies = [ "bytecheck", "enum-iterator", "enumset", - "indexmap", + "indexmap 1.9.3", "more-asserts", "rkyv", "target-lexicon", @@ -5097,7 +5244,7 @@ dependencies = [ "derivative", "enum-iterator", "fnv", - "indexmap", + "indexmap 1.9.3", "lazy_static", "libc", "mach", @@ -5122,7 +5269,7 @@ version = "0.95.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2ea896273ea99b15132414be1da01ab0d8836415083298ecaffbe308eaac87a" dependencies = [ - "indexmap", + "indexmap 1.9.3", "url", ] @@ -5511,6 +5658,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.13", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 35d0773ef..24a238077 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ resolver = "2" members = ["core", "transport", "node", "rpc", "derive"] [workspace.package] -version = "0.4.2" +version = "0.5.0" edition = "2021" license = "GPL-3.0" authors = ["RND "] diff --git a/core/src/dht/stabilization.rs b/core/src/dht/stabilization.rs index a71af8811..cca15f8b9 100644 --- a/core/src/dht/stabilization.rs +++ b/core/src/dht/stabilization.rs @@ -29,7 +29,7 @@ use crate::swarm::Swarm; pub struct Stabilization { chord: Arc, swarm: Arc, - timeout: usize, + timeout: u64, } /// A trait with `wait` method. @@ -58,7 +58,7 @@ impl Stabilization { impl Stabilization { /// Create a new instance of Stabilization - pub fn new(swarm: Arc, timeout: usize) -> Self { + pub fn new(swarm: Arc, timeout: u64) -> Self { Self { chord: swarm.dht(), swarm, @@ -67,7 +67,7 @@ impl Stabilization { } /// Get timeout of waiting delays. - pub fn get_timeout(&self) -> usize { + pub fn get_timeout(&self) -> u64 { self.timeout } } @@ -206,7 +206,7 @@ mod stabilizer { impl TStabilize for Stabilization { async fn wait(self: Arc) { loop { - let timeout = Delay::new(Duration::from_secs(self.timeout as u64)).fuse(); + let timeout = Delay::new(Duration::from_secs(self.timeout)).fuse(); pin_mut!(timeout); select! { _ = timeout => self diff --git a/core/src/inspect.rs b/core/src/inspect.rs index 0cdbcda57..5e8c4e0ca 100644 --- a/core/src/inspect.rs +++ b/core/src/inspect.rs @@ -31,7 +31,7 @@ pub struct DHTInspect { pub successors: Vec, #[serde(default)] pub predecessor: Option, - pub finger_table: Vec<(Option, usize, usize)>, + pub finger_table: Vec<(Option, u64, u64)>, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -126,11 +126,11 @@ impl StorageInspect { } } -pub fn compress_iter(iter: impl Iterator) -> Vec<(T, usize, usize)> +pub fn compress_iter(iter: impl Iterator) -> Vec<(T, u64, u64)> where T: PartialEq { let mut result = vec![]; - let mut start = 0; - let mut count = 0; + let mut start = 0u64; + let mut count = 0u64; let mut prev: Option = None; for (i, x) in iter.enumerate() { @@ -142,7 +142,7 @@ where T: PartialEq { if let Some(p) = prev { result.push((p, start, start + count - 1)); } - start = i; + start = i as u64; count = 1; } } diff --git a/core/src/storage/persistence/idb.rs b/core/src/storage/persistence/idb.rs index 682131195..dd33c0f93 100644 --- a/core/src/storage/persistence/idb.rs +++ b/core/src/storage/persistence/idb.rs @@ -31,7 +31,7 @@ pub const DEFAULT_REXIE_STORE_NAME: &str = "rings-storage"; struct DataStruct { key: String, last_visit_time: i64, - visit_count: u64, + visit_count: u32, created_time: i64, data: T, } @@ -53,7 +53,7 @@ impl DataStruct { /// StorageInstance struct pub struct IDBStorage { db: Rexie, - cap: usize, + cap: u32, storage_name: String, } @@ -66,7 +66,7 @@ pub trait IDBStorageBasic { impl IDBStorage { /// New IDBStorage /// * cap: rows of data limit - pub async fn new_with_cap(cap: usize) -> Result { + pub async fn new_with_cap(cap: u32) -> Result { Self::new_with_cap_and_name(cap, DEFAULT_REXIE_STORE_NAME).await } @@ -78,13 +78,13 @@ impl IDBStorage { /// New IDBStorage /// * cap: max_size in bytes /// * path: db file location - pub async fn new_with_cap_and_path

(cap: usize, _path: P) -> Result + pub async fn new_with_cap_and_path

(cap: u32, _path: P) -> Result where P: AsRef { Self::new_with_cap(cap).await } /// New IDBStorage with capacity and name - pub async fn new_with_cap_and_name(cap: usize, name: &str) -> Result { + pub async fn new_with_cap_and_name(cap: u32, name: &str) -> Result { if cap == 0 { return Err(Error::InvalidCapacity); } @@ -217,13 +217,13 @@ impl PersistenceStorageOperation for IDBStorage { Ok(()) } - async fn count(&self) -> Result { + async fn count(&self) -> Result { let (_tx, store) = self.get_tx_store(TransactionMode::ReadOnly)?; let count = store.count(None).await.map_err(Error::IDBError)?; - Ok(count as u64) + Ok(count) } - async fn max_size(&self) -> Result { + async fn max_size(&self) -> Result { Ok(self.cap) } @@ -242,12 +242,11 @@ impl PersistenceStorageOperation for IDBStorage { async fn prune(&self) -> Result<()> { let (tx, store) = self.get_tx_store(TransactionMode::ReadWrite)?; - let count = store.count(None).await.map_err(Error::IDBError)? as usize; + let count = store.count(None).await.map_err(Error::IDBError)?; if count < self.cap { return Ok(()); } let delete_count = count.sub(self.cap).add(1); - let delete_count = u32::try_from(delete_count).unwrap_or(0); if delete_count == 0 { return Ok(()); } diff --git a/core/src/storage/persistence/kv.rs b/core/src/storage/persistence/kv.rs index 846217719..68cbe8604 100644 --- a/core/src/storage/persistence/kv.rs +++ b/core/src/storage/persistence/kv.rs @@ -22,7 +22,7 @@ trait KvStorageBasic { #[allow(dead_code)] pub struct KvStorage { db: sled::Db, - cap: usize, + cap: u32, path: String, } @@ -30,7 +30,7 @@ impl KvStorage { /// New KvStorage /// * cap: max_size in bytes /// * path: db file location - pub async fn new_with_cap_and_path

(cap: usize, path: P) -> Result + pub async fn new_with_cap_and_path

(cap: u32, path: P) -> Result where P: AsRef { let db = sled::Config::new() .path(path.as_ref()) @@ -48,13 +48,13 @@ impl KvStorage { /// New KvStorage /// * cap: max_size in bytes /// * name: db file location - pub async fn new_with_cap_and_name(cap: usize, name: &str) -> Result { + pub async fn new_with_cap_and_name(cap: u32, name: &str) -> Result { Self::new_with_cap_and_path(cap, name).await } /// New KvStorage with default path /// default_path is `./` - pub async fn new_with_cap(cap: usize) -> Result { + pub async fn new_with_cap(cap: u32) -> Result { Self::new_with_cap_and_path(cap, "./data").await } @@ -104,11 +104,11 @@ impl PersistenceStorageOperation for KvStorage { Ok(()) } - async fn count(&self) -> Result { - Ok(self.db.len() as u64) + async fn count(&self) -> Result { + Ok(self.db.len() as u32) } - async fn max_size(&self) -> Result { + async fn max_size(&self) -> Result { Ok(self.cap) } diff --git a/core/src/storage/persistence/mod.rs b/core/src/storage/persistence/mod.rs index 73ed4be9a..88bd469d7 100644 --- a/core/src/storage/persistence/mod.rs +++ b/core/src/storage/persistence/mod.rs @@ -40,10 +40,10 @@ pub trait PersistenceStorageOperation { async fn clear(&self) -> Result<()>; /// Get the current storage usage, if applicable. - async fn count(&self) -> Result; + async fn count(&self) -> Result; /// Get the maximum storage size, if applicable. - async fn max_size(&self) -> Result; + async fn max_size(&self) -> Result; /// Get the storage size, if applicable. async fn total_size(&self) -> Result; diff --git a/core/src/tests/default/test_stabilization.rs b/core/src/tests/default/test_stabilization.rs index 78b8ec75f..6128fe282 100644 --- a/core/src/tests/default/test_stabilization.rs +++ b/core/src/tests/default/test_stabilization.rs @@ -18,11 +18,11 @@ use crate::tests::manually_establish_connection; async fn run_stabilize(swarm: Arc) { let mut result = Result::<()>::Ok(()); - let stabilization = Stabilization::new(swarm, 5usize); + let stabilization = Stabilization::new(swarm, 5); let timeout_in_secs = stabilization.get_timeout(); println!("RUN Stabilization"); while result.is_ok() { - let timeout = sleep(Duration::from_secs(timeout_in_secs as u64)); + let timeout = sleep(Duration::from_secs(timeout_in_secs)); tokio::pin!(timeout); tokio::select! { _ = timeout.as_mut() => { @@ -71,7 +71,7 @@ async fn test_stabilization_once() -> Result<()> { sleep(Duration::from_millis(1000)).await; assert!(swarm1.dht().successors().list()?.contains(&key2.address().into())); assert!(swarm2.dht().successors().list()?.contains(&key1.address().into())); - let stabilization = Stabilization::new(Arc::clone(&swarm1), 5usize); + let stabilization = Stabilization::new(Arc::clone(&swarm1), 5); let _ = stabilization.stabilize().await; sleep(Duration::from_millis(10000)).await; assert_eq!(*swarm2.dht().lock_predecessor()?, Some(key1.address().into())); diff --git a/core/src/tests/wasm/test_idb_storage.rs b/core/src/tests/wasm/test_idb_storage.rs index 32f05e42e..65b258e7c 100644 --- a/core/src/tests/wasm/test_idb_storage.rs +++ b/core/src/tests/wasm/test_idb_storage.rs @@ -13,7 +13,7 @@ use crate::storage::persistence::PersistenceStorageOperation; use crate::storage::persistence::PersistenceStorageReadAndWrite; use crate::storage::persistence::PersistenceStorageRemove; -async fn create_db_instance(cap: usize) -> IDBStorage { +async fn create_db_instance(cap: u32) -> IDBStorage { let instance = IDBStorage::new_with_cap(cap).await.unwrap(); let (tx, store) = instance.get_tx_store(TransactionMode::ReadWrite).unwrap(); store.clear().await.unwrap(); diff --git a/core/src/utils.rs b/core/src/utils.rs index 4e6ef9b36..e3d68cb8e 100644 --- a/core/src/utils.rs +++ b/core/src/utils.rs @@ -8,7 +8,6 @@ pub fn get_epoch_ms() -> u128 { #[cfg(feature = "wasm")] /// Toolset for wasm pub mod js_value { - use js_sys::Reflect; use serde::de::DeserializeOwned; use serde::Serialize; use serde::Serializer; @@ -17,21 +16,6 @@ pub mod js_value { use crate::error::Error; use crate::error::Result; - /// Get property from a JsValue. - pub fn get(obj: &JsValue, key: impl Into) -> Result { - let key = key.into(); - let value = Reflect::get(obj, &JsValue::from(key.clone())) - .map_err(|_| Error::FailedOnGetProperty(key.clone()))?; - serde_wasm_bindgen::from_value(value).map_err(Error::SerdeWasmBindgenError) - } - - /// Set Property to a JsValue. - pub fn set(obj: &JsValue, key: impl Into, value: impl Into) -> Result { - let key = key.into(); - Reflect::set(obj, &JsValue::from(key.clone()), &value.into()) - .map_err(|_| Error::FailedOnSetProperty(key.clone())) - } - /// From serde to JsValue pub fn serialize(obj: &impl Serialize) -> Result { let serializer = serde_wasm_bindgen::Serializer::json_compatible(); @@ -44,6 +28,14 @@ pub mod js_value { pub fn deserialize(obj: impl Into) -> Result { serde_wasm_bindgen::from_value(obj.into()).map_err(Error::SerdeWasmBindgenError) } + + /// From JsValue to serde_json::Value + pub fn json_value(obj: impl Into) -> Result { + let s = js_sys::JSON::stringify(&obj.into()) + .map_err(|_| Error::JsError("failed to stringify obj".to_string()))?; + + serde_json::from_str(&String::from(s)).map_err(Error::Deserialize) + } } #[cfg(feature = "wasm")] diff --git a/examples/ffi/rings.h b/examples/ffi/rings.h new file mode 100644 index 000000000..56177dfe0 --- /dev/null +++ b/examples/ffi/rings.h @@ -0,0 +1,99 @@ +typedef enum LogLevel { + Debug, + Info, + Warn, + Error, + Trace, +} LogLevel; + +/** + * Processor for rings-node rpc server + */ +typedef struct Processor Processor; + +/** + * Internal rpc handler for rings-node rpc server + */ +typedef struct InternalRpcHandler InternalRpcHandler; + +/** + * A structure to represent the Provider in a C-compatible format. + * This is necessary as using Arc directly in FFI can be unsafe. + */ +typedef struct ProviderPtr { + const struct Processor *processor; + const InternalRpcHandler *handler; +} ProviderPtr; + +/** + * Context for handling backend behaviour + */ +typedef struct FFIBackendBehaviour { + void (**paintext_message_handler)(const struct FFIBackendBehaviour*, + const struct ProviderPtr*, + const char*, + const char*); + void (**service_message_handler)(const struct FFIBackendBehaviour*, + const struct ProviderPtr*, + const char*, + const char*); + void (**extension_message_handler)(const struct FFIBackendBehaviour*, + const struct ProviderPtr*, + const char*, + const char*); +} FFIBackendBehaviour; + +/** + * Backend behaviour for FFI + */ +struct FFIBackendBehaviour new_ffi_backend_behaviour(void (*paintext_message_handler)(const struct FFIBackendBehaviour*, + const struct ProviderPtr*, + const char*, + const char*), + void (*service_message_handler)(const struct FFIBackendBehaviour*, + const struct ProviderPtr*, + const char*, + const char*), + void (*extension_message_handler)(const struct FFIBackendBehaviour*, + const struct ProviderPtr*, + const char*, + const char*)); + +void init_logging(enum LogLevel level); + +/** + * Start message listening and stabilization + * # Safety + * Listen function accept a ProviderPtr and will unsafety cast it into Arc based Provider + */ +void listen(const struct ProviderPtr *provider_ptr); + +/** + * Start message listening and stabilization + * This function will launch listener in a new thread + * # Safety + * Listen function accept a ProviderPtr and will unsafety cast it into Arc based Provider + */ +void async_listen(const struct ProviderPtr *provider_ptr); + +/** + * Request internal rpc api + * # Safety + * + * * This function accept a ProviderPtr and will unsafety cast it into Arc based Provider + * * This function cast CStr into Str + */ +const char *request(const struct ProviderPtr *provider_ptr, const char *method, const char *params); + +/** + * Craft a new Provider with signer and callback ptr + * # Safety + * + * * This function cast CStr into Str + */ +struct ProviderPtr new_provider_with_callback(const char *ice_server, + uint64_t stabilize_timeout, + const char *account, + const char *account_type, + void (*signer)(const char*, char*), + const struct FFIBackendBehaviour *callback_ptr); diff --git a/examples/ffi/rings.py b/examples/ffi/rings.py index d77fd5d73..1c06b2fbc 100644 --- a/examples/ffi/rings.py +++ b/examples/ffi/rings.py @@ -18,7 +18,7 @@ ffi = cffi.FFI() -c_header = open("./target/include/rings.h", "r").read() +c_header = open("./examples/ffi/rings.h", "r").read() c_header = re.sub(r"#define .*", "", c_header) ffi.cdef(c_header) rings = ffi.dlopen(f"./target/debug/librings_node.{extension}") diff --git a/index.ts b/index.ts new file mode 100644 index 000000000..2f27090a8 --- /dev/null +++ b/index.ts @@ -0,0 +1,2 @@ +export * from "./dist/rings_node"; +export * from "./dist/rings_node_proto"; diff --git a/node/Cargo.toml b/node/Cargo.toml index 93d09b2cd..6a96dee4d 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -85,6 +85,7 @@ rings-transport = { workspace = true } serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.70" serde_yaml = "0.9.17" +strum = "0.25.0" thiserror = "1" tracing = "0.1.37" tracing-log = "0.1.3" diff --git a/node/bin/rings.rs b/node/bin/rings.rs index 1aa21135e..d923be372 100644 --- a/node/bin/rings.rs +++ b/node/bin/rings.rs @@ -12,15 +12,16 @@ use futures::pin_mut; use futures::select; use futures::StreamExt; use futures_timer::Delay; +use rings_node::backend::native::BackendBehaviour; use rings_node::backend::native::BackendConfig; -use rings_node::backend::native::BackendContext; use rings_node::backend::Backend; use rings_node::logging::init_logging; use rings_node::logging::LogLevel; use rings_node::measure::PeriodicMeasure; use rings_node::native::cli::Client; use rings_node::native::config; -use rings_node::native::endpoint::run_http_api; +use rings_node::native::endpoint::run_external_api; +use rings_node::native::endpoint::run_internal_api; use rings_node::prelude::rings_core::dht::Did; use rings_node::prelude::rings_core::ecc::SecretKey; use rings_node::prelude::PersistenceStorage; @@ -104,11 +105,16 @@ struct NewSessionCommand { struct RunCommand { #[arg( long, - short = 'b', - help = "Rings node listen address. If not provided, use bind_addr in config file or 127.0.0.1:50000", + help = "Rings node external api listen address. If not provided, use bind_addr in config file or 127.0.0.1:50001", env )] - pub http_addr: Option, + pub external_api_addr: Option, + + #[arg( + long, + help = "Rings node internal api listen port. If not provided, use internal_api_port in config file or 50000" + )] + pub internal_api_port: Option, #[arg( long, @@ -130,7 +136,7 @@ struct RunCommand { help = "Stabilize service timeout. If not provided, use stabilize_timeout in config file or 3", env )] - pub stabilize_timeout: Option, + pub stabilize_timeout: Option, #[arg(long, help = "external ip address", env)] pub external_ip: Option, @@ -148,7 +154,7 @@ struct RunCommand { help = "Storage capcity. If not provider, use storage.capacity in config file or 200000000", env )] - pub storage_capacity: Option, + pub storage_capacity: Option, #[command(flatten)] config_args: ConfigArgs, @@ -300,8 +306,6 @@ struct PeerDisconnectCommand { #[derive(Subcommand, Debug)] #[command(rename_all = "kebab-case")] enum SendCommand { - #[command(about = "Sends a raw message.")] - Raw(SendRawCommand), #[command(about = "Sends an HTTP request message.")] Http(SendHttpCommand), #[command(about = "Sends a simple text message.")] @@ -310,16 +314,6 @@ enum SendCommand { Custom(SendCustomMessageCommand), } -#[derive(Args, Debug)] -struct SendRawCommand { - #[command(flatten)] - client_args: ClientArgs, - - to_did: String, - - text: String, -} - #[derive(Args, Debug)] struct SendHttpCommand { #[command(flatten)] @@ -414,8 +408,11 @@ async fn daemon_run(args: RunCommand) -> anyhow::Result<()> { if let Some(stabilize_timeout) = args.stabilize_timeout { c.stabilize_timeout = stabilize_timeout; } - if let Some(http_addr) = args.http_addr { - c.http_addr = http_addr; + if let Some(external_api_addr) = args.external_api_addr { + c.external_api_addr = external_api_addr; + } + if let Some(internal_api_port) = args.internal_api_port { + c.internal_api_port = internal_api_port; } let pc = ProcessorConfig::try_from(c.clone())?; @@ -451,17 +448,19 @@ async fn daemon_run(args: RunCommand) -> anyhow::Result<()> { .build()?, ); println!("Did: {}", processor.swarm.did()); - let backend_context = BackendContext::new(bc).await?; - let backend_service_names = backend_context.service_names(); + let backend_behaviour = BackendBehaviour::new(bc).await?; + let backend_service_names = backend_behaviour.service_names(); let provider = Arc::new(Provider::from_processor(processor.clone())); - let backend = Arc::new(Backend::new(provider, Box::new(backend_context))); + let backend = Arc::new(Backend::new(provider, Box::new(backend_behaviour))); processor.swarm.set_callback(backend).unwrap(); - let processor_clone = processor.clone(); + let processor_clone1 = processor.clone(); + let processor_clone2 = processor.clone(); let _ = futures::join!( processor.listen(), service_loop_register(&processor, backend_service_names), - run_http_api(c.http_addr, processor_clone), + run_internal_api(c.internal_api_port, processor_clone2), + run_external_api(c.external_api_addr, processor_clone1), ); Ok(()) @@ -543,15 +542,6 @@ async fn main() -> anyhow::Result<()> { .display(); Ok(()) } - Command::Send(SendCommand::Raw(args)) => { - args.client_args - .new_client() - .await? - .send_message(args.to_did.as_str(), args.text.as_str()) - .await? - .display(); - Ok(()) - } Command::Send(SendCommand::Http(args)) => { args.client_args .new_client() diff --git a/node/build.rs b/node/build.rs index 93e232b43..aec3ff499 100644 --- a/node/build.rs +++ b/node/build.rs @@ -2,24 +2,6 @@ extern crate cbindgen; use std::process::Command; -#[cfg(feature = "ffi")] -fn gen_cbinding() { - let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); - - match cbindgen::Builder::new() - .with_language(cbindgen::Language::C) - .with_no_includes() - .with_documentation(true) - .with_crate(&crate_dir) - .generate() - { - Ok(g) => { - g.write_to_file(crate_dir + "/../target/include/rings.h"); - } - Err(e) => println!("Unable to generate bindings, {:?}", e), - }; -} - fn gen_version() { if let Ok(output) = Command::new("git") .args(["rev-parse", "--short", "HEAD"]) @@ -31,7 +13,5 @@ fn gen_version() { } fn main() { - #[cfg(feature = "ffi")] - gen_cbinding(); gen_version(); } diff --git a/node/src/backend/browser.rs b/node/src/backend/browser.rs index 328275e07..810a2a661 100644 --- a/node/src/backend/browser.rs +++ b/node/src/backend/browser.rs @@ -1,5 +1,5 @@ #![warn(missing_docs)] -//! BackendContext implementation for browser +//! BackendBehaviour implementation for browser use std::sync::Arc; use async_trait::async_trait; @@ -11,21 +11,21 @@ use rings_derive::wasm_export; use wasm_bindgen::JsValue; use crate::backend::types::BackendMessage; -use crate::backend::types::MessageEndpoint; +use crate::backend::types::MessageHandler; use crate::error::Result; use crate::provider::Provider; -/// BackendContext is a context instance for handling backend message for browser +/// BackendBehaviour is a context instance for handling backend message for browser #[wasm_export] #[derive(Clone)] -pub struct BackendContext { +pub struct BackendBehaviour { service_message_handler: Option, plain_text_message_handler: Option, extension_message_handler: Option, } #[wasm_export] -impl BackendContext { +impl BackendBehaviour { /// Create a new instance of message callback, this function accept one argument: /// /// * backend_message_handler: `function(provider: Arc, payload: string, message: string) -> Promise<()>`; @@ -34,8 +34,8 @@ impl BackendContext { service_message_handler: Option, plain_text_message_handler: Option, extension_message_handler: Option, - ) -> BackendContext { - BackendContext { + ) -> BackendBehaviour { + BackendBehaviour { service_message_handler, plain_text_message_handler, extension_message_handler, @@ -45,8 +45,8 @@ impl BackendContext { #[cfg_attr(feature = "browser", async_trait(?Send))] #[cfg_attr(not(feature = "browser"), async_trait)] -impl MessageEndpoint for BackendContext { - async fn on_message( +impl MessageHandler for BackendBehaviour { + async fn handle_message( &self, provider: Arc, payload: &MessagePayload, @@ -58,20 +58,20 @@ impl MessageEndpoint for BackendContext { BackendMessage::ServiceMessage(m) => { if let Some(func) = &self.service_message_handler { let m = js_value::serialize(m)?; - let cb = js_func::of4::(func); + let cb = js_func::of4::(func); cb(self.clone(), provider, ctx, m).await?; } } BackendMessage::Extension(m) => { if let Some(func) = &self.extension_message_handler { let m = js_value::serialize(m)?; - let cb = js_func::of4::(func); + let cb = js_func::of4::(func); cb(self.clone(), provider, ctx, m).await?; } } BackendMessage::PlainText(m) => { if let Some(func) = &self.plain_text_message_handler { - let cb = js_func::of4::(func); + let cb = js_func::of4::(func); cb(self.clone(), provider, ctx, m.to_string()).await?; } } diff --git a/node/src/backend/ffi.rs b/node/src/backend/ffi.rs index 02cb8c24d..77b409967 100644 --- a/node/src/backend/ffi.rs +++ b/node/src/backend/ffi.rs @@ -9,7 +9,7 @@ use std::sync::Arc; use async_trait::async_trait; use crate::backend::types::BackendMessage; -use crate::backend::types::MessageEndpoint; +use crate::backend::types::MessageHandler; use crate::error::Result; use crate::prelude::MessagePayload; use crate::provider::ffi::ProviderPtr; @@ -80,9 +80,9 @@ pub extern "C" fn new_ffi_backend_behaviour( >, ) -> FFIBackendBehaviour { FFIBackendBehaviour { - paintext_message_handler: paintext_message_handler.map(|c| Box::new(c)), - service_message_handler: service_message_handler.map(|c| Box::new(c)), - extension_message_handler: extension_message_handler.map(|c| Box::new(c)), + paintext_message_handler: paintext_message_handler.map(Box::new), + service_message_handler: service_message_handler.map(Box::new), + extension_message_handler: extension_message_handler.map(Box::new), } } @@ -106,8 +106,8 @@ macro_rules! handle_backend_message { } #[async_trait] -impl MessageEndpoint for FFIBackendBehaviour { - async fn on_message( +impl MessageHandler for FFIBackendBehaviour { + async fn handle_message( &self, provider: Arc, payload: &MessagePayload, diff --git a/node/src/backend/mod.rs b/node/src/backend/mod.rs index dd82be4bd..54b0fef4c 100644 --- a/node/src/backend/mod.rs +++ b/node/src/backend/mod.rs @@ -11,7 +11,7 @@ use rings_core::message::MessagePayload; use rings_core::swarm::callback::SwarmCallback; use crate::backend::types::BackendMessage; -use crate::backend::types::MessageEndpoint; +use crate::backend::types::MessageHandler; use crate::error::Result; use crate::provider::Provider; @@ -25,9 +25,9 @@ pub mod native; pub mod ffi; #[cfg(feature = "node")] -type HandlerTrait = dyn MessageEndpoint + Send + Sync; +type HandlerTrait = dyn MessageHandler + Send + Sync; #[cfg(feature = "browser")] -type HandlerTrait = dyn MessageEndpoint; +type HandlerTrait = dyn MessageHandler; /// Backend handle custom messages from Swarm pub struct Backend { @@ -47,7 +47,7 @@ impl Backend { msg: &BackendMessage, ) -> Result<()> { let provider = self.provider.clone(); - self.handler.on_message(provider, payload, msg).await + self.handler.handle_message(provider, payload, msg).await } } diff --git a/node/src/backend/native/extension.rs b/node/src/backend/native/extension.rs index a95633d01..4eaf10212 100644 --- a/node/src/backend/native/extension.rs +++ b/node/src/backend/native/extension.rs @@ -38,7 +38,7 @@ use reqwest; use serde::Deserialize; use serde::Serialize; -use super::MessageEndpoint; +use super::MessageHandler; use crate::error::Error; use crate::error::Result; use crate::prelude::*; @@ -142,9 +142,9 @@ impl Extension { #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] -impl MessageEndpoint for Extension { +impl MessageHandler for Extension { /// Handles the incoming message by passing it to the extension handlers and returning the resulting events. - async fn on_message( + async fn handle_message( &self, provider: Arc, _ctx: &MessagePayload, @@ -243,11 +243,9 @@ pub mod loader { if let Some(provider) = guard.clone() { let params = serde_json::from_str(params) .map_err(|_| wasmer::RuntimeError::new("Failed to serialize params"))?; - futures::executor::block_on(provider.request_internal( - method.to_string(), - params, - None, - )) + futures::executor::block_on( + provider.request_internal(method.to_string(), params), + ) .map_err(|_| { wasmer::RuntimeError::new("Failed to call async request function") })?; diff --git a/node/src/backend/native/mod.rs b/node/src/backend/native/mod.rs index bd7eb98ca..3c17b33d4 100644 --- a/node/src/backend/native/mod.rs +++ b/node/src/backend/native/mod.rs @@ -1,6 +1,6 @@ #![warn(missing_docs)] //! This module provides the implementation of a native Backend, -//! which includes BackendConfig and BackendContext. +//! which includes BackendConfig and BackendBehaviour. //! //! This module has two submodules: extension and service. //! @@ -25,7 +25,7 @@ use crate::backend::native::extension::ExtensionConfig; use crate::backend::native::service::ServiceConfig; use crate::backend::native::service::ServiceProvider; use crate::backend::types::BackendMessage; -use crate::backend::types::MessageEndpoint; +use crate::backend::types::MessageHandler; use crate::error::Result; use crate::provider::Provider; @@ -37,16 +37,16 @@ pub struct BackendConfig { pub extensions: ExtensionConfig, } -/// BackendContext is a Context holder of backend message handler -pub struct BackendContext { +/// BackendBehaviour is a Context holder of backend message handler +pub struct BackendBehaviour { server: ServiceProvider, extension: Extension, } #[cfg_attr(feature = "browser", async_trait(?Send))] #[cfg_attr(not(feature = "browser"), async_trait)] -impl MessageEndpoint for BackendContext { - async fn on_message( +impl MessageHandler for BackendBehaviour { + async fn handle_message( &self, provider: Arc, payload: &MessagePayload, @@ -56,8 +56,8 @@ impl MessageEndpoint for BackendContext { } } -impl BackendContext { - /// Create a new BackendContext instance with config +impl BackendBehaviour { + /// Create a new BackendBehaviour instance with config pub async fn new(config: BackendConfig) -> Result { Ok(Self { server: ServiceProvider::new(config.services), @@ -82,10 +82,10 @@ impl BackendContext { ) -> Result<()> { match msg { BackendMessage::Extension(data) => { - self.extension.on_message(provider, payload, data).await + self.extension.handle_message(provider, payload, data).await } BackendMessage::ServiceMessage(data) => { - self.server.on_message(provider, payload, data).await + self.server.handle_message(provider, payload, data).await } BackendMessage::PlainText(text) => { let peer_did = payload.transaction.signer(); diff --git a/node/src/backend/native/service/mod.rs b/node/src/backend/native/service/mod.rs index bada19c20..3a4374711 100644 --- a/node/src/backend/native/service/mod.rs +++ b/node/src/backend/native/service/mod.rs @@ -24,12 +24,13 @@ use std::time::Duration; use dashmap::DashMap; use rings_core::message::MessagePayload; use rings_core::message::MessageVerificationExt; +use rings_rpc::method::Method; use serde::Deserialize; use serde::Serialize; use crate::backend::native::service::tcp_proxy::tcp_connect_with_timeout; use crate::backend::native::service::tcp_proxy::Tunnel; -use crate::backend::native::MessageEndpoint; +use crate::backend::native::MessageHandler; use crate::backend::types::BackendMessage; use crate::backend::types::HttpRequest; use crate::backend::types::HttpResponse; @@ -38,8 +39,6 @@ use crate::backend::types::TunnelId; use crate::consts::TCP_SERVER_TIMEOUT; use crate::error::Error; use crate::error::Result; -use crate::jsonrpc::server::BackendMessageParams; -use crate::prelude::jsonrpc_core::Params; use crate::provider::Provider; /// Service Config for creating a Server instance @@ -79,8 +78,8 @@ impl ServiceProvider { } #[async_trait::async_trait] -impl MessageEndpoint for ServiceProvider { - async fn on_message( +impl MessageHandler for ServiceProvider { + async fn handle_message( &self, provider: Arc, ctx: &MessagePayload, @@ -98,14 +97,8 @@ impl MessageEndpoint for ServiceProvider { reason: e, }; let backend_message: BackendMessage = msg.into(); - let params: Params = BackendMessageParams { - did: peer_did, - data: backend_message, - } - .try_into()?; - provider - .request("sendBackendMessage".to_string(), params, None) - .await?; + let params = backend_message.to_request_params(peer_did)?; + provider.request(Method::SendBackendMessage, params).await?; Err(Error::TunnelError(e)) } @@ -134,15 +127,9 @@ impl MessageEndpoint for ServiceProvider { ServiceMessage::HttpRequest(req) => { let service = self.service(&req.service).ok_or(Error::InvalidService)?; let resp = handle_http_request(service.addr, req).await?; - let msg: BackendMessage = ServiceMessage::HttpResponse(resp).into(); - let params: Params = BackendMessageParams { - did: peer_did, - data: msg, - } - .try_into()?; - let resp = provider - .request("sendBackendMessage".to_string(), params, None) - .await?; + let backend_message: BackendMessage = ServiceMessage::HttpResponse(resp).into(); + let params = backend_message.to_request_params(peer_did)?; + let resp = provider.request(Method::SendBackendMessage, params).await?; tracing::info!("done calling provider {:?}", resp); Ok(()) } diff --git a/node/src/backend/native/service/tcp_proxy.rs b/node/src/backend/native/service/tcp_proxy.rs index 036dbb209..ee0719734 100644 --- a/node/src/backend/native/service/tcp_proxy.rs +++ b/node/src/backend/native/service/tcp_proxy.rs @@ -6,6 +6,7 @@ use std::time::Duration; use bytes::Bytes; use rings_core::dht::Did; +use rings_rpc::method::Method; use tokio::io::AsyncReadExt; use tokio::io::AsyncWriteExt; use tokio::net::TcpStream; @@ -17,8 +18,6 @@ use crate::backend::types::BackendMessage; use crate::backend::types::ServiceMessage; use crate::backend::types::TunnelDefeat; use crate::backend::types::TunnelId; -use crate::jsonrpc::server::BackendMessageParams; -use crate::prelude::jsonrpc_core::Params; use crate::provider::Provider; /// Abstract Tcp Tunnel @@ -143,16 +142,8 @@ impl TunnelListener { }; let backend_message: BackendMessage = msg.into(); - let params: Params = BackendMessageParams { - did: self.peer_did, - data: backend_message, - } - .try_into() - .expect("Failed on cover backend message to rpc Params"); - if let Err(e) = provider - .request("sendBackendMessage".to_string(), params, None) - .await - { + let params = backend_message.to_request_params(self.peer_did).unwrap(); + if let Err(e) = provider.request(Method::SendBackendMessage, params).await { tracing::error!("Send TcpPackage message failed: {e:?}"); break TunnelDefeat::WebrtcDatachannelSendFailed; } @@ -183,9 +174,10 @@ impl TunnelListener { tid: self.tid, reason: defeat, }; - let backend_message: BackendMessage = msg.into(); - let params: Params = BackendMessageParams{did: self.peer_did, data: backend_message}.try_into().expect("Failed to cover backend message to rpc params"); - if let Err(e) = provider.request("sendBackendMessage".to_string(), params, None).await { + + let backend_message: BackendMessage = msg.into(); + let params = backend_message.to_request_params(self.peer_did).unwrap(); + if let Err(e) = provider.request(Method::SendBackendMessage, params).await { tracing::error!("Send TcpClose message failed: {e:?}"); } }, @@ -195,9 +187,10 @@ impl TunnelListener { tid: self.tid, reason: defeat, }; - let backend_message: BackendMessage = msg.into(); - let params: Params = BackendMessageParams{did: self.peer_did, data: backend_message}.try_into().expect("Failed to cover backend message to rpc params"); - let _ = provider.request("sendBackendMessage".to_string(), params, None).await; + + let backend_message: BackendMessage = msg.into(); + let params = backend_message.to_request_params(self.peer_did).unwrap(); + let _ = provider.request(Method::SendBackendMessage, params).await; } } } diff --git a/node/src/backend/types.rs b/node/src/backend/types.rs index 84aac24a3..680fcfc80 100644 --- a/node/src/backend/types.rs +++ b/node/src/backend/types.rs @@ -5,7 +5,9 @@ use std::io::ErrorKind as IOErrorKind; use std::sync::Arc; use bytes::Bytes; +use rings_core::dht::Did; use rings_core::message::MessagePayload; +use rings_rpc::protos::rings_node::SendBackendMessageRequest; use serde::Deserialize; use serde::Serialize; @@ -110,12 +112,12 @@ pub struct HttpResponse { pub body: Option, } -/// MessageEndpoint trait +/// MessageHandler trait #[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] -pub trait MessageEndpoint { +pub trait MessageHandler { /// handle_message - async fn on_message( + async fn handle_message( &self, provider: Arc, ctx: &MessagePayload, @@ -123,26 +125,6 @@ pub trait MessageEndpoint { ) -> Result<()>; } -#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] -impl MessageEndpoint - for Vec + Send + Sync>> -{ - async fn on_message( - &self, - provider: Arc, - ctx: &MessagePayload, - data: &BackendMessage, - ) -> Result<()> { - for endpoint in self { - if let Err(e) = endpoint.on_message(provider.clone(), ctx, data).await { - tracing::error!("Failed to handle message, {:?}", e) - } - } - Ok(()) - } -} - impl From for BackendMessage { fn from(val: ServiceMessage) -> Self { BackendMessage::ServiceMessage(val) @@ -160,3 +142,13 @@ impl From for TunnelDefeat { } } } + +impl BackendMessage { + /// Convert to SendBackendMessageRequest + pub fn to_request_params(&self, destination_did: Did) -> Result { + Ok(SendBackendMessageRequest { + destination_did: destination_did.to_string(), + data: serde_json::to_string(self)?, + }) + } +} diff --git a/node/src/error.rs b/node/src/error.rs index bcfad1197..4712f2057 100644 --- a/node/src/error.rs +++ b/node/src/error.rs @@ -51,8 +51,8 @@ pub enum Error { WasmGlobalMemoryLockError = 405, #[error("WASM failed to load file.")] WasmFailedToLoadFile = 406, - #[error("Invalid did.")] - InvalidDid = 500, + #[error("Invalid did: {0}")] + InvalidDid(String) = 500, #[error("Invalid method.")] InvalidMethod = 501, #[error("Internal error: {0}.")] diff --git a/node/src/jsonrpc/handler.rs b/node/src/jsonrpc/handler.rs deleted file mode 100644 index c841c45ec..000000000 --- a/node/src/jsonrpc/handler.rs +++ /dev/null @@ -1,177 +0,0 @@ -#![warn(missing_docs)] -//! JSON-RPC handler for both feature=browser and feature=node. -//! We support running the JSON-RPC server in either native or browser environment. -//! For the native environment, we use jsonrpc_core to handle requests. -//! For the browser environment, we utilize a Simple MessageHandler to process the requests. -use core::future::Future; -use std::pin::Pin; -use std::sync::Arc; - -use async_trait::async_trait; -use dashmap::DashMap; - -#[cfg(feature = "node")] -pub use self::default::build_handler; -use super::server; -use super::server::RpcMeta; -use crate::prelude::jsonrpc_core::types::error::Error; -use crate::prelude::jsonrpc_core::types::error::ErrorCode; -use crate::prelude::jsonrpc_core::types::request::MethodCall; -use crate::prelude::jsonrpc_core::types::response::Output; -use crate::prelude::jsonrpc_core::Params; -use crate::prelude::jsonrpc_core::Result; -use crate::prelude::jsonrpc_core::Value; -use crate::prelude::rings_rpc::method::Method; -use crate::processor::Processor; - -/// Type of handler function -#[cfg(feature = "node")] -pub type MethodFnBox = Box< - dyn Fn(Params, RpcMeta) -> Pin> + Send>> + Send + Sync, ->; - -/// Type of handler function, note that, in browser environment there is no Send and Sync -#[cfg(feature = "browser")] -pub type MethodFnBox = Box Pin>>>>; - -/// This macro is used to transform an asynchronous function item into -/// a boxed function reference that returns a pinned future. -macro_rules! pin { - ($fn:path) => { - Box::new(|params, meta| Box::pin($fn(params, meta))) - }; -} - -/// The InternalRPCHandler is -/// consistent with the signature type MetaIoHandler in jsonrpc_core, -/// but it is more simpler and does not have Send and Sync bounds. -/// Here, the type T is likely to be RpcMeta indefinitely. -#[derive(Clone)] -pub struct InternalRPCHandler { - /// MetaData for jsonrpc - meta: T, - /// Registered Methods - methods: DashMap>, -} - -/// This trait defines the register function for method registration. -pub trait MethodRegister { - /// Registers a method with the given name and function. - fn register(&mut self, name: &str, func: MethodFnBox); -} - -/// A trait that defines the method handler for handling method requests. -#[cfg_attr(feature = "browser", async_trait(?Send))] -#[cfg_attr(not(feature = "browser"), async_trait)] -pub trait MethodHandler { - /// Handles the incoming method request and returns the result. - /// Please note that the function type here is MethodCall instead of - /// Request in jsonrpc_core, which means that batch requests are not supported here. - async fn handle_request(&self, request: MethodCall) -> Result; -} - -#[cfg_attr(feature = "browser", async_trait(?Send))] -#[cfg_attr(not(feature = "browser"), async_trait)] -impl MethodHandler for InternalRPCHandler { - async fn handle_request(&self, request: MethodCall) -> Result { - let output: Result = if let Some(handler) = self.methods.get(&request.method) { - let ret = handler(request.params, self.meta.clone()).await?; - Ok(ret) - } else { - Err(Error { - code: ErrorCode::MethodNotFound, - message: format!("method {} is not found", &request.method), - data: None, - }) - }; - Ok(Output::from(output, request.id, None)) - } -} - -impl MethodRegister for InternalRPCHandler { - fn register(&mut self, name: &str, func: MethodFnBox) { - self.methods.insert(name.to_string(), Arc::new(func)); - } -} - -impl InternalRPCHandler { - /// Create a new instance of message handler - pub fn new(meta: server::RpcMeta) -> Self { - Self { - meta, - methods: DashMap::new(), - } - } - - /// Register all methods - pub fn build(&mut self) { - for m in methods() { - self.register(m.0.as_str(), m.1); - } - } -} - -impl From> for HandlerType { - fn from(p: Arc) -> Self { - let meta: server::RpcMeta = p.into(); - HandlerType::new(meta) - } -} - -/// Type alice for InternalRpcHandler -pub type HandlerType = InternalRPCHandler; - -/// This function will return a list of public functions for all interfaces. -/// If you need to define interfaces separately for the browser or native, -/// you should use cfg to control the conditions. -pub fn methods() -> Vec<(Method, MethodFnBox)> { - vec![ - ( - Method::ConnectPeerViaHttp, - pin!(server::connect_peer_via_http), - ), - ( - Method::ConnectPeerViaHttp, - pin!(server::connect_peer_via_http), - ), - (Method::ConnectWithSeed, pin!(server::connect_with_seed)), - (Method::AnswerOffer, pin!(server::answer_offer)), - (Method::ConnectWithDid, pin!(server::connect_with_did)), - (Method::CreateOffer, pin!(server::create_offer)), - (Method::AcceptAnswer, pin!(server::accept_answer)), - (Method::ListPeers, pin!(server::list_peers)), - (Method::Disconnect, pin!(server::close_connection)), - (Method::SendTo, pin!(server::send_raw_message)), - (Method::SendCustomMessage, pin!(server::send_custom_message)), - ( - Method::SendBackendMessage, - pin!(server::send_backend_message), - ), - ( - Method::PublishMessageToTopic, - pin!(server::publish_message_to_topic), - ), - ( - Method::FetchMessagesOfTopic, - pin!(server::fetch_messages_of_topic), - ), - (Method::RegisterService, pin!(server::register_service)), - (Method::LookupService, pin!(server::lookup_service)), - (Method::NodeInfo, pin!(server::node_info)), - (Method::NodeDid, pin!(server::node_did)), - ] -} - -/// Implementation for native node -#[cfg(feature = "node")] -pub mod default { - use super::*; - use crate::prelude::jsonrpc_core::MetaIoHandler; - - /// Build handler add method with metadata. - pub async fn build_handler(handler: &mut MetaIoHandler) { - for m in methods() { - handler.add_method_with_meta(m.0.as_str(), m.1); - } - } -} diff --git a/node/src/jsonrpc/mod.rs b/node/src/jsonrpc/mod.rs deleted file mode 100644 index 2c217d89f..000000000 --- a/node/src/jsonrpc/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! A jsonrpc-server of rings-node. -/// [JSON-RPC]: `` -// pub mod method; -//pub mod response; -pub mod server; -/// RpcMeta basic info struct -pub use server::RpcMeta; - -/// MetaIoHandler add methods from `super::methods::*` with RpcMeta -pub mod handler; -#[cfg(feature = "node")] -pub use self::handler::build_handler; -pub use self::handler::HandlerType; diff --git a/node/src/jsonrpc/server.rs b/node/src/jsonrpc/server.rs deleted file mode 100644 index a37e90ab6..000000000 --- a/node/src/jsonrpc/server.rs +++ /dev/null @@ -1,491 +0,0 @@ -#![warn(missing_docs)] -//! A jsonrpc-server of rings-node -/// [JSON-RPC]: https://www.jsonrpc.org/specification -use std::collections::HashSet; -use std::str::FromStr; -use std::sync::Arc; - -use futures::future::join_all; -use rings_core::swarm::impls::ConnectionHandshake; -use rings_transport::core::transport::ConnectionInterface; -use serde_json::Value; - -use crate::backend::types::BackendMessage; -use crate::error::Error as ServerError; -use crate::prelude::jsonrpc_core::Error; -use crate::prelude::jsonrpc_core::ErrorCode; -use crate::prelude::jsonrpc_core::Params; -use crate::prelude::jsonrpc_core::Result; -use crate::prelude::rings_core::dht::Did; -use crate::prelude::rings_core::message::Decoder; -use crate::prelude::rings_core::message::Encoded; -use crate::prelude::rings_core::message::Encoder; -use crate::prelude::rings_core::message::MessagePayload; -use crate::prelude::rings_core::prelude::vnode::VirtualNode; -use crate::prelude::rings_rpc; -use crate::prelude::rings_rpc::response::Peer; -use crate::processor::Processor; -use crate::seed::Seed; - -/// RpcMeta basic info struct -/// * processor: contain `swarm` instance and `stabilization` instance. -/// * is_auth: is_auth set true after verify. -#[derive(Clone)] -pub struct RpcMeta { - processor: Arc, - /// if is_auth set to true, rpc server of *native node* will check signature from - /// HEAD['X-SIGNATURE'] - is_auth: bool, -} - -impl RpcMeta { - fn require_authed(&self) -> Result<()> { - if !self.is_auth { - return Err(Error::from(ServerError::NoPermission)); - } - Ok(()) - } -} - -impl From<(Arc, bool)> for RpcMeta { - fn from((processor, is_auth): (Arc, bool)) -> Self { - Self { processor, is_auth } - } -} - -impl From> for RpcMeta { - fn from(processor: Arc) -> Self { - Self { - processor, - is_auth: true, - } - } -} - -/// Params for method `BackendMessage` -pub struct BackendMessageParams { - /// destination did - pub did: Did, - /// data of backend message - pub data: BackendMessage, -} - -impl TryFrom for BackendMessageParams { - type Error = Error; - fn try_from(params: Params) -> Result { - let params: Vec = params.parse()?; - let did = params - .get(0) - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))? - .as_str() - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))?; - let did = Did::from_str(did).map_err(|_| Error::new(ErrorCode::InvalidParams))?; - - let data = params - .get(1) - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))? - .as_str() - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))?; - let data: BackendMessage = - serde_json::from_str(data).map_err(|_| Error::new(ErrorCode::InvalidParams))?; - Ok(Self { did, data }) - } -} - -impl TryInto for BackendMessageParams { - type Error = Error; - fn try_into(self) -> Result { - let data: String = - serde_json::to_string(&self.data).map_err(|_| Error::new(ErrorCode::InvalidParams))?; - Ok(Params::Array(vec![ - serde_json::Value::String(self.did.to_string()), - serde_json::Value::String(data), - ])) - } -} - -pub(crate) async fn node_info(_: Params, meta: RpcMeta) -> Result { - let node_info = meta - .processor - .get_node_info() - .await - .map_err(|_| Error::new(ErrorCode::InternalError))?; - serde_json::to_value(node_info).map_err(|_| Error::new(ErrorCode::ParseError)) -} - -pub(crate) async fn node_did(_: Params, meta: RpcMeta) -> Result { - let did = meta.processor.did(); - serde_json::to_value(did).map_err(|_| Error::new(ErrorCode::ParseError)) -} - -/// Connect Peer VIA http -pub(crate) async fn connect_peer_via_http(params: Params, meta: RpcMeta) -> Result { - meta.require_authed()?; - let p: Vec = params.parse()?; - let peer_url = p - .first() - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))?; - let did = meta - .processor - .connect_peer_via_http(peer_url) - .await - .map_err(Error::from)?; - Ok(Value::String(did.to_string())) -} - -/// Connect Peer with seed -pub(crate) async fn connect_with_seed(params: Params, meta: RpcMeta) -> Result { - meta.require_authed()?; - let p: Vec = params.parse()?; - let seed = p - .first() - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))?; - - let mut connected_addresses: HashSet = - HashSet::from_iter(meta.processor.swarm.get_connection_ids()); - connected_addresses.insert(meta.processor.swarm.did()); - - let tasks = seed - .peers - .iter() - .filter(|&x| !connected_addresses.contains(&x.did)) - .map(|x| meta.processor.connect_peer_via_http(&x.endpoint)); - - let results = join_all(tasks).await; - - let first_err = results.into_iter().find(|x| x.is_err()); - if let Some(err) = first_err { - err.map_err(Error::from)?; - } - - Ok(Value::Null) -} - -/// Handle Connect with DID -pub(crate) async fn connect_with_did(params: Params, meta: RpcMeta) -> Result { - meta.require_authed()?; - let p: Vec = params.parse()?; - - let address_str = p - .first() - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))?; - let did = Did::from_str(address_str).map_err(|_| Error::new(ErrorCode::InvalidParams))?; - - meta.processor - .connect_with_did(did, true) - .await - .map_err(Error::from)?; - - Ok(Value::Null) -} - -/// Handle create offer -pub(crate) async fn create_offer(params: Params, meta: RpcMeta) -> Result { - meta.require_authed()?; - let p: Vec = params.parse()?; - - let address_str = p - .first() - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))?; - let did = Did::from_str(address_str).map_err(|_| Error::new(ErrorCode::InvalidParams))?; - - let (_, offer_payload) = meta - .processor - .swarm - .create_offer(did) - .await - .map_err(ServerError::CreateOffer) - .map_err(Error::from)?; - - let encoded = offer_payload - .encode() - .map_err(|_| ServerError::EncodeError)?; - serde_json::to_value(encoded) - .map_err(ServerError::SerdeJsonError) - .map_err(Error::from) -} - -/// Handle Answer Offer -pub(crate) async fn answer_offer(params: Params, meta: RpcMeta) -> Result { - let p: Vec = params.parse()?; - let offer_payload_str = p - .first() - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))?; - let encoded: Encoded = >::from(offer_payload_str); - let offer_payload = - MessagePayload::from_encoded(&encoded).map_err(|_| ServerError::DecodeError)?; - - let (_, answer_payload) = meta - .processor - .swarm - .answer_offer(offer_payload) - .await - .map_err(ServerError::AnswerOffer) - .map_err(Error::from)?; - - tracing::debug!("connect_peer_via_ice response: {:?}", answer_payload); - let encoded = answer_payload - .encode() - .map_err(|_| ServerError::EncodeError)?; - serde_json::to_value(encoded) - .map_err(ServerError::SerdeJsonError) - .map_err(Error::from) -} - -/// Handle accept answer -pub(crate) async fn accept_answer(params: Params, meta: RpcMeta) -> Result { - meta.require_authed()?; - - let p: Vec = params.parse()?; - let answer_payload_str = p - .first() - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))?; - let encoded: Encoded = >::from(answer_payload_str); - let answer_payload = - MessagePayload::from_encoded(&encoded).map_err(|_| ServerError::DecodeError)?; - - let dc = meta - .processor - .swarm - .accept_answer(answer_payload) - .await - .map_err(ServerError::AcceptAnswer)?; - - dc2p(dc) - .to_json_obj() - .map_err(|_| ServerError::EncodeError) - .map_err(Error::from) -} - -/// Handle list peers -pub(crate) async fn list_peers(_params: Params, meta: RpcMeta) -> Result { - meta.require_authed()?; - let peers = meta.processor.swarm.get_connections(); - let r: Vec = peers.into_iter().map(dc2p).collect(); - serde_json::to_value(r).map_err(|_| Error::from(ServerError::EncodeError)) -} - -/// Handle close connection -pub(crate) async fn close_connection(params: Params, meta: RpcMeta) -> Result { - meta.require_authed()?; - let params: Vec = params.parse()?; - let did = params - .first() - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))?; - let did = Did::from_str(did).map_err(|_| Error::from(ServerError::InvalidDid))?; - meta.processor.disconnect(did).await?; - Ok(serde_json::json!({})) -} - -/// Handle send message -pub(crate) async fn send_raw_message(params: Params, meta: RpcMeta) -> Result { - meta.require_authed()?; - let params: serde_json::Map = params.parse()?; - let destination = params - .get("destination") - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))? - .as_str() - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))?; - let text = params - .get("text") - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))? - .as_str() - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))?; - let tx_id = meta - .processor - .send_message(destination, text.as_bytes()) - .await?; - Ok( - serde_json::to_value(rings_rpc::response::SendMessageResponse::from( - tx_id.to_string(), - )) - .unwrap(), - ) -} - -/// send custom message to specifice destination -/// * Params -/// - destination: destination did -/// - data: base64 of [u8] -pub(crate) async fn send_custom_message(params: Params, meta: RpcMeta) -> Result { - meta.require_authed()?; - let params: Vec = params.parse()?; - let destination = params - .get(0) - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))? - .as_str() - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))?; - - let data = params - .get(1) - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))? - .as_str() - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))?; - - let data = base64::decode(data).map_err(|_| Error::new(ErrorCode::InvalidParams))?; - let tx_id = meta.processor.send_message(destination, &data).await?; - - Ok( - serde_json::to_value(rings_rpc::response::SendMessageResponse::from( - tx_id.to_string(), - )) - .unwrap(), - ) -} - -/// send custom message to specifice destination -/// * Params -/// - destination: destination did -/// - data: base64 of [u8] -pub(crate) async fn send_backend_message(params: Params, meta: RpcMeta) -> Result { - meta.require_authed()?; - let bm_params: BackendMessageParams = params.try_into()?; - let tx_id = meta - .processor - .send_backend_message(bm_params.did, bm_params.data) - .await?; - tracing::info!("Send Response message"); - Ok( - serde_json::to_value(rings_rpc::response::SendMessageResponse::from( - tx_id.to_string(), - )) - .unwrap(), - ) -} - -pub(crate) async fn publish_message_to_topic(params: Params, meta: RpcMeta) -> Result { - meta.require_authed()?; - let params: Vec = params.parse()?; - let topic = params - .get(0) - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))? - .as_str() - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))?; - let data = params - .get(1) - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))? - .as_str() - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))? - .to_string() - .encode() - .map_err(|_| Error::new(ErrorCode::InvalidParams))?; - - meta.processor.storage_append_data(topic, data).await?; - - Ok(serde_json::json!({})) -} - -pub(crate) async fn fetch_messages_of_topic(params: Params, meta: RpcMeta) -> Result { - meta.require_authed()?; - let params: Vec = params.parse()?; - let topic = params - .get(0) - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))? - .as_str() - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))?; - let index = params - .get(1) - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))? - .as_i64() - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))?; - - let vid = VirtualNode::gen_did(topic).map_err(|_| Error::new(ErrorCode::InvalidParams))?; - - meta.processor.storage_fetch(vid).await?; - let result = meta.processor.storage_check_cache(vid).await; - - if let Some(vnode) = result { - let messages = vnode - .data - .iter() - .skip(index as usize) - .map(|v| v.decode()) - .filter_map(|v| v.ok()) - .collect::>(); - Ok(serde_json::json!(messages)) - } else { - Ok(serde_json::json!(Vec::::new())) - } -} - -pub(crate) async fn register_service(params: Params, meta: RpcMeta) -> Result { - meta.require_authed()?; - let params: Vec = params.parse()?; - let name = params - .get(0) - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))? - .as_str() - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))?; - meta.processor.register_service(name).await?; - Ok(serde_json::json!({})) -} - -pub(crate) async fn lookup_service(params: Params, meta: RpcMeta) -> Result { - meta.require_authed()?; - let params: Vec = params.parse()?; - let name = params - .get(0) - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))? - .as_str() - .ok_or_else(|| Error::new(ErrorCode::InvalidParams))?; - - let rid = VirtualNode::gen_did(name).map_err(|_| Error::new(ErrorCode::InvalidParams))?; - - meta.processor.storage_fetch(rid).await?; - let result = meta.processor.storage_check_cache(rid).await; - - if let Some(vnode) = result { - let dids = vnode - .data - .iter() - .map(|v| v.decode()) - .filter_map(|v| v.ok()) - .collect::>(); - Ok(serde_json::json!(dids)) - } else { - Ok(serde_json::json!(Vec::::new())) - } -} - -fn dc2p((did, conn): (Did, impl ConnectionInterface)) -> Peer { - Peer { - did: did.to_string(), - cid: did.to_string(), - state: format!("{:?}", conn.webrtc_connection_state()), - } -} - -#[cfg(feature = "node")] -#[cfg(test)] -mod tests { - use std::sync::Arc; - - use jsonrpc_core::types::params::Params; - - use super::*; - use crate::prelude::*; - use crate::tests::native::prepare_processor; - - async fn new_rnd_meta() -> RpcMeta { - let (processor, _) = prepare_processor().await; - Arc::new(processor).into() - } - - #[tokio::test] - async fn test_maually_handshake() { - let meta1 = new_rnd_meta().await; - let meta2 = new_rnd_meta().await; - let offer = create_offer( - Params::Array(vec![meta2.processor.did().to_string().into()]), - meta1.clone(), - ) - .await - .unwrap(); - let answer = answer_offer(Params::Array(vec![offer]), meta2) - .await - .unwrap(); - accept_answer(Params::Array(vec![answer]), meta1) - .await - .unwrap(); - } -} diff --git a/node/src/lib.rs b/node/src/lib.rs index cfa13fe20..7036ca32b 100644 --- a/node/src/lib.rs +++ b/node/src/lib.rs @@ -3,7 +3,6 @@ pub mod backend; pub mod consts; pub mod error; -pub mod jsonrpc; pub mod logging; pub mod measure; #[cfg(feature = "node")] @@ -11,6 +10,7 @@ pub mod native; pub mod prelude; pub mod processor; pub mod provider; +mod rpc_impl; pub mod seed; #[cfg(test)] mod tests; diff --git a/node/src/native/cli.rs b/node/src/native/cli.rs index 502f9bdd4..cca55721b 100644 --- a/node/src/native/cli.rs +++ b/node/src/native/cli.rs @@ -24,7 +24,6 @@ use futures::select; use futures::FutureExt; use futures::Stream; use futures_timer::Delay; -use serde_json::json; use crate::backend::types::BackendMessage; use crate::backend::types::HttpRequest; @@ -131,18 +130,6 @@ impl Client { ClientOutput::ok("Done.".into(), ()) } - /// Sends a message to the specified peer. - pub async fn send_message(&self, did: &str, text: &str) -> Output<()> { - let mut params = serde_json::Map::new(); - params.insert("destination".to_owned(), json!(did)); - params.insert("text".to_owned(), json!(text)); - self.client - .send_message(did, text) - .await - .map_err(|e| anyhow::anyhow!("{}", e))?; - ClientOutput::ok("Done.".into(), ()) - } - /// Sends a custom message to the specified peer. pub async fn send_custom_message(&self, did: &str, data: &str) -> Output<()> { self.client diff --git a/node/src/native/config.rs b/node/src/native/config.rs index e029c13f7..efa67b00f 100644 --- a/node/src/native/config.rs +++ b/node/src/native/config.rs @@ -29,11 +29,12 @@ lazy_static::lazy_static! { }; } -pub const DEFAULT_BIND_ADDRESS: &str = "127.0.0.1:50000"; +pub const DEFAULT_INTERNAL_API_PORT: u16 = 50000; +pub const DEFAULT_EXTERNAL_API_ADDR: &str = "127.0.0.1:50001"; pub const DEFAULT_ENDPOINT_URL: &str = "http://127.0.0.1:50000"; pub const DEFAULT_ICE_SERVERS: &str = "stun://stun.l.google.com:19302"; -pub const DEFAULT_STABILIZE_TIMEOUT: usize = 3; -pub const DEFAULT_STORAGE_CAPACITY: usize = 200000000; +pub const DEFAULT_STABILIZE_TIMEOUT: u64 = 3; +pub const DEFAULT_STORAGE_CAPACITY: u32 = 200000000; pub fn get_storage_location

(prefix: P, path: P) -> String where P: AsRef { @@ -52,11 +53,11 @@ pub struct Config { #[serde(skip_serializing_if = "Option::is_none")] pub session_manager: Option, pub session_sk: Option, - #[serde(rename = "bind")] - pub http_addr: String, + pub internal_api_port: u16, + pub external_api_addr: String, pub endpoint_url: String, pub ice_servers: String, - pub stabilize_timeout: usize, + pub stabilize_timeout: u64, #[serde(skip_serializing_if = "Option::is_none")] pub external_ip: Option, /// When there is no configuration in the YAML file, @@ -134,7 +135,8 @@ impl Config { ecdsa_key: None, session_manager: None, session_sk: Some(session_sk), - http_addr: DEFAULT_BIND_ADDRESS.to_string(), + internal_api_port: DEFAULT_INTERNAL_API_PORT, + external_api_addr: DEFAULT_EXTERNAL_API_ADDR.to_string(), endpoint_url: DEFAULT_ENDPOINT_URL.to_string(), ice_servers: DEFAULT_ICE_SERVERS.to_string(), stabilize_timeout: DEFAULT_STABILIZE_TIMEOUT, @@ -170,11 +172,11 @@ impl Config { #[derive(Debug, Clone, Deserialize, Serialize)] pub struct StorageConfig { pub path: String, - pub capacity: usize, + pub capacity: u32, } impl StorageConfig { - pub fn new(path: &str, capacity: usize) -> Self { + pub fn new(path: &str, capacity: u32) -> Self { Self { path: path.to_string(), capacity, @@ -190,7 +192,8 @@ mod tests { fn test_deserialization_with_missed_field() { let yaml = r#" session_sk: session_sk -bind: 127.0.0.1:50000 +internal_api_port: 50000 +external_api_addr: 127.0.0.1:50001 endpoint_url: http://127.0.0.1:50000 ice_servers: stun://stun.l.google.com:19302 stabilize_timeout: 3 diff --git a/node/src/native/endpoint/mod.rs b/node/src/native/endpoint/mod.rs index c4ba7af4d..114cb66d2 100644 --- a/node/src/native/endpoint/mod.rs +++ b/node/src/native/endpoint/mod.rs @@ -16,18 +16,17 @@ use axum::Router; use tower_http::cors::CorsLayer; use self::http_error::HttpError; -use crate::jsonrpc::RpcMeta; use crate::prelude::jsonrpc_core::MetaIoHandler; -use crate::prelude::rings_rpc::response::NodeInfo; +use crate::prelude::rings_rpc::protos::rings_node::NodeInfoResponse; use crate::processor::Processor; -impl crate::prelude::jsonrpc_core::Metadata for RpcMeta {} - -/// Jsonrpc state +/// JSON-RPC state #[derive(Clone)] -pub struct JsonrpcState { +pub struct JsonRpcState +where M: crate::prelude::jsonrpc_core::Middleware> +{ processor: Arc, - io_handler: Arc>, + io_handler: MetaIoHandler, M>, } /// websocket state @@ -43,17 +42,17 @@ pub struct StatusState { processor: Arc, } -/// Run a web server to handle jsonrpc request -pub async fn run_http_api(addr: String, processor: Arc) -> anyhow::Result<()> { - let binding_addr = addr.parse().unwrap(); +struct ExternalRpcMiddleware; +struct InternalRpcMiddleware; - let mut jsonrpc_handler: MetaIoHandler = MetaIoHandler::default(); - crate::jsonrpc::build_handler(&mut jsonrpc_handler).await; - let jsonrpc_handler_layer = Arc::new(jsonrpc_handler); +/// Run a web server to handle jsonrpc request locally +pub async fn run_internal_api(port: u16, processor: Arc) -> anyhow::Result<()> { + let binding_addr = SocketAddr::from(([127, 0, 0, 1], port)); - let jsonrpc_state = Arc::new(JsonrpcState { + let jsonrpc_handler = MetaIoHandler::with_middleware(InternalRpcMiddleware); + let jsonrpc_state = Arc::new(JsonRpcState { processor: processor.clone(), - io_handler: jsonrpc_handler_layer, + io_handler: jsonrpc_handler, }); let ws_state = Arc::new(WsState { @@ -73,44 +72,53 @@ pub async fn run_http_api(addr: String, processor: Arc) -> anyhow::Re .layer(axum::middleware::from_fn(node_info_header)) .into_make_service_with_connect_info::(); + println!("JSON-RPC endpoint: http://{}", binding_addr); + println!("WebSocket endpoint: http://{}/ws", binding_addr); + axum::Server::bind(&binding_addr) + .serve(axum_make_service) + .await?; + Ok(()) +} + +/// Run a web server to handle jsonrpc request from external +pub async fn run_external_api(addr: String, processor: Arc) -> anyhow::Result<()> { + let binding_addr = addr.parse().unwrap(); + + let jsonrpc_handler = MetaIoHandler::with_middleware(ExternalRpcMiddleware); + let jsonrpc_state = Arc::new(JsonRpcState { + processor: processor.clone(), + io_handler: jsonrpc_handler, + }); + + let status_state = Arc::new(StatusState { processor }); + + let axum_make_service = Router::new() + .route( + "/", + post(jsonrpc_io_handler).with_state(jsonrpc_state.clone()), + ) + .route("/status", get(status_handler).with_state(status_state)) + .layer(CorsLayer::permissive()) + .layer(axum::middleware::from_fn(node_info_header)) + .into_make_service_with_connect_info::(); + println!("JSON-RPC endpoint: http://{}", addr); - println!("WebSocket endpoint: http://{}/ws", addr); axum::Server::bind(&binding_addr) .serve(axum_make_service) .await?; Ok(()) } -async fn jsonrpc_io_handler( - State(state): State>, - headermap: http::HeaderMap, +async fn jsonrpc_io_handler( + State(state): State>>, body: String, -) -> Result { - let is_auth = if let Some(signature) = headermap.get("X-SIGNATURE") { - let sig = base64::decode(signature).map_err(|e| { - tracing::debug!("signature: {:?}", signature); - tracing::error!("signature decode failed: {:?}", e); - HttpError::BadRequest - })?; - state - .processor - .swarm - .session_sk() - .session() - .verify(body.as_bytes(), sig) - .map_err(|e| { - tracing::debug!("body: {:?}", body); - tracing::debug!("signature: {:?}", signature); - tracing::error!("signature verify failed: {:?}", e); - e - }) - .is_ok() - } else { - false - }; +) -> Result +where + M: crate::prelude::jsonrpc_core::Middleware>, +{ let r = state .io_handler - .handle_request(&body, (state.processor.clone(), is_auth).into()) + .handle_request(&body, state.processor.clone()) .await .ok_or(HttpError::BadRequest)?; Ok(JsonResponse(r)) @@ -131,7 +139,7 @@ async fn node_info_header( async fn status_handler( State(state): State>, -) -> Result, HttpError> { +) -> Result, HttpError> { let info = state .processor .get_node_info() @@ -158,3 +166,74 @@ async fn ws_handler( tracing::info!("ws connected, remote: {}", addr); ws.on_upgrade(move |socket| self::ws::handle_socket(state, socket)) } + +mod jsonrpc_middleware_impl { + use std::future::Future; + + use rings_rpc::protos::rings_node_handler::ExternalRpcHandler; + use rings_rpc::protos::rings_node_handler::InternalRpcHandler; + + use super::*; + use crate::native::endpoint::jsonrpc_middleware_impl::middleware::NoopCallFuture; + use crate::native::endpoint::jsonrpc_middleware_impl::middleware::NoopFuture; + use crate::prelude::jsonrpc_core::futures_util::future; + use crate::prelude::jsonrpc_core::futures_util::future::Either; + use crate::prelude::jsonrpc_core::futures_util::FutureExt; + use crate::prelude::jsonrpc_core::*; + + impl Middleware> for InternalRpcMiddleware { + type Future = NoopFuture; + type CallFuture = NoopCallFuture; + + fn on_call( + &self, + call: Call, + meta: Arc, + next: F, + ) -> Either + where + F: Fn(Call, Arc) -> X + Send + Sync, + X: Future> + Send + 'static, + { + match call { + Call::MethodCall(req) => { + let fut = InternalRpcHandler + .handle_request(meta, req.method, req.params.into()) + .then(move |res| { + future::ready(Some(Output::from(res, req.id, req.jsonrpc))) + }); + Either::Left(Box::pin(fut)) + } + _ => Either::Left(Box::pin(next(call, meta))), + } + } + } + + impl Middleware> for ExternalRpcMiddleware { + type Future = NoopFuture; + type CallFuture = NoopCallFuture; + + fn on_call( + &self, + call: Call, + meta: Arc, + next: F, + ) -> Either + where + F: Fn(Call, Arc) -> X + Send + Sync, + X: Future> + Send + 'static, + { + match call { + Call::MethodCall(req) => { + let fut = ExternalRpcHandler + .handle_request(meta, req.method, req.params.into()) + .then(move |res| { + future::ready(Some(Output::from(res, req.id, req.jsonrpc))) + }); + Either::Left(Box::pin(fut)) + } + _ => Either::Left(Box::pin(next(call, meta))), + } + } + } +} diff --git a/node/src/processor.rs b/node/src/processor.rs index 272b50d96..3abf9d572 100644 --- a/node/src/processor.rs +++ b/node/src/processor.rs @@ -1,6 +1,6 @@ #![warn(missing_docs)] -//! Processor of rings-node jsonrpc-server. +//! Processor of rings-node rpc server. use std::str::FromStr; use std::sync::Arc; @@ -34,7 +34,7 @@ use crate::prelude::rings_core::swarm::MeasureImpl; use crate::prelude::rings_core::swarm::Swarm; use crate::prelude::rings_core::swarm::SwarmBuilder; use crate::prelude::rings_rpc::method; -use crate::prelude::rings_rpc::response; +use crate::prelude::rings_rpc::protos::rings_node::NodeInfoResponse; use crate::prelude::vnode; use crate::prelude::wasm_export; use crate::prelude::ChordStorageInterface; @@ -53,13 +53,13 @@ pub struct ProcessorConfig { /// [SessionSk]. session_sk: SessionSk, /// Stabilization timeout. - stabilize_timeout: usize, + stabilize_timeout: u64, } #[wasm_export] impl ProcessorConfig { /// Creates a new `ProcessorConfig` instance without an external address. - pub fn new(ice_servers: String, session_sk: SessionSk, stabilize_timeout: usize) -> Self { + pub fn new(ice_servers: String, session_sk: SessionSk, stabilize_timeout: u64) -> Self { Self { ice_servers, external_address: None, @@ -72,7 +72,7 @@ impl ProcessorConfig { pub fn new_with_ext_addr( ice_servers: String, session_sk: SessionSk, - stabilize_timeout: usize, + stabilize_timeout: u64, external_address: String, ) -> Self { Self { @@ -109,12 +109,12 @@ pub struct ProcessorConfigSerialized { /// A string representing the dumped `SessionSk`. session_sk: String, /// An unsigned integer representing the stabilization timeout. - stabilize_timeout: usize, + stabilize_timeout: u64, } impl ProcessorConfigSerialized { /// Creates a new `ProcessorConfigSerialized` instance without an external address. - pub fn new(ice_servers: String, session_sk: String, stabilize_timeout: usize) -> Self { + pub fn new(ice_servers: String, session_sk: String, stabilize_timeout: u64) -> Self { Self { ice_servers, external_address: None, @@ -127,7 +127,7 @@ impl ProcessorConfigSerialized { pub fn new_with_ext_addr( ice_servers: String, session_sk: String, - stabilize_timeout: usize, + stabilize_timeout: u64, external_address: String, ) -> Self { Self { @@ -198,10 +198,10 @@ pub struct ProcessorBuilder { session_sk: SessionSk, storage: Option, measure: Option, - stabilize_timeout: usize, + stabilize_timeout: u64, } -/// Processor for rings-node jsonrpc server +/// Processor for rings-node rpc server #[derive(Clone)] pub struct Processor { /// a swarm instance @@ -303,10 +303,10 @@ impl Processor { .call_method(method::Method::NodeDid.as_str(), jsonrpc_core::Params::None) .await .map_err(|e| Error::RemoteRpcError(e.to_string()))?; - let did = serde_json::from_value::(did_resp) - .map_err(|_| Error::InvalidDid)? + let did = serde_json::from_value::(did_resp.clone()) + .map_err(|_| Error::InvalidDid(did_resp.clone().to_string()))? .parse() - .map_err(|_| Error::InvalidDid)?; + .map_err(|_| Error::InvalidDid(did_resp.clone().to_string()))?; let (_, offer) = self .swarm @@ -351,8 +351,8 @@ impl Processor { /// 3. PeerC can connect PeerA with PeerA's web3 address. pub async fn connect_with_did(&self, did: Did, wait_for_open: bool) -> Result<()> { let conn = self.swarm.connect(did).await.map_err(Error::ConnectError)?; - tracing::debug!("wait for connection connected"); if wait_for_open { + tracing::debug!("wait for connection connected"); conn.webrtc_wait_for_data_channel_open() .await .map_err(|e| Error::ConnectError(rings_core::error::Error::Transport(e)))?; @@ -381,13 +381,12 @@ impl Processor { } /// Send custom message to a did. - pub async fn send_message(&self, destination: &str, msg: &[u8]) -> Result { + pub async fn send_message(&self, destination: Did, msg: &[u8]) -> Result { tracing::info!( "send_message, destination: {}, message size: {:?}", destination, msg.len(), ); - let destination = Did::from_str(destination).map_err(|_| Error::InvalidDid)?; let msg = Message::custom(msg).map_err(Error::SendMessage)?; @@ -404,8 +403,7 @@ impl Processor { backend_msg: BackendMessage, ) -> Result { let msg_bytes = bincode::serialize(&backend_msg).map_err(|_| Error::EncodeError)?; - self.send_message(&destination.to_string(), &msg_bytes) - .await + self.send_message(destination, &msg_bytes).await } /// check local cache of dht @@ -455,10 +453,10 @@ impl Processor { } /// get node info - pub async fn get_node_info(&self) -> Result { - Ok(response::NodeInfo { + pub async fn get_node_info(&self) -> Result { + Ok(NodeInfoResponse { version: crate::util::build_version(), - swarm: self.swarm.inspect().await, + swarm: Some(self.swarm.inspect().await.into()), }) } } @@ -522,8 +520,8 @@ mod test { p1.swarm.set_callback(callback1.clone()).unwrap(); p2.swarm.set_callback(callback2.clone()).unwrap(); - let did1 = p1.did().to_string(); - let did2 = p2.did().to_string(); + let did1 = p1.did(); + let did2 = p2.did(); println!("p1_did: {}", did1); println!("p2_did: {}", did2); @@ -544,11 +542,10 @@ mod test { let (conn2, answer) = p2.swarm.answer_offer(offer).await.unwrap(); let (peer_did, _) = p1.swarm.accept_answer(answer).await.unwrap(); - assert!( - peer_did.to_string().eq(&did2), + assert_eq!( + peer_did, did2, "peer.address got {}, expect: {}", - peer_did, - did2 + peer_did, did2 ); println!("waiting for connection"); @@ -580,19 +577,13 @@ mod test { let test_text2 = "test2"; println!("send_message 1"); - let uuid1 = p1 - .send_message(did2.as_str(), test_text1.as_bytes()) - .await - .unwrap(); + let uuid1 = p1.send_message(did2, test_text1.as_bytes()).await.unwrap(); println!("send_message 1 done, msg id: {}", uuid1); tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; println!("send_message 2"); - let uuid2 = p2 - .send_message(did1.as_str(), test_text2.as_bytes()) - .await - .unwrap(); + let uuid2 = p2.send_message(did1, test_text2.as_bytes()).await.unwrap(); println!("send_message 2 done, msg id: {}", uuid2); tokio::time::sleep(tokio::time::Duration::from_secs(3)).await; diff --git a/node/src/provider/browser/mod.rs b/node/src/provider/browser/mod.rs index 917e3fedb..43d02d92d 100644 --- a/node/src/provider/browser/mod.rs +++ b/node/src/provider/browser/mod.rs @@ -3,7 +3,6 @@ #![allow(non_snake_case, non_upper_case_globals)] //! rings-node browser support. pub mod provider; -pub mod utils; use std::str::FromStr; use wasm_bindgen; diff --git a/node/src/provider/browser/provider.rs b/node/src/provider/browser/provider.rs index a45269192..1a22a3663 100644 --- a/node/src/provider/browser/provider.rs +++ b/node/src/provider/browser/provider.rs @@ -26,7 +26,7 @@ use wasm_bindgen_futures; use wasm_bindgen_futures::future_to_promise; use wasm_bindgen_futures::JsFuture; -use crate::backend::browser::BackendContext; +use crate::backend::browser::BackendBehaviour; use crate::backend::types::BackendMessage; use crate::backend::types::HttpRequest; use crate::backend::types::ServiceMessage; @@ -81,11 +81,11 @@ impl Provider { #[wasm_bindgen(constructor)] pub fn new_instance( ice_servers: String, - stabilize_timeout: usize, + stabilize_timeout: u64, account: String, account_type: String, signer: js_sys::Function, - backend_context: Option, + backend_behaviour: Option, ) -> js_sys::Promise { fn wrapped_signer(signer: js_sys::Function) -> AsyncSigner { Box::new( @@ -118,7 +118,7 @@ impl Provider { Signer::Async(Box::new(signer)), ) .await?; - if let Some(cb) = backend_context { + if let Some(cb) = backend_behaviour { let backend: Backend = Backend::new(Arc::new(provider.clone()), Box::new(cb)); provider .set_swarm_callback(Arc::new(backend)) @@ -131,7 +131,7 @@ impl Provider { /// Create new provider instance with serialized config (yaml/json) pub fn new_provider_with_serialized_config( config: String, - backend: Option, + backend: Option, ) -> js_sys::Promise { let cfg: ProcessorConfig = serde_yaml::from_str(&config).unwrap(); Self::new_provider_with_config(cfg, backend) @@ -140,7 +140,7 @@ impl Provider { /// Create a new provider instance. pub fn new_provider_with_config( config: ProcessorConfig, - backend: Option, + backend: Option, ) -> js_sys::Promise { Self::new_provider_with_storage(config, backend, "rings-node".to_string()) } @@ -154,14 +154,14 @@ impl Provider { /// create new unsigned Provider pub fn new_provider_with_storage( config: ProcessorConfig, - backend_context: Option, + backend_behaviour: Option, storage_name: String, ) -> js_sys::Promise { future_to_promise(async move { let provider = Self::new_provider_with_storage_internal(config, storage_name) .await .map_err(JsError::from)?; - if let Some(cb) = backend_context { + if let Some(cb) = backend_behaviour { let backend: Backend = Backend::new(Arc::new(provider.clone()), Box::new(cb)); provider .set_swarm_callback(Arc::new(backend)) @@ -172,18 +172,13 @@ impl Provider { } /// Request local rpc interface - pub fn request( - &self, - method: String, - params: JsValue, - opt_id: Option, - ) -> js_sys::Promise { + pub fn request(&self, method: String, params: JsValue) -> js_sys::Promise { let ins = self.clone(); future_to_promise(async move { - let params = super::utils::parse_params(params) - .map_err(|e| JsError::new(e.to_string().as_str()))?; + let params = + js_value::json_value(params).map_err(|e| JsError::new(e.to_string().as_str()))?; let ret = ins - .request_internal(method, params, opt_id) + .request_internal(method, params) .await .map_err(JsError::from)?; Ok(js_value::serialize(&ret).map_err(JsError::from)?) @@ -303,7 +298,8 @@ impl Provider { pub fn send_message(&self, destination: String, msg: js_sys::Uint8Array) -> js_sys::Promise { let p = self.processor.clone(); future_to_promise(async move { - p.send_message(destination.as_str(), &msg.to_vec()) + let destination_did = get_did(destination.as_str(), AddressType::DEFAULT)?; + p.send_message(destination_did, &msg.to_vec()) .await .map_err(JsError::from)?; Ok(JsValue::from_bool(true)) diff --git a/node/src/provider/browser/utils.rs b/node/src/provider/browser/utils.rs deleted file mode 100644 index bc2b5ce32..000000000 --- a/node/src/provider/browser/utils.rs +++ /dev/null @@ -1,67 +0,0 @@ -#![warn(missing_docs)] -//! Utils for browser client -use js_sys; -use wasm_bindgen::prelude::*; - -use crate::error::Error; -use crate::prelude::jsonrpc_core; -use crate::prelude::rings_core::utils::js_value; - -/// A parser convert JsValue to jsonrpc_core::Params. -/// # Examples -/// -/// ``` -/// let nv = JsValue::null(); -/// match parse_params(nv) { -/// Ok(v) => { -/// assert!(v == jsonrpc_core::Params::None, "not null"); -/// } -/// Err(_) => { -/// panic!("Exception"); -/// } -/// } -/// -/// let arr_v = js_sys::Array::new(); -/// arr_v.push(&JsValue::from_str("test1")); -/// let jv: &JsValue = arr_v.as_ref(); -/// let value2 = browser::utils::parse_params(jv.clone()).unwrap(); -/// if let jsonrpc_core::Params::Array(v) = value2 { -/// assert!(v.len() == 1, "value2.len got {}, expect 1", v.len()); -/// let v0 = v.get(0).unwrap(); -/// assert!(v0.is_string(), "v0 not string"); -/// assert!(v0.as_str() == Some("test1"), "v0 value {:?}", v0.as_str()); -/// } else { -/// panic!("value2 not array"); -/// } -/// ``` -pub fn parse_params(params: JsValue) -> Result { - let params = if params.is_null() { - jsonrpc_core::Params::None - } else if js_sys::Array::is_array(¶ms) { - let arr = js_sys::Array::from(¶ms); - let v = arr - .iter() - .flat_map(|x| js_value::deserialize::(&x).ok()) - .collect::>(); - jsonrpc_core::Params::Array(v) - } else if params.is_object() { - let mut s_map = serde_json::Map::new(); - let obj = js_sys::Object::from(params); - let entries = js_sys::Object::entries(&obj); - for e in entries.iter() { - if js_sys::Array::is_array(&e) { - let arr = js_sys::Array::from(&e); - if arr.length() != 2 { - continue; - } - let k = arr.get(0); - let v = arr.get(1); - s_map.insert(k.as_string().unwrap(), js_value::deserialize(&v).unwrap()); - } - } - jsonrpc_core::Params::Map(s_map) - } else { - return Err(Error::JsError("unsupported params".to_owned())); - }; - Ok(params) -} diff --git a/node/src/provider/ffi.rs b/node/src/provider/ffi.rs index 18609bfac..3c9d7d908 100644 --- a/node/src/provider/ffi.rs +++ b/node/src/provider/ffi.rs @@ -99,6 +99,7 @@ use std::ffi::CString; use std::sync::Arc; use futures::executor; +use rings_rpc::protos::rings_node_handler::InternalRpcHandler; use super::Provider; use super::Signer; @@ -106,7 +107,6 @@ use crate::backend::ffi::FFIBackendBehaviour; use crate::backend::Backend; use crate::error::Error; use crate::error::Result; -use crate::jsonrpc::HandlerType; use crate::processor::Processor; /// A structure to represent the Provider in a C-compatible format. @@ -114,7 +114,7 @@ use crate::processor::Processor; #[repr(C)] pub struct ProviderPtr { processor: *const Processor, - handler: *const HandlerType, + handler: *const InternalRpcHandler, } impl Provider { @@ -139,16 +139,9 @@ impl Provider { let threshold = 5; let p_count = Arc::strong_count(&self.processor); - let h_count = Arc::strong_count(&self.handler); - tracing::debug!("processor arc: {:?} handler arc {:?}", p_count, h_count); - if h_count < threshold { - for _ in 0..threshold - h_count { - unsafe { self.increase_handler_count() }; - } - tracing::debug!("Arc will be released when out of scope, increased") - } + tracing::debug!("processor arc: {:?}", p_count); if p_count < threshold { - for _ in 0..threshold - h_count { + for _ in 0..threshold - p_count { unsafe { self.increase_processor_count() }; } tracing::debug!("Arc will be released when out of scope, increased") @@ -156,19 +149,10 @@ impl Provider { } unsafe fn increase_processor_count(&self) { - tracing::debug!("Increment strong count on processor and handler"); + tracing::debug!("Increment strong count on processor"); let p = Arc::into_raw(self.processor.clone()); Arc::increment_strong_count(p); } - - /// Decreases the reference count for the associated Arcs. - /// # Safety - /// This function unsafely decrements the reference count of the Arcs. - unsafe fn increase_handler_count(&self) { - tracing::debug!("Decrement strong count on processor and handler"); - let h = Arc::into_raw(self.handler.clone()); - Arc::increment_strong_count(h); - } } impl From<&ProviderPtr> for Provider { @@ -179,7 +163,7 @@ impl From<&ProviderPtr> for Provider { fn from(ptr: &ProviderPtr) -> Provider { tracing::debug!("FFI: Provider from Ptr!"); let processor = unsafe { Arc::::from_raw(ptr.processor as *const Processor) }; - let handler = unsafe { Arc::::from_raw(ptr.handler as *const HandlerType) }; + let handler = unsafe { *ptr.handler }; Self { processor, handler } } } @@ -191,7 +175,7 @@ impl From<&Provider> for ProviderPtr { // Clone the Arcs, which increases the ref count, // then turn them into raw pointers. let processor_ptr = Arc::into_raw(provider.processor.clone()); - let handler_ptr = Arc::into_raw(provider.handler.clone()); + let handler_ptr = &provider.handler as *const InternalRpcHandler; provider.check_arc(); ProviderPtr { processor: processor_ptr, @@ -238,16 +222,11 @@ pub extern "C" fn request( let method = c_char_to_string(method)?; let params = c_char_to_string(params)?; let params = serde_json::from_str(¶ms) - .expect(&format!("Failed on covering data {:?} to JSON", params)); + .unwrap_or_else(|_| panic!("Failed on covering data {:?} to JSON", params)); let handle = std::thread::spawn(move || { let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(async { - provider - .request_internal(method, params, None) - .await - .unwrap() - }) + rt.block_on(async { provider.request_internal(method, params).await.unwrap() }) }); let ret: String = serde_json::to_string(&handle.join().unwrap())?; let c_ret = CString::new(ret)?.into_raw(); @@ -268,7 +247,7 @@ pub extern "C" fn request( #[no_mangle] pub unsafe extern "C" fn new_provider_with_callback( ice_server: *const c_char, - stabilize_timeout: u32, + stabilize_timeout: u64, account: *const c_char, account_type: *const c_char, signer: extern "C" fn(*const c_char, *mut c_char) -> (), @@ -301,7 +280,7 @@ pub unsafe extern "C" fn new_provider_with_callback( executor::block_on(Provider::new_provider_internal( ice, - stabilize_timeout as usize, + stabilize_timeout, acc, acc_ty, Signer::Sync(Box::new(wrapped_signer(signer))), diff --git a/node/src/provider/mod.rs b/node/src/provider/mod.rs index b1e2339ec..6409bd1a6 100644 --- a/node/src/provider/mod.rs +++ b/node/src/provider/mod.rs @@ -8,16 +8,11 @@ use std::sync::Arc; use rings_core::session::SessionSkBuilder; use rings_core::storage::PersistenceStorage; use rings_core::swarm::callback::SharedSwarmCallback; +use rings_rpc::protos::rings_node_handler::InternalRpcHandler; use crate::error::Error; use crate::error::Result; -use crate::jsonrpc::handler::MethodHandler; -use crate::jsonrpc::HandlerType; use crate::measure::PeriodicMeasure; -use crate::prelude::jsonrpc_core::types::id::Id; -use crate::prelude::jsonrpc_core::MethodCall; -use crate::prelude::jsonrpc_core::Output; -use crate::prelude::jsonrpc_core::Params; use crate::prelude::wasm_export; use crate::processor::Processor; use crate::processor::ProcessorBuilder; @@ -37,7 +32,7 @@ pub mod ffi; #[wasm_export] pub struct Provider { processor: Arc, - handler: Arc, + handler: InternalRpcHandler, } /// Async signer, without Send required @@ -61,11 +56,9 @@ pub enum Signer { impl Provider { /// Create provider from processor directly pub fn from_processor(processor: Arc) -> Self { - let mut handler: HandlerType = processor.clone().into(); - handler.build(); Self { processor, - handler: handler.into(), + handler: InternalRpcHandler, } } /// Create a provider instance with storage name @@ -91,12 +84,9 @@ impl Provider { let processor = Arc::new(processor_builder.build()?); - let mut handler: HandlerType = processor.clone().into(); - handler.build(); - Ok(Provider { processor, - handler: handler.into(), + handler: InternalRpcHandler, }) } @@ -120,7 +110,7 @@ impl Provider { /// Signer should function as same as account_type declared, Eg: eip191 or secp256k1 or ed25519. pub(crate) async fn new_provider_internal( ice_servers: String, - stabilize_timeout: usize, + stabilize_timeout: u64, account: String, account_type: String, signer: Signer, @@ -149,24 +139,11 @@ impl Provider { pub async fn request_internal( &self, method: String, - params: Params, - opt_id: Option, - ) -> Result { - let handler = self.handler.clone(); - let id = if let Some(id) = opt_id { - Id::Str(id) - } else { - Id::Null - }; - let req: MethodCall = MethodCall { - jsonrpc: None, - method, - params, - id, - }; - tracing::debug!("request {:?}", req); - handler - .handle_request(req) + params: serde_json::Value, + ) -> Result { + tracing::debug!("request {} params: {:?}", method, params); + self.handler + .handle_request(self.processor.clone(), method, params) .await .map_err(Error::InternalRpcError) } @@ -175,12 +152,15 @@ impl Provider { #[cfg(feature = "node")] impl Provider { /// A request function implementation for native provider - pub async fn request( + pub async fn request( &self, - method: String, - params: Params, - opt_id: Option, - ) -> Result { - self.request_internal(method, params, opt_id).await + method: rings_rpc::method::Method, + params: T, + ) -> Result + where + T: serde::Serialize, + { + let params = serde_json::to_value(params)?; + self.request_internal(method.to_string(), params).await } } diff --git a/node/src/rpc_impl.rs b/node/src/rpc_impl.rs new file mode 100644 index 000000000..02a7673e1 --- /dev/null +++ b/node/src/rpc_impl.rs @@ -0,0 +1,319 @@ +#![warn(missing_docs)] +//! JSON-RPC handler for both feature=browser and feature=node. +//! We support running the JSON-RPC server in either native or browser environment. +//! For the native environment, we use jsonrpc_core to handle requests. +//! For the browser environment, we utilize a Simple MessageHandler to process the requests. +use std::collections::HashSet; +use std::str::FromStr; + +use async_trait::async_trait; +use futures::future::join_all; +use rings_core::swarm::impls::ConnectionHandshake; +use rings_rpc::protos::rings_node::*; +use rings_rpc::protos::rings_node_handler::HandleRpc; +use rings_transport::core::transport::ConnectionInterface; + +use crate::error::Error as ServerError; +use crate::prelude::jsonrpc_core::types::error::Error; +use crate::prelude::jsonrpc_core::types::error::ErrorCode; +use crate::prelude::jsonrpc_core::Result; +use crate::prelude::rings_core::dht::Did; +use crate::prelude::rings_core::message::Decoder; +use crate::prelude::rings_core::message::Encoded; +use crate::prelude::rings_core::message::Encoder; +use crate::prelude::rings_core::message::MessagePayload; +use crate::prelude::rings_core::prelude::vnode::VirtualNode; +use crate::processor::Processor; +use crate::seed::Seed; + +#[cfg_attr(feature = "browser", async_trait(?Send))] +#[cfg_attr(not(feature = "browser"), async_trait)] +impl HandleRpc for Processor { + async fn handle_rpc( + &self, + req: ConnectPeerViaHttpRequest, + ) -> Result { + let did = self + .connect_peer_via_http(&req.url) + .await + .map_err(Error::from)?; + + Ok(ConnectPeerViaHttpResponse { + did: did.to_string(), + }) + } +} + +#[cfg_attr(feature = "browser", async_trait(?Send))] +#[cfg_attr(not(feature = "browser"), async_trait)] +impl HandleRpc for Processor { + async fn handle_rpc(&self, req: ConnectWithDidRequest) -> Result { + let did = s2d(&req.did)?; + self.connect_with_did(did, true) + .await + .map_err(Error::from)?; + + Ok(ConnectWithDidResponse {}) + } +} + +#[cfg_attr(feature = "browser", async_trait(?Send))] +#[cfg_attr(not(feature = "browser"), async_trait)] +impl HandleRpc for Processor { + async fn handle_rpc(&self, req: ConnectWithSeedRequest) -> Result { + let seed: Seed = Seed::try_from(req)?; + + let mut connected: HashSet = HashSet::from_iter(self.swarm.get_connection_ids()); + connected.insert(self.swarm.did()); + + let tasks = seed + .peers + .iter() + .filter(|&x| !connected.contains(&x.did)) + .map(|x| self.connect_peer_via_http(&x.url)); + + let results = join_all(tasks).await; + + let first_err = results.into_iter().find(|x| x.is_err()); + if let Some(err) = first_err { + err.map_err(Error::from)?; + } + + Ok(ConnectWithSeedResponse {}) + } +} + +#[cfg_attr(feature = "browser", async_trait(?Send))] +#[cfg_attr(not(feature = "browser"), async_trait)] +impl HandleRpc for Processor { + async fn handle_rpc(&self, _req: ListPeersRequest) -> Result { + let peers = self.swarm.get_connections().into_iter().map(dc2p).collect(); + Ok(ListPeersResponse { peers }) + } +} + +#[cfg_attr(feature = "browser", async_trait(?Send))] +#[cfg_attr(not(feature = "browser"), async_trait)] +impl HandleRpc for Processor { + async fn handle_rpc(&self, req: CreateOfferRequest) -> Result { + let did = s2d(&req.did)?; + let (_, offer_payload) = self + .swarm + .create_offer(did) + .await + .map_err(ServerError::CreateOffer) + .map_err(Error::from)?; + + let encoded = offer_payload + .encode() + .map_err(|_| ServerError::EncodeError)?; + + Ok(CreateOfferResponse { + offer: encoded.to_string(), + }) + } +} + +#[cfg_attr(feature = "browser", async_trait(?Send))] +#[cfg_attr(not(feature = "browser"), async_trait)] +impl HandleRpc for Processor { + async fn handle_rpc(&self, req: AnswerOfferRequest) -> Result { + if req.offer.is_empty() { + return Err(Error::invalid_params("Offer is empty")); + } + let encoded: Encoded = >::from(req.offer); + + let offer_payload = + MessagePayload::from_encoded(&encoded).map_err(|_| ServerError::DecodeError)?; + + let (_, answer_payload) = self + .swarm + .answer_offer(offer_payload) + .await + .map_err(ServerError::AnswerOffer) + .map_err(Error::from)?; + + tracing::debug!("connect_peer_via_ice response: {:?}", answer_payload); + let encoded = answer_payload + .encode() + .map_err(|_| ServerError::EncodeError)?; + + Ok(AnswerOfferResponse { + answer: encoded.to_string(), + }) + } +} + +#[cfg_attr(feature = "browser", async_trait(?Send))] +#[cfg_attr(not(feature = "browser"), async_trait)] +impl HandleRpc for Processor { + async fn handle_rpc(&self, req: AcceptAnswerRequest) -> Result { + if req.answer.is_empty() { + return Err(Error::invalid_params("Answer is empty")); + } + let encoded: Encoded = >::from(req.answer); + + let answer_payload = + MessagePayload::from_encoded(&encoded).map_err(|_| ServerError::DecodeError)?; + + let dc = self + .swarm + .accept_answer(answer_payload) + .await + .map_err(ServerError::AcceptAnswer)?; + + Ok(AcceptAnswerResponse { + peer: Some(dc2p(dc)), + }) + } +} + +#[cfg_attr(feature = "browser", async_trait(?Send))] +#[cfg_attr(not(feature = "browser"), async_trait)] +impl HandleRpc for Processor { + async fn handle_rpc(&self, req: DisconnectRequest) -> Result { + let did = s2d(&req.did)?; + self.disconnect(did).await?; + Ok(DisconnectResponse {}) + } +} + +#[cfg_attr(feature = "browser", async_trait(?Send))] +#[cfg_attr(not(feature = "browser"), async_trait)] +impl HandleRpc for Processor { + async fn handle_rpc(&self, req: SendCustomMessageRequest) -> Result { + let destination = s2d(&req.destination_did)?; + let data = base64::decode(req.data) + .map_err(|_| Error::invalid_params("Base64 decode data failed"))?; + self.send_message(destination, &data).await?; + Ok(SendCustomMessageResponse {}) + } +} + +#[cfg_attr(feature = "browser", async_trait(?Send))] +#[cfg_attr(not(feature = "browser"), async_trait)] +impl HandleRpc for Processor { + async fn handle_rpc( + &self, + req: SendBackendMessageRequest, + ) -> Result { + let destination = s2d(&req.destination_did)?; + let data = serde_json::from_str(&req.data) + .map_err(|_| Error::invalid_params("Serialize data as json failed"))?; + self.send_backend_message(destination, data).await?; + Ok(SendBackendMessageResponse {}) + } +} + +#[cfg_attr(feature = "browser", async_trait(?Send))] +#[cfg_attr(not(feature = "browser"), async_trait)] +impl HandleRpc for Processor { + async fn handle_rpc( + &self, + req: PublishMessageToTopicRequest, + ) -> Result { + let encoded = req + .data + .encode() + .map_err(|e| Error::invalid_params(format!("Failed to encode data: {e:?}")))?; + self.storage_append_data(&req.topic, encoded).await?; + Ok(PublishMessageToTopicResponse {}) + } +} + +#[cfg_attr(feature = "browser", async_trait(?Send))] +#[cfg_attr(not(feature = "browser"), async_trait)] +impl HandleRpc for Processor { + async fn handle_rpc( + &self, + req: FetchMessagesOfTopicRequest, + ) -> Result { + let vid = VirtualNode::gen_did(&req.topic) + .map_err(|_| Error::invalid_params("Failed to get id of topic"))?; + + self.storage_fetch(vid).await?; + let result = self.storage_check_cache(vid).await; + + let Some(vnode) = result else { + return Ok(FetchMessagesOfTopicResponse { data: vec![] }); + }; + + let data = vnode + .data + .iter() + .skip(req.skip as usize) + .map(|v| v.decode()) + .filter_map(|v| v.ok()) + .collect::>(); + + Ok(FetchMessagesOfTopicResponse { data }) + } +} + +#[cfg_attr(feature = "browser", async_trait(?Send))] +#[cfg_attr(not(feature = "browser"), async_trait)] +impl HandleRpc for Processor { + async fn handle_rpc(&self, req: RegisterServiceRequest) -> Result { + self.register_service(&req.name).await?; + Ok(RegisterServiceResponse {}) + } +} + +#[cfg_attr(feature = "browser", async_trait(?Send))] +#[cfg_attr(not(feature = "browser"), async_trait)] +impl HandleRpc for Processor { + async fn handle_rpc(&self, req: LookupServiceRequest) -> Result { + let vid = VirtualNode::gen_did(&req.name) + .map_err(|_| Error::invalid_params("Failed to get id of topic"))?; + + self.storage_fetch(vid).await?; + let result = self.storage_check_cache(vid).await; + + let Some(vnode) = result else { + return Ok(LookupServiceResponse { dids: vec![] }); + }; + + let dids = vnode + .data + .iter() + .map(|v| v.decode()) + .filter_map(|v| v.ok()) + .collect::>(); + + Ok(LookupServiceResponse { dids }) + } +} + +#[cfg_attr(feature = "browser", async_trait(?Send))] +#[cfg_attr(not(feature = "browser"), async_trait)] +impl HandleRpc for Processor { + async fn handle_rpc(&self, _req: NodeInfoRequest) -> Result { + self.get_node_info() + .await + .map_err(|_| Error::new(ErrorCode::InternalError)) + } +} + +#[cfg_attr(feature = "browser", async_trait(?Send))] +#[cfg_attr(not(feature = "browser"), async_trait)] +impl HandleRpc for Processor { + async fn handle_rpc(&self, _req: NodeDidRequest) -> Result { + let did = self.did(); + Ok(NodeDidResponse { + did: did.to_string(), + }) + } +} + +/// Convert did and connection to Peer +fn dc2p((did, conn): (Did, impl ConnectionInterface)) -> PeerInfo { + PeerInfo { + did: did.to_string(), + state: format!("{:?}", conn.webrtc_connection_state()), + } +} + +/// Get did from string or return InvalidParam Error +fn s2d(s: &str) -> Result { + Did::from_str(s).map_err(|_| Error::invalid_params(format!("Invalid Did: {s}"))) +} diff --git a/node/src/seed.rs b/node/src/seed.rs index 25f1a0ca2..f69bcd848 100644 --- a/node/src/seed.rs +++ b/node/src/seed.rs @@ -1,8 +1,12 @@ //! Seed and SeedLoader use for getting peers from endpoint. +use std::str::FromStr; + use serde::Deserialize; use serde::Serialize; +use crate::error::Error; use crate::prelude::rings_core::dht::Did; +use crate::prelude::rings_rpc::protos::rings_node::ConnectWithSeedRequest; /// A list contains SeedPeer. #[derive(Deserialize, Serialize, Debug)] @@ -16,5 +20,20 @@ pub struct SeedPeer { /// an unique identify. pub did: Did, /// remote client endpoint - pub endpoint: String, + pub url: String, +} + +impl TryFrom for Seed { + type Error = Error; + + fn try_from(req: ConnectWithSeedRequest) -> Result { + let mut peers = Vec::new(); + + for peer in req.peers { + let did = Did::from_str(&peer.did).map_err(|_| Error::InvalidDid(peer.did.clone()))?; + peers.push(SeedPeer { did, url: peer.url }); + } + + Ok(Seed { peers }) + } } diff --git a/node/src/tests/wasm/browser.rs b/node/src/tests/wasm/browser.rs index 104ebf3ba..dc793a9a2 100644 --- a/node/src/tests/wasm/browser.rs +++ b/node/src/tests/wasm/browser.rs @@ -1,9 +1,7 @@ -use wasm_bindgen::JsValue; +use rings_rpc::protos::rings_node::*; use wasm_bindgen_futures::JsFuture; use wasm_bindgen_test::*; -use crate::prelude::jsonrpc_core::types::response::Output; -use crate::prelude::jsonrpc_core::types::Value; use crate::prelude::rings_core::prelude::uuid; use crate::prelude::rings_core::utils; use crate::prelude::rings_core::utils::js_value; @@ -47,55 +45,39 @@ async fn get_peers(provider: &Provider) -> Vec { } async fn create_connection(provider1: &Provider, provider2: &Provider) { - futures::try_join!( - JsFuture::from(provider1.listen()), - JsFuture::from(provider2.listen()), - ) + let req0 = CreateOfferRequest { + did: provider2.address(), + }; + let resp0 = JsFuture::from(provider1.request( + "createOffer".to_string(), + js_value::serialize(&req0).unwrap(), + )) + .await .unwrap(); - let address = JsValue::from_str(&provider2.address()); - let req0 = js_sys::Array::of1(&address); - let offer_fut = JsFuture::from(provider1.request("createOffer".to_string(), req0.into(), None)) - .await - .unwrap(); - - let offer: String = - if let Output::Success(ret) = js_value::deserialize::(&offer_fut).unwrap() { - if let Value::String(o) = ret.result { - o - } else { - panic!("failed to get offer from output result {:?}", ret); - } - } else { - panic!("request failed at create offer"); - }; + let offer = js_value::deserialize::(resp0) + .unwrap() + .offer; - let js_offer = JsValue::from_str(&offer); - let req1 = js_sys::Array::of1(&js_offer); - let answer_fut = - JsFuture::from(provider2.request("answerOffer".to_string(), req1.into(), None)) - .await - .unwrap(); - - let answer: String = match js_value::deserialize::(&answer_fut).unwrap() { - Output::Success(ret) => { - if let Value::String(o) = ret.result { - o - } else { - panic!("failed to get answer from output result {:?}", ret); - } - } - Output::Failure(e) => { - panic!("request failed at accept offer, {:?}", e); - } - }; + let req1 = AnswerOfferRequest { offer }; + let resp1 = JsFuture::from(provider2.request( + "answerOffer".to_string(), + js_value::serialize(&req1).unwrap(), + )) + .await + .unwrap(); - let js_answer = JsValue::from_str(&answer); - let req2 = js_sys::Array::of1(&js_answer); + let answer = js_value::deserialize::(resp1) + .unwrap() + .answer; - let _ret = JsFuture::from(provider1.request("acceptAnswer".to_string(), req2.into(), None)) - .await - .unwrap(); + let req2 = AcceptAnswerRequest { answer }; + let _resp2 = JsFuture::from(provider1.request( + "acceptAnswer".to_string(), + js_value::serialize(&req2).unwrap(), + )) + .await + .unwrap(); } #[wasm_bindgen_test] @@ -138,30 +120,6 @@ async fn test_two_provider_connect_and_list() { assert_eq!(peers.len(), 0); } -#[wasm_bindgen_test] -async fn test_provider_parse_params() { - let null_value = browser::utils::parse_params(JsValue::null()); - assert!(null_value.is_ok(), "null_value is error"); - match null_value { - Ok(v) => assert!(v == jsonrpc_core::Params::None, "not null"), - Err(_) => panic!("err"), - } - - let arr_v = js_sys::Array::new(); - arr_v.push(&JsValue::from_str("test1")); - - let jv: &JsValue = arr_v.as_ref(); - let value2 = browser::utils::parse_params(jv.clone()).unwrap(); - if let jsonrpc_core::Params::Array(v) = value2 { - assert!(v.len() == 1, "value2.len got {}, expect 1", v.len()); - let v0 = v.get(0).unwrap(); - assert!(v0.is_string(), "v0 not string"); - assert!(v0.as_str() == Some("test1"), "v0 value {:?}", v0.as_str()); - } else { - panic!("value2 not array"); - } -} - #[wasm_bindgen_test] async fn test_get_address_from_hex_pubkey() { let pk = "02c0eeef8d136b10b862a0ac979eac2ad036f9902d87963ddf0fa108f1e275b9c7"; @@ -202,60 +160,3 @@ async fn test_get_address() { expect_address ) } - -#[wasm_bindgen_test] -async fn test_create_connection_via_local_rpc() { - // super::setup_log(); - let (provider1, _storage1) = new_provider().await; - let (provider2, _storage2) = new_provider().await; - - futures::try_join!( - JsFuture::from(provider1.listen()), - JsFuture::from(provider2.listen()), - ) - .unwrap(); - - let address = JsValue::from_str(&provider2.address()); - let req0 = js_sys::Array::of1(&address); - let offer_fut = JsFuture::from(provider1.request("createOffer".to_string(), req0.into(), None)) - .await - .unwrap(); - - let offer: String = - if let Output::Success(ret) = js_value::deserialize::(&offer_fut).unwrap() { - if let Value::String(o) = ret.result { - o - } else { - panic!("failed to get offer from output result {:?}", ret); - } - } else { - panic!("request failed at create offer"); - }; - - let js_offer = JsValue::from_str(&offer); - let req1 = js_sys::Array::of1(&js_offer); - let answer_fut = - JsFuture::from(provider2.request("answerOffer".to_string(), req1.into(), None)) - .await - .unwrap(); - - let answer: String = match js_value::deserialize::(&answer_fut).unwrap() { - Output::Success(ret) => { - if let Value::String(o) = ret.result { - o - } else { - panic!("failed to get answer from output result {:?}", ret); - } - } - Output::Failure(e) => { - panic!("request failed at accept offer, {:?}", e); - } - }; - - let js_answer = JsValue::from_str(&answer); - let req2 = js_sys::Array::of1(&js_answer); - - let _ret = JsFuture::from(provider1.request("acceptAnswer".to_string(), req2.into(), None)) - .await - .unwrap(); -} diff --git a/node/src/tests/wasm/processor.rs b/node/src/tests/wasm/processor.rs index 550d00cac..cf100b1bc 100644 --- a/node/src/tests/wasm/processor.rs +++ b/node/src/tests/wasm/processor.rs @@ -117,10 +117,10 @@ async fn test_processor_handshake_and_msg() { let test_text4 = "test4"; let test_text5 = "test5"; - let p1_addr = p1.did().to_string(); - let p2_addr = p2.did().to_string(); - console_log!("p1_addr: {}", p1_addr); - console_log!("p2_addr: {}", p2_addr); + let p1_did = p1.did(); + let p2_did = p2.did(); + console_log!("p1_did: {}", p1_did); + console_log!("p2_did: {}", p2_did); console_log!("listen"); listen(&p1).await; @@ -134,27 +134,27 @@ async fn test_processor_handshake_and_msg() { .unwrap(); console_log!("processor_send_test_text_messages"); - p1.send_message(p2_addr.as_str(), test_text1.as_bytes()) + p1.send_message(p2_did, test_text1.as_bytes()) .await .unwrap(); console_log!("send test_text1 done"); - p2.send_message(p1_addr.as_str(), test_text2.as_bytes()) + p2.send_message(p1_did, test_text2.as_bytes()) .await .unwrap(); console_log!("send test_text2 done"); - p2.send_message(p1_addr.as_str(), test_text3.as_bytes()) + p2.send_message(p1_did, test_text3.as_bytes()) .await .unwrap(); console_log!("send test_text3 done"); - p1.send_message(p2_addr.as_str(), test_text4.as_bytes()) + p1.send_message(p2_did, test_text4.as_bytes()) .await .unwrap(); console_log!("send test_text4 done"); - p2.send_message(p1_addr.as_str(), test_text5.as_bytes()) + p2.send_message(p1_did, test_text5.as_bytes()) .await .unwrap(); console_log!("send test_text5 done"); diff --git a/package.json b/package.json index 778435f46..b4ecc3ac5 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "RND " ], "description": "Rings is a structured peer-to-peer network implementation using WebRTC, Chord algorithm, and full WebAssembly (WASM) support.\n", - "version": "0.3.4", + "version": "0.5.0", "license": "GPL-3.0", "repository": { "type": "git", @@ -12,17 +12,23 @@ }, "scripts": { "wasm_pack": "wasm-pack build node --scope ringsnetwork -t web --no-default-features --features browser --features console_error_panic_hook", - "prepare": "npm run wasm_pack && cp node/pkg/rings_node* ./" + "proto_generate": "mkdir -p dist && pbjs -t static-module -o dist/rings_node_proto.js rpc/src/protos/rings_node.proto && pbts -o dist/rings_node_proto.d.ts dist/rings_node_proto.js", + "prepare": "npm run wasm_pack && npm run proto_generate && cp node/pkg/rings_node* ./dist && tsc" + }, + "dependencies": { + "protobufjs": "^7.2.5" + }, + "devDependencies": { + "protobufjs-cli": "^1.1.2", + "typescript": "^4.9.5" }, "files": [ - "rings_node_bg.wasm", - "rings_node.js", - "rings_node.d.ts" + "dist/" ], - "module": "rings_node.js", - "types": "rings_node.d.ts", + "module": "dist/index.js", + "types": "dist/index.d.ts", "type": "module", - "main": "rings_node.js", + "main": "dist/index.js", "sideEffects": false, "keywords": [ "Chord", diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 16cdba03c..90431c591 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -17,23 +17,24 @@ crate-type = ["cdylib", "rlib"] [features] default = ["std"] -std = [ - "rings-core/default", - "reqwest", -] -wasm = [ - "rings-core/wasm", - "reqwest-wasm", -] +std = ["rings-core/default", "reqwest"] +wasm = ["rings-core/wasm", "reqwest-wasm"] [dependencies] +async-trait = "0.1.73" base64 = { version = "0.13.0" } +bytes = "1.5.0" http = "0.2.6" jsonrpc-core = { version = "18.0.0" } jsonrpc-pubsub = { version = "18.0.0" } +prost = "0.12.3" reqwest = { version = "0.11", features = ["json", "rustls-tls"], optional = true, default-features = false } reqwest-wasm = { version = "0.11", features = ["json", "rustls-tls"], optional = true, default-features = false } rings-core = { workspace = true, optional = true } serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.70" thiserror = "1" + +[build-dependencies] +prost-build-config = "0.5.0" +serde_yaml = "0.9.27" diff --git a/rpc/build.rs b/rpc/build.rs new file mode 100644 index 000000000..0487d570a --- /dev/null +++ b/rpc/build.rs @@ -0,0 +1,19 @@ +use std::process::Command; + +use prost_build_config::BuildConfig; +use prost_build_config::Builder; + +fn main() { + let config_content = include_str!("src/protos/build_config.yaml"); + let config: BuildConfig = serde_yaml::from_str(config_content).unwrap(); + Builder::from(config).build_protos(); + + Command::new("cargo") + .args(["+nightly", "fmt"]) + .output() + .unwrap(); + + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=src/protos/rings_node.proto"); + println!("cargo:rerun-if-changed=src/protos/build_config.yaml"); +} diff --git a/rpc/src/client.rs b/rpc/src/client.rs index b8919e97f..11fbd6f12 100644 --- a/rpc/src/client.rs +++ b/rpc/src/client.rs @@ -96,23 +96,6 @@ impl Client { Ok(()) } - /// Sends a message to the specified peer. - pub async fn send_message( - &self, - did: &str, - text: &str, - ) -> Result { - let mut params = serde_json::Map::new(); - params.insert("destination".to_owned(), json!(did)); - params.insert("text".to_owned(), json!(text)); - let result = self - .client - .call_method(Method::SendTo.as_str(), Params::Map(params)) - .await - .map_err(Error::RpcError)?; - serde_json::from_value(result).map_err(|_| Error::DecodeError) - } - /// Sends a custom message to the specified peer. pub async fn send_custom_message( &self, diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 9be4694dd..aeeb5cd51 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -4,5 +4,6 @@ pub mod error; pub mod jsonrpc_client; pub mod method; pub mod prelude; +pub mod protos; pub mod response; pub mod types; diff --git a/rpc/src/method.rs b/rpc/src/method.rs index 45437c4e9..fef35a68f 100644 --- a/rpc/src/method.rs +++ b/rpc/src/method.rs @@ -1,10 +1,12 @@ //! A JSONRPC `method` enum. #![warn(missing_docs)] + use super::error::Error; use super::error::Result; /// supported methods. #[derive(Debug, Clone)] +#[non_exhaustive] pub enum Method { /// Connect peer with remote jsonrpc server url ConnectPeerViaHttp, @@ -20,8 +22,6 @@ pub enum Method { AnswerOffer, /// Accept Answer for manually handshake AcceptAnswer, - /// Send custom message to peer - SendTo, /// Disconnect a peer Disconnect, /// SendCustomMessage, @@ -52,7 +52,6 @@ impl Method { Method::ListPeers => "listPeers", Method::CreateOffer => "createOffer", Method::AnswerOffer => "answerOffer", - Method::SendTo => "sendTo", Method::Disconnect => "disconnect", Method::AcceptAnswer => "acceptAnswer", Method::SendCustomMessage => "sendCustomMessage", @@ -84,7 +83,6 @@ impl TryFrom<&str> for Method { "listPeers" => Self::ListPeers, "createOffer" => Self::CreateOffer, "answerOffer" => Self::AnswerOffer, - "sendTo" => Self::SendTo, "disconnect" => Self::Disconnect, "acceptAnswer" => Self::AcceptAnswer, "sendBackendMessage" => Self::SendBackendMessage, diff --git a/rpc/src/protos/build_config.yaml b/rpc/src/protos/build_config.yaml new file mode 100644 index 000000000..bcf48908f --- /dev/null +++ b/rpc/src/protos/build_config.yaml @@ -0,0 +1,51 @@ +--- +includes: + - src/protos +files: + - src/protos/rings_node.proto +output: src/protos +messages: + - attrs: + - derive(serde::Serialize, serde::Deserialize) + paths: + - rings_node.ConnectPeerViaHttpRequest + - rings_node.ConnectPeerViaHttpResponse + - rings_node.ConnectWithDidRequest + - rings_node.ConnectWithDidResponse + - rings_node.SeedPeer + - rings_node.Seed + - rings_node.ConnectWithSeedRequest + - rings_node.ConnectWithSeedResponse + - rings_node.PeerInfo + - rings_node.ListPeersRequest + - rings_node.ListPeersResponse + - rings_node.CreateOfferRequest + - rings_node.CreateOfferResponse + - rings_node.AnswerOfferRequest + - rings_node.AnswerOfferResponse + - rings_node.AcceptAnswerRequest + - rings_node.AcceptAnswerResponse + - rings_node.DisconnectRequest + - rings_node.DisconnectResponse + - rings_node.SendCustomMessageRequest + - rings_node.SendCustomMessageResponse + - rings_node.SendBackendMessageRequest + - rings_node.SendBackendMessageResponse + - rings_node.PublishMessageToTopicRequest + - rings_node.PublishMessageToTopicResponse + - rings_node.FetchMessagesOfTopicRequest + - rings_node.FetchMessagesOfTopicResponse + - rings_node.RegisterServiceRequest + - rings_node.RegisterServiceResponse + - rings_node.LookupServiceRequest + - rings_node.LookupServiceResponse + - rings_node.NodeInfoRequest + - rings_node.FingerTableRange + - rings_node.DhtInfo + - rings_node.StorageValue + - rings_node.StorageItem + - rings_node.StorageInfo + - rings_node.SwarmInfo + - rings_node.NodeInfoResponse + - rings_node.NodeDidRequest + - rings_node.NodeDidResponse diff --git a/rpc/src/protos/mod.rs b/rpc/src/protos/mod.rs new file mode 100644 index 000000000..124065e07 --- /dev/null +++ b/rpc/src/protos/mod.rs @@ -0,0 +1,56 @@ +pub mod rings_node; +pub mod rings_node_handler; + +use rings_core::inspect::StorageInspect; +use rings_core::inspect::SwarmInspect; + +impl From for rings_node::SwarmInfo { + fn from(inspect: SwarmInspect) -> Self { + let peers = inspect + .connections + .into_iter() + .map(|conn| rings_node::PeerInfo { + did: conn.did, + state: conn.state, + }) + .collect(); + + let dht = rings_node::DhtInfo { + did: inspect.dht.did, + successors: inspect.dht.successors, + predecessor: inspect.dht.predecessor, + finger_table_ranges: inspect + .dht + .finger_table + .into_iter() + .map(|(did, start, end)| rings_node::FingerTableRange { did, start, end }) + .collect(), + }; + + Self { + peers, + dht: Some(dht), + persistence_storage: Some(inspect.persistence_storage.into()), + cache_storage: Some(inspect.cache_storage.into()), + } + } +} + +impl From for rings_node::StorageInfo { + fn from(inspect: StorageInspect) -> Self { + Self { + items: inspect + .items + .into_iter() + .map(|(key, vnode)| rings_node::StorageItem { + key, + value: Some(rings_node::StorageValue { + did: vnode.did.to_string(), + kind: format!("{:?}", vnode.kind), + data: vnode.data.into_iter().map(|x| x.value().clone()).collect(), + }), + }) + .collect(), + } + } +} diff --git a/rpc/src/protos/rings_node.proto b/rpc/src/protos/rings_node.proto new file mode 100644 index 000000000..4e846bc1d --- /dev/null +++ b/rpc/src/protos/rings_node.proto @@ -0,0 +1,206 @@ +syntax = "proto3"; +package rings_node; + +message ConnectPeerViaHttpRequest { + string url = 1; +} + +message ConnectPeerViaHttpResponse { + string did = 1; +} + +message ConnectWithDidRequest { + string did = 1; +} + +message ConnectWithDidResponse {} + +message SeedPeer { + string did = 1; + string url = 2; +} + +message ConnectWithSeedRequest { + repeated SeedPeer peers = 1; +} + +message ConnectWithSeedResponse {} + +message PeerInfo { + string did = 1; + string state = 2; +} + +message ListPeersRequest {} + +message ListPeersResponse { + repeated PeerInfo peers = 1; +} + +message CreateOfferRequest { + string did = 1; +} + +message CreateOfferResponse { + string offer = 1; +} + +message AnswerOfferRequest { + string offer = 1; +} + +message AnswerOfferResponse { + string answer = 1; +} + +message AcceptAnswerRequest { + string answer = 1; +} + +message AcceptAnswerResponse { + PeerInfo peer = 1; +} + +message DisconnectRequest { + string did = 1; +} + +message DisconnectResponse {} + +message SendCustomMessageRequest { + string destination_did = 1; + string data = 2; +} + +message SendCustomMessageResponse {} + +message SendBackendMessageRequest { + string destination_did = 1; + string data = 2; +} + +message SendBackendMessageResponse {} + +message PublishMessageToTopicRequest { + string topic = 1; + string data = 2; +} + +message PublishMessageToTopicResponse {} + +message FetchMessagesOfTopicRequest { + string topic = 1; + int64 skip = 2; +} + +message FetchMessagesOfTopicResponse { + repeated string data = 1; +} + +message RegisterServiceRequest { + string name = 1; +} + +message RegisterServiceResponse {} + +message LookupServiceRequest { + string name = 1; +} + +message LookupServiceResponse { + repeated string dids = 1; +} + +message NodeInfoRequest {} + +message FingerTableRange { + optional string did = 1; + uint64 start = 2; + uint64 end = 3; +} + +message DhtInfo { + string did = 1; + repeated string successors = 2; + optional string predecessor = 3; + repeated FingerTableRange finger_table_ranges = 4; +} + +message StorageValue { + string did = 1; + string kind = 2; + repeated string data = 3; +} + +message StorageItem { + string key = 1; + StorageValue value = 2; +} + +message StorageInfo { + repeated StorageItem items = 1; +} + +message SwarmInfo { + repeated PeerInfo peers = 1; + DhtInfo dht = 2; + StorageInfo persistence_storage = 3; + StorageInfo cache_storage =4; +} + +message NodeInfoResponse { + string version = 1; + SwarmInfo swarm = 2; +} + +message NodeDidRequest {} + +message NodeDidResponse { + string did = 1; +} + +// Rings node internal service +service InternalService { + // Connect peer via remote peer's http endpoint + rpc ConnectPeerViaHttp (ConnectPeerViaHttpRequest) returns (ConnectPeerViaHttpResponse); + // Connect peer with remote peer's did + rpc ConnectWithDid (ConnectWithDidRequest) returns (ConnectWithDidResponse); + // Connect peers from a seed file + rpc ConnectWithSeed (ConnectWithSeedRequest) returns (ConnectWithSeedResponse); + // List all connected peers + rpc ListPeers (ListPeersRequest) returns (ListPeersResponse); + // Create offer for manually handshake + rpc CreateOffer (CreateOfferRequest) returns (CreateOfferResponse); + // Answer offer for manually handshake + rpc AnswerOffer (AnswerOfferRequest) returns (AnswerOfferResponse); + // Accept Answer for manually handshake + rpc AcceptAnswer (AcceptAnswerRequest) returns (AcceptAnswerResponse); + // Disconnect a peer + rpc Disconnect (DisconnectRequest) returns (DisconnectResponse); + // Send custom message to peer + rpc SendCustomMessage (SendCustomMessageRequest) returns (SendCustomMessageResponse); + // Send backend message + rpc SendBackendMessage (SendBackendMessageRequest) returns (SendBackendMessageResponse); + // Append data to topic + rpc PublishMessageToTopic(PublishMessageToTopicRequest) returns (PublishMessageToTopicResponse); + // Fetch data of topic + rpc FetchMessagesOfTopic(FetchMessagesOfTopicRequest) returns (FetchMessagesOfTopicResponse); + // Register service + rpc RegisterService(RegisterServiceRequest) returns (RegisterServiceResponse); + // Lookup service + rpc LookupService(LookupServiceRequest) returns (LookupServiceResponse); + // Retrieve Node info + rpc NodeInfo(NodeInfoRequest) returns (NodeInfoResponse); + // Retrieve Node DID + rpc NodeDid(NodeDidRequest) returns (NodeDidResponse); +} + +// Rings node external service +service ExternalService { + // Answer offer for manually handshake + rpc AnswerOffer (AnswerOfferRequest) returns (AnswerOfferResponse); + /// Retrieve Node info + rpc NodeInfo(NodeInfoRequest) returns (NodeInfoResponse); + /// Retrieve Node DID + rpc NodeDid(NodeDidRequest) returns (NodeDidResponse); +} diff --git a/rpc/src/protos/rings_node.rs b/rpc/src/protos/rings_node.rs new file mode 100644 index 000000000..6c0367daf --- /dev/null +++ b/rpc/src/protos/rings_node.rs @@ -0,0 +1,286 @@ +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ConnectPeerViaHttpRequest { + #[prost(string, tag = "1")] + pub url: ::prost::alloc::string::String, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ConnectPeerViaHttpResponse { + #[prost(string, tag = "1")] + pub did: ::prost::alloc::string::String, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ConnectWithDidRequest { + #[prost(string, tag = "1")] + pub did: ::prost::alloc::string::String, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ConnectWithDidResponse {} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SeedPeer { + #[prost(string, tag = "1")] + pub did: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub url: ::prost::alloc::string::String, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ConnectWithSeedRequest { + #[prost(message, repeated, tag = "1")] + pub peers: ::prost::alloc::vec::Vec, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ConnectWithSeedResponse {} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PeerInfo { + #[prost(string, tag = "1")] + pub did: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub state: ::prost::alloc::string::String, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ListPeersRequest {} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ListPeersResponse { + #[prost(message, repeated, tag = "1")] + pub peers: ::prost::alloc::vec::Vec, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateOfferRequest { + #[prost(string, tag = "1")] + pub did: ::prost::alloc::string::String, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateOfferResponse { + #[prost(string, tag = "1")] + pub offer: ::prost::alloc::string::String, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AnswerOfferRequest { + #[prost(string, tag = "1")] + pub offer: ::prost::alloc::string::String, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AnswerOfferResponse { + #[prost(string, tag = "1")] + pub answer: ::prost::alloc::string::String, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AcceptAnswerRequest { + #[prost(string, tag = "1")] + pub answer: ::prost::alloc::string::String, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AcceptAnswerResponse { + #[prost(message, optional, tag = "1")] + pub peer: ::core::option::Option, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DisconnectRequest { + #[prost(string, tag = "1")] + pub did: ::prost::alloc::string::String, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DisconnectResponse {} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SendCustomMessageRequest { + #[prost(string, tag = "1")] + pub destination_did: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub data: ::prost::alloc::string::String, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SendCustomMessageResponse {} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SendBackendMessageRequest { + #[prost(string, tag = "1")] + pub destination_did: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub data: ::prost::alloc::string::String, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SendBackendMessageResponse {} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PublishMessageToTopicRequest { + #[prost(string, tag = "1")] + pub topic: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub data: ::prost::alloc::string::String, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PublishMessageToTopicResponse {} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FetchMessagesOfTopicRequest { + #[prost(string, tag = "1")] + pub topic: ::prost::alloc::string::String, + #[prost(int64, tag = "2")] + pub skip: i64, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FetchMessagesOfTopicResponse { + #[prost(string, repeated, tag = "1")] + pub data: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RegisterServiceRequest { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RegisterServiceResponse {} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct LookupServiceRequest { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct LookupServiceResponse { + #[prost(string, repeated, tag = "1")] + pub dids: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct NodeInfoRequest {} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FingerTableRange { + #[prost(string, optional, tag = "1")] + pub did: ::core::option::Option<::prost::alloc::string::String>, + #[prost(uint64, tag = "2")] + pub start: u64, + #[prost(uint64, tag = "3")] + pub end: u64, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DhtInfo { + #[prost(string, tag = "1")] + pub did: ::prost::alloc::string::String, + #[prost(string, repeated, tag = "2")] + pub successors: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(string, optional, tag = "3")] + pub predecessor: ::core::option::Option<::prost::alloc::string::String>, + #[prost(message, repeated, tag = "4")] + pub finger_table_ranges: ::prost::alloc::vec::Vec, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct StorageValue { + #[prost(string, tag = "1")] + pub did: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub kind: ::prost::alloc::string::String, + #[prost(string, repeated, tag = "3")] + pub data: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct StorageItem { + #[prost(string, tag = "1")] + pub key: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub value: ::core::option::Option, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct StorageInfo { + #[prost(message, repeated, tag = "1")] + pub items: ::prost::alloc::vec::Vec, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SwarmInfo { + #[prost(message, repeated, tag = "1")] + pub peers: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "2")] + pub dht: ::core::option::Option, + #[prost(message, optional, tag = "3")] + pub persistence_storage: ::core::option::Option, + #[prost(message, optional, tag = "4")] + pub cache_storage: ::core::option::Option, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct NodeInfoResponse { + #[prost(string, tag = "1")] + pub version: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub swarm: ::core::option::Option, +} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct NodeDidRequest {} +#[derive(serde::Serialize, serde::Deserialize)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct NodeDidResponse { + #[prost(string, tag = "1")] + pub did: ::prost::alloc::string::String, +} diff --git a/rpc/src/protos/rings_node_handler.rs b/rpc/src/protos/rings_node_handler.rs new file mode 100644 index 000000000..6eed434f4 --- /dev/null +++ b/rpc/src/protos/rings_node_handler.rs @@ -0,0 +1,205 @@ +use std::sync::Arc; + +use async_trait::async_trait; + +use super::rings_node::*; +use crate::method::Method; +use crate::prelude::jsonrpc_core::types::error::Error; +use crate::prelude::jsonrpc_core::types::error::ErrorCode; +use crate::prelude::jsonrpc_core::Result; + +/// Used for processor to match rpc request and response. +#[cfg_attr(feature = "wasm", async_trait(?Send))] +#[cfg_attr(not(feature = "wasm"), async_trait)] +pub trait HandleRpc { + /// Handle rpc request and return response. + async fn handle_rpc(&self, req: Req) -> Result; +} + +/// Provide handle_request method for internal rpc api. +#[derive(Clone, Copy)] +pub struct InternalRpcHandler; + +/// Provide handle_request method for external rpc api. +#[derive(Clone, Copy)] +pub struct ExternalRpcHandler; + +impl InternalRpcHandler { + /// Handle rpc request. + pub async fn handle_request

( + &self, + processor: Arc

, + method: String, + params: serde_json::Value, + ) -> Result + where + P: HandleRpc + + HandleRpc + + HandleRpc + + HandleRpc + + HandleRpc + + HandleRpc + + HandleRpc + + HandleRpc + + HandleRpc + + HandleRpc + + HandleRpc + + HandleRpc + + HandleRpc + + HandleRpc + + HandleRpc + + HandleRpc, + { + let method = Method::try_from(method.as_str()).map_err(|_| Error { + code: ErrorCode::MethodNotFound, + message: format!("method {} is not found", method), + data: None, + })?; + + match method { + Method::ConnectPeerViaHttp => { + let req = serde_json::from_value::(params) + .map_err(|e| Error::invalid_params(e.to_string()))?; + let resp = processor.handle_rpc(req).await?; + serde_json::to_value(resp).map_err(|_| Error::new(ErrorCode::ParseError)) + } + Method::ConnectWithDid => { + let req = serde_json::from_value::(params) + .map_err(|e| Error::invalid_params(e.to_string()))?; + let resp = processor.handle_rpc(req).await?; + serde_json::to_value(resp).map_err(|_| Error::new(ErrorCode::ParseError)) + } + Method::ConnectWithSeed => { + let req = serde_json::from_value::(params) + .map_err(|e| Error::invalid_params(e.to_string()))?; + let resp = processor.handle_rpc(req).await?; + serde_json::to_value(resp).map_err(|_| Error::new(ErrorCode::ParseError)) + } + Method::ListPeers => { + let req = serde_json::from_value::(params) + .map_err(|e| Error::invalid_params(e.to_string()))?; + let resp = processor.handle_rpc(req).await?; + serde_json::to_value(resp).map_err(|_| Error::new(ErrorCode::ParseError)) + } + Method::CreateOffer => { + let req = serde_json::from_value::(params) + .map_err(|e| Error::invalid_params(e.to_string()))?; + let resp = processor.handle_rpc(req).await?; + serde_json::to_value(resp).map_err(|_| Error::new(ErrorCode::ParseError)) + } + Method::AnswerOffer => { + let req = serde_json::from_value::(params) + .map_err(|e| Error::invalid_params(e.to_string()))?; + let resp = processor.handle_rpc(req).await?; + serde_json::to_value(resp).map_err(|_| Error::new(ErrorCode::ParseError)) + } + Method::AcceptAnswer => { + let req = serde_json::from_value::(params) + .map_err(|e| Error::invalid_params(e.to_string()))?; + let resp = processor.handle_rpc(req).await?; + serde_json::to_value(resp).map_err(|_| Error::new(ErrorCode::ParseError)) + } + Method::Disconnect => { + let req = serde_json::from_value::(params) + .map_err(|e| Error::invalid_params(e.to_string()))?; + let resp = processor.handle_rpc(req).await?; + serde_json::to_value(resp).map_err(|_| Error::new(ErrorCode::ParseError)) + } + Method::SendCustomMessage => { + let req = serde_json::from_value::(params) + .map_err(|e| Error::invalid_params(e.to_string()))?; + let resp = processor.handle_rpc(req).await?; + serde_json::to_value(resp).map_err(|_| Error::new(ErrorCode::ParseError)) + } + Method::SendBackendMessage => { + let req = serde_json::from_value::(params) + .map_err(|e| Error::invalid_params(e.to_string()))?; + let resp = processor.handle_rpc(req).await?; + serde_json::to_value(resp).map_err(|_| Error::new(ErrorCode::ParseError)) + } + Method::PublishMessageToTopic => { + let req = serde_json::from_value::(params) + .map_err(|e| Error::invalid_params(e.to_string()))?; + let resp = processor.handle_rpc(req).await?; + serde_json::to_value(resp).map_err(|_| Error::new(ErrorCode::ParseError)) + } + Method::FetchMessagesOfTopic => { + let req = serde_json::from_value::(params) + .map_err(|e| Error::invalid_params(e.to_string()))?; + let resp = processor.handle_rpc(req).await?; + serde_json::to_value(resp).map_err(|_| Error::new(ErrorCode::ParseError)) + } + Method::RegisterService => { + let req = serde_json::from_value::(params) + .map_err(|e| Error::invalid_params(e.to_string()))?; + let resp = processor.handle_rpc(req).await?; + serde_json::to_value(resp).map_err(|_| Error::new(ErrorCode::ParseError)) + } + Method::LookupService => { + let req = serde_json::from_value::(params) + .map_err(|e| Error::invalid_params(e.to_string()))?; + let resp = processor.handle_rpc(req).await?; + serde_json::to_value(resp).map_err(|_| Error::new(ErrorCode::ParseError)) + } + Method::NodeInfo => { + let req = serde_json::from_value::(params) + .map_err(|e| Error::invalid_params(e.to_string()))?; + let resp = processor.handle_rpc(req).await?; + serde_json::to_value(resp).map_err(|_| Error::new(ErrorCode::ParseError)) + } + Method::NodeDid => { + let req = serde_json::from_value::(params) + .map_err(|e| Error::invalid_params(e.to_string()))?; + let resp = processor.handle_rpc(req).await?; + serde_json::to_value(resp).map_err(|_| Error::new(ErrorCode::ParseError)) + } + } + } +} + +impl ExternalRpcHandler { + /// Handle rpc request. + pub async fn handle_request

( + &self, + processor: Arc

, + method: String, + params: serde_json::Value, + ) -> Result + where + P: HandleRpc + + HandleRpc + + HandleRpc, + { + let method = Method::try_from(method.as_str()).map_err(|_| Error { + code: ErrorCode::MethodNotFound, + message: format!("method {} is not found", method), + data: None, + })?; + + match method { + Method::AnswerOffer => { + let req = serde_json::from_value::(params) + .map_err(|e| Error::invalid_params(e.to_string()))?; + let resp = processor.handle_rpc(req).await?; + serde_json::to_value(resp).map_err(|_| Error::new(ErrorCode::ParseError)) + } + Method::NodeInfo => { + let req = serde_json::from_value::(params) + .map_err(|e| Error::invalid_params(e.to_string()))?; + let resp = processor.handle_rpc(req).await?; + serde_json::to_value(resp).map_err(|_| Error::new(ErrorCode::ParseError)) + } + Method::NodeDid => { + let req = serde_json::from_value::(params) + .map_err(|e| Error::invalid_params(e.to_string()))?; + let resp = processor.handle_rpc(req).await?; + serde_json::to_value(resp).map_err(|_| Error::new(ErrorCode::ParseError)) + } + _ => Err(Error { + code: ErrorCode::InvalidRequest, + message: format!("method {} is not allowed", method.as_str()), + data: None, + }), + } + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..a5bbdb26f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "outDir": "./dist", + "module": "commonjs", + "target": "es5", + "declaration": true, + "esModuleInterop": true + }, + "include": ["./index.ts"] +}