Merge branch 'release/0.2.0'

This commit is contained in:
Dave Hrycyszyn
2020-01-06 18:01:57 +00:00
17 changed files with 3597 additions and 71 deletions
+5 -1
View File
@@ -1,2 +1,6 @@
/target
/targetString
**/*.rs.bk
/*/target
/test_inbox
.idea
target
+13
View File
@@ -0,0 +1,13 @@
# nym-sfw-provider Changelog
## 0.2.0
* removed the `--local` flag
* introduced `--directory` argument to support arbitrary directory servers. Leaving it out will point the node at the "https://directory.nymtech.net" alpha testnet server
* IPv6 support
* provider version number is now shown at node start
* directory server location is now shown at node start
## 0.1.0 - Initial Release
* The bare minimum set of features required by a Nym Store and Forward Provider
Generated
+1642 -31
View File
File diff suppressed because it is too large Load Diff
+19 -3
View File
@@ -1,11 +1,27 @@
[package]
build = "build.rs"
name = "nym-sfw-provider"
version = "0.1.0"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
version = "0.2.0"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
base64 = "0.11.0"
clap = "2.33.0"
curve25519-dalek = "1.2.3"
hex = "0.4.0"
futures = "0.3.1"
nym-client = { path = "../nym-client" }
rand = "0.7.2"
sfw-provider-requests = { path = "./sfw-provider-requests" }
sphinx = { path = "../sphinx" }
tokio = { version = "0.2", features = ["full"] }
tokio = { version = "0.2.4", features = ["full"] }
sha2 = "0.8.0"
serde = { version = "1.0.104", features = ["derive"] }
serde_json = "1.0.44"
hmac = "0.7.1"
[build-dependencies]
built = "0.3.2"
+2
View File
@@ -0,0 +1,2 @@
# nym-sfw-provider
A store-and-forward provider for Nym, implemented in Rust
+5
View File
@@ -0,0 +1,5 @@
use built;
fn main() {
built::write_built_file().expect("Failed to acquire build-time information");
}
+463
View File
@@ -0,0 +1,463 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "aes-ctr"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "aes-soft"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "aesni"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "arrayref"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "blake2"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "block-buffer"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "block-cipher-trait"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "block-padding"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "byte-tools"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "byteorder"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "c2-chacha"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cc"
version = "1.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "chacha"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"keystream 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "clear_on_drop"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "crypto-mac"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
"subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ctr"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "curve25519-dalek"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "digest"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fake-simd"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "generic-array"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "getrandom"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "hkdf"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "hmac"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "keystream"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lioness"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"chacha 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"keystream 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "opaque-debug"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "ppv-lite86"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rand"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_chacha"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_distr"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_os"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "sfw-provider-requests"
version = "0.1.0"
dependencies = [
"sphinx 0.1.0",
]
[[package]]
name = "sha2"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "sphinx"
version = "0.1.0"
dependencies = [
"aes-ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"chacha 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"curve25519-dalek 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"hkdf 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lioness 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_distr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "stream-cipher"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "subtle"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "subtle"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "typenum"
version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "wasi"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum aes-ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d2e5b0458ea3beae0d1d8c0f3946564f8e10f90646cf78c06b4351052058d1ee"
"checksum aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d"
"checksum aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100"
"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
"checksum blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330"
"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
"checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774"
"checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
"checksum cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)" = "f52a465a666ca3d838ebbf08b241383421412fe7ebb463527bba275526d89f76"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum chacha 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddf3c081b5fba1e5615640aae998e0fbd10c24cbd897ee39ed754a77601a4862"
"checksum clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5"
"checksum ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "022cd691704491df67d25d006fe8eca083098253c4d43516c2206479c58c6736"
"checksum curve25519-dalek 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8b7dcd30ba50cdf88b55b033456138b7c0ac4afdc436d82e1b79f370f24cc66d"
"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407"
"checksum hkdf 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3fa08a006102488bd9cd5b8013aabe84955cf5ae22e304c2caf655b633aefae3"
"checksum hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695"
"checksum keystream 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c33070833c9ee02266356de0c43f723152bd38bd96ddf52c82b3af10c9138b28"
"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
"checksum lioness 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4ae926706ba42c425c9457121178330d75e273df2e82e28b758faf3de3a9acb9"
"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412"
"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853"
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
"checksum rand_distr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96977acbdd3a6576fb1d27391900035bf3863d4a16422973a409b488cf29ffb2"
"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d"
"checksum stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8131256a5896cabcf5eb04f4d6dacbe1aefda854b0d9896e09cb58829ec5638c"
"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
"checksum subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941"
"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9"
"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+10
View File
@@ -0,0 +1,10 @@
[package]
name = "sfw-provider-requests"
version = "0.1.0"
authors = ["Jedrzej Stuczynski <jedrzej.stuczynski@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
sphinx = { path = "../../sphinx" }
+7
View File
@@ -0,0 +1,7 @@
pub mod requests;
pub mod responses;
pub const DUMMY_MESSAGE_CONTENT: &[u8] =
b"[DUMMY MESSAGE] Wanting something does not give you the right to have it.";
pub type AuthToken = [u8; 32];
+231
View File
@@ -0,0 +1,231 @@
use crate::AuthToken;
use sphinx::route::DestinationAddressBytes;
const PULL_REQUEST_MESSAGE_PREFIX: [u8; 2] = [1, 0];
const REGISTER_MESSAGE_PREFIX: [u8; 2] = [0, 1];
// TODO: how to do it more nicely, considering all sfw-provider-requests implement same trait that is exercised here?
#[derive(Debug)]
pub enum ProviderRequests {
PullMessages(PullRequest),
Register(RegisterRequest),
}
impl ProviderRequests {
pub fn to_bytes(&self) -> Vec<u8> {
use ProviderRequests::*;
match self {
PullMessages(pr) => pr.to_bytes(),
Register(pr) => pr.to_bytes(),
}
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, ProviderRequestError> {
use ProviderRequests::*;
if bytes.len() < 2 {
return Err(ProviderRequestError::UnmarshalError);
}
let mut received_prefix = [0; 2];
received_prefix.copy_from_slice(&bytes[..2]);
match received_prefix {
PULL_REQUEST_MESSAGE_PREFIX => Ok(PullMessages(PullRequest::from_bytes(bytes)?)),
REGISTER_MESSAGE_PREFIX => Ok(Register(RegisterRequest::from_bytes(bytes)?)),
_ => Err(ProviderRequestError::UnmarshalErrorIncorrectPrefix),
}
}
}
#[derive(Debug)]
pub enum ProviderRequestError {
MarshalError,
UnmarshalError,
UnmarshalErrorIncorrectPrefix,
}
pub trait ProviderRequest
where
Self: Sized,
{
fn get_prefix() -> [u8; 2];
fn to_bytes(&self) -> Vec<u8>;
fn from_bytes(bytes: &[u8]) -> Result<Self, ProviderRequestError>;
}
#[derive(Debug)]
pub struct PullRequest {
// TODO: public keys, signatures, tokens, etc. basically some kind of authentication bs
pub auth_token: AuthToken,
pub destination_address: sphinx::route::DestinationAddressBytes,
}
impl PullRequest {
pub fn new(
destination_address: sphinx::route::DestinationAddressBytes,
auth_token: AuthToken,
) -> Self {
PullRequest {
auth_token,
destination_address,
}
}
}
impl ProviderRequest for PullRequest {
fn get_prefix() -> [u8; 2] {
PULL_REQUEST_MESSAGE_PREFIX
}
fn to_bytes(&self) -> Vec<u8> {
Self::get_prefix()
.to_vec()
.into_iter()
.chain(self.destination_address.iter().cloned())
.chain(self.auth_token.iter().cloned())
.collect()
}
fn from_bytes(bytes: &[u8]) -> Result<Self, ProviderRequestError> {
if bytes.len() != 2 + 32 + 32 {
return Err(ProviderRequestError::UnmarshalError);
}
let mut received_prefix = [0u8; 2];
received_prefix.copy_from_slice(&bytes[..2]);
if received_prefix != Self::get_prefix() {
return Err(ProviderRequestError::UnmarshalErrorIncorrectPrefix);
}
let mut destination_address = [0u8; 32];
destination_address.copy_from_slice(&bytes[2..34]);
let mut auth_token = [0u8; 32];
auth_token.copy_from_slice(&bytes[34..]);
Ok(PullRequest {
auth_token,
destination_address,
})
}
}
#[derive(Debug)]
pub struct RegisterRequest {
pub destination_address: DestinationAddressBytes,
}
impl RegisterRequest {
pub fn new(destination_address: DestinationAddressBytes) -> Self {
RegisterRequest {
destination_address,
}
}
}
impl ProviderRequest for RegisterRequest {
fn get_prefix() -> [u8; 2] {
REGISTER_MESSAGE_PREFIX
}
fn to_bytes(&self) -> Vec<u8> {
Self::get_prefix()
.to_vec()
.into_iter()
.chain(self.destination_address.iter().cloned())
.collect()
}
fn from_bytes(bytes: &[u8]) -> Result<Self, ProviderRequestError> {
if bytes.len() != 2 + 32 {
return Err(ProviderRequestError::UnmarshalError);
}
let mut received_prefix = [0u8; 2];
received_prefix.copy_from_slice(&bytes[..2]);
if received_prefix != Self::get_prefix() {
return Err(ProviderRequestError::UnmarshalErrorIncorrectPrefix);
}
let mut destination_address = [0u8; 32];
destination_address.copy_from_slice(&bytes[2..]);
Ok(RegisterRequest {
destination_address,
})
}
}
#[cfg(test)]
mod creating_pull_request {
use super::*;
#[test]
fn it_is_possible_to_recover_it_from_bytes() {
let address = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
0, 1, 2,
];
let auth_token = [1u8; 32];
let pull_request = PullRequest::new(address, auth_token);
let bytes = pull_request.to_bytes();
let recovered = PullRequest::from_bytes(&bytes).unwrap();
assert_eq!(address, recovered.destination_address);
assert_eq!(auth_token, recovered.auth_token);
}
#[test]
fn it_is_possible_to_recover_it_from_bytes_with_enum_wrapper() {
let address = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
0, 1, 2,
];
let auth_token = [1u8; 32];
let pull_request = PullRequest::new(address, auth_token);
let bytes = pull_request.to_bytes();
let recovered = ProviderRequests::from_bytes(&bytes).unwrap();
match recovered {
ProviderRequests::PullMessages(req) => {
assert_eq!(address, req.destination_address);
assert_eq!(auth_token, req.auth_token);
}
_ => panic!("expected to recover pull request!"),
}
}
}
#[cfg(test)]
mod creating_register_request {
use super::*;
#[test]
fn it_is_possible_to_recover_it_from_bytes() {
let address = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
0, 1, 2,
];
let register_request = RegisterRequest::new(address);
let bytes = register_request.to_bytes();
let recovered = RegisterRequest::from_bytes(&bytes).unwrap();
assert_eq!(address, recovered.destination_address);
}
#[test]
fn it_is_possible_to_recover_it_from_bytes_with_enum_wrapper() {
let address = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
0, 1, 2,
];
let register_request = RegisterRequest::new(address);
let bytes = register_request.to_bytes();
let recovered = ProviderRequests::from_bytes(&bytes).unwrap();
match recovered {
ProviderRequests::Register(req) => {
assert_eq!(address, req.destination_address);
}
_ => panic!("expected to recover pull request!"),
}
}
}
+151
View File
@@ -0,0 +1,151 @@
use crate::AuthToken;
use std::convert::TryInto;
#[derive(Debug)]
pub enum ProviderResponseError {
MarshalError,
UnmarshalError,
UnmarshalErrorInvalidLength,
}
pub trait ProviderResponse
where
Self: Sized,
{
fn to_bytes(&self) -> Vec<u8>;
fn from_bytes(bytes: &[u8]) -> Result<Self, ProviderResponseError>;
}
#[derive(Debug)]
pub struct PullResponse {
pub messages: Vec<Vec<u8>>,
}
#[derive(Debug)]
pub struct RegisterResponse {
pub auth_token: AuthToken,
}
impl PullResponse {
pub fn new(messages: Vec<Vec<u8>>) -> Self {
PullResponse { messages }
}
}
impl RegisterResponse {
pub fn new(auth_token: AuthToken) -> Self {
RegisterResponse { auth_token }
}
}
// TODO: This should go into some kind of utils module/crate
fn read_be_u16(input: &mut &[u8]) -> u16 {
let (int_bytes, rest) = input.split_at(std::mem::size_of::<u16>());
*input = rest;
u16::from_be_bytes(int_bytes.try_into().unwrap())
}
// TODO: currently this allows for maximum 64kB payload - if we go over that in sphinx,
// we need to update this code.
impl ProviderResponse for PullResponse {
// num_msgs || len1 || len2 || ... || msg1 || msg2 || ...
fn to_bytes(&self) -> Vec<u8> {
let num_msgs = self.messages.len() as u16;
let msgs_lens: Vec<u16> = self.messages.iter().map(|msg| msg.len() as u16).collect();
num_msgs
.to_be_bytes()
.to_vec()
.into_iter()
.chain(
msgs_lens
.into_iter()
.flat_map(|len| len.to_be_bytes().to_vec().into_iter()),
)
.chain(self.messages.iter().flat_map(|msg| msg.clone().into_iter()))
.collect()
}
fn from_bytes(bytes: &[u8]) -> Result<Self, ProviderResponseError> {
// can we read number of messages?
if bytes.len() < 2 {
return Err(ProviderResponseError::UnmarshalErrorInvalidLength);
}
let mut bytes_copy = bytes.clone();
let num_msgs = read_be_u16(&mut bytes_copy);
// can we read all lengths of messages?
if bytes_copy.len() < (num_msgs * 2) as usize {
return Err(ProviderResponseError::UnmarshalErrorInvalidLength);
}
let msgs_lens: Vec<_> = (0..num_msgs)
.map(|_| read_be_u16(&mut bytes_copy))
.collect();
let required_remaining_len = msgs_lens
.iter()
.fold(0usize, |acc, &len| acc + (len as usize));
// can we read messages themselves?
if bytes_copy.len() != required_remaining_len {
return Err(ProviderResponseError::UnmarshalErrorInvalidLength);
}
let msgs = msgs_lens
.iter()
.scan(0usize, |i, &len| {
let j = *i + (len as usize);
let msg = bytes_copy[*i..j].to_vec();
*i = j;
Some(msg)
})
.collect();
Ok(PullResponse { messages: msgs })
}
}
impl ProviderResponse for RegisterResponse {
fn to_bytes(&self) -> Vec<u8> {
self.auth_token.to_vec()
}
fn from_bytes(bytes: &[u8]) -> Result<Self, ProviderResponseError> {
match bytes.len() {
32 => {
let mut auth_token = [0u8; 32];
auth_token.copy_from_slice(&bytes[..32]);
Ok(RegisterResponse { auth_token })
}
_ => Err(ProviderResponseError::UnmarshalErrorInvalidLength),
}
}
}
#[cfg(test)]
mod creating_pull_response {
use super::*;
#[test]
fn it_is_possible_to_recover_it_from_bytes() {
let msg1 = vec![1, 2, 3, 4, 5];
let msg2 = vec![];
let msg3 = vec![
1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4,
5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3,
4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2,
3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1,
2, 3, 4, 5, 1, 2, 3, 4, 5,
];
let msg4 = vec![1, 2, 3, 4, 5, 6, 7];
let msgs = vec![msg1, msg2, msg3, msg4];
let pull_response = PullResponse::new(msgs.clone());
let bytes = pull_response.to_bytes();
let recovered = PullResponse::from_bytes(&bytes).unwrap();
assert_eq!(msgs, recovered.messages);
}
}
+182 -36
View File
@@ -1,41 +1,187 @@
use tokio::net::TcpListener;
use tokio::prelude::*;
use sphinx::SphinxPacket;
use crate::provider::ServiceProvider;
use clap::{App, Arg, ArgMatches, SubCommand};
use nym_client::identity::mixnet::KeyPair;
use std::net::ToSocketAddrs;
use std::path::PathBuf;
use std::process;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let my_address = "127.0.0.1:8081";
let mut listener = TcpListener::bind(my_address).await?;
pub mod provider;
println!("Starting Nym store-and-forward Provider on address {:?}", my_address);
println!("Waiting for input...");
fn main() {
let arg_matches = App::new("Nym Service Provider")
.version("0.1.0")
.author("Nymtech")
.about("Implementation of the Loopix-based Service Provider")
.subcommand(
SubCommand::with_name("run")
.about("Starts the service provider")
.arg(
Arg::with_name("mixHost")
.long("mixHost")
.help("The custom host on which the service provider will be running for receiving sphinx packets")
.takes_value(true),
)
.arg(
Arg::with_name("mixPort")
.long("mixPort")
.help("The port on which the service provider will be listening for sphinx packets")
.takes_value(true)
)
.arg(
Arg::with_name("clientHost")
.long("clientHost")
.help("The custom host on which the service provider will be running for receiving client sfw-provider-requests")
.takes_value(true),
)
.arg(
Arg::with_name("clientPort")
.long("clientPort")
.help("The port on which the service provider will be listening for client sfw-provider-requests")
.takes_value(true)
)
.arg(
Arg::with_name("storeDir")
.short("s")
.long("storeDir")
.help("Directory storing all packets for the clients")
.takes_value(true)
)
.arg(
Arg::with_name("registeredLedger")
.short("r")
.long("registeredLedger")
.help("Directory of the ledger of registered clients")
.takes_value(true)
)
.arg(
Arg::with_name("directory")
.long("directory")
.help("Address of the directory server the node is sending presence and metrics to")
.takes_value(true),
),
)
.get_matches();
loop {
let (mut inbound, _) = listener.accept().await?;
tokio::spawn(async move {
let mut buf = [0; 1024];
loop {
let _ = match inbound.read(&mut buf).await {
Ok(length) if length == 0 =>
{
println!("Remote connection closed.");
return
}
Ok(_) => {
println!("Received a packet!");
let packet = SphinxPacket::from_bytes(buf.to_vec()).unwrap();
let (unwrapped_packet, _) = packet.process(Default::default());
let message = unwrapped_packet.payload.content;
println!("Got message: {:?}", String::from_utf8(message).unwrap());
}
Err(e) => {
println!("failed to read from socket; err = {:?}", e);
return;
}
};
}
});
if let Err(e) = execute(arg_matches) {
println!("{}", e);
process::exit(1);
}
}
fn run(matches: &ArgMatches) {
let config = new_config(matches);
let provider = ServiceProvider::new(&config);
provider.start().unwrap()
}
fn new_config(matches: &ArgMatches) -> provider::Config {
println!("Running the service provider!");
let directory_server = matches
.value_of("directory")
.unwrap_or("https://directory.nymtech.net")
.to_string();
let mix_host = matches.value_of("mixHost").unwrap();
let mix_port = match matches.value_of("mixPort").unwrap_or("8085").parse::<u16>() {
Ok(n) => n,
Err(err) => panic!("Invalid mix host port value provided - {:?}", err),
};
let client_host = matches.value_of("clientHost").unwrap();
let client_port = match matches
.value_of("clientPort")
.unwrap_or("9000")
.parse::<u16>()
{
Ok(n) => n,
Err(err) => panic!("Invalid client port value provided - {:?}", err),
};
let key_pair = KeyPair::new(); // TODO: persist this so keypairs don't change every restart
let store_dir = PathBuf::from(
matches
.value_of("storeDir")
.unwrap_or("/tmp/nym-provider/inboxes"),
);
let registered_client_ledger_dir = PathBuf::from(
matches
.value_of("registeredLedger")
.unwrap_or("/tmp/nym-provider/registered_clients"),
);
println!("The value of mix_host is: {:?}", mix_host);
println!("The value of mix_port is: {:?}", mix_port);
println!("The value of client_host is: {:?}", client_host);
println!("The value of client_port is: {:?}", client_port);
println!("The value of key is: {:?}", key_pair.private.clone());
println!("The value of store_dir is: {:?}", store_dir);
println!(
"The value of registered_client_ledger_dir is: {:?}",
registered_client_ledger_dir
);
let mix_socket_address = (mix_host, mix_port)
.to_socket_addrs()
.expect("Failed to combine host and port")
.next()
.expect("Failed to extract the socket address from the iterator");
let client_socket_address = (client_host, client_port)
.to_socket_addrs()
.expect("Failed to combine host and port")
.next()
.expect("Failed to extract the socket address from the iterator");
println!(
"The full combined mix socket address is {}",
mix_socket_address
);
println!(
"The full combined client socket address is {}",
client_socket_address
);
provider::Config {
mix_socket_address,
directory_server,
public_key: key_pair.public,
client_socket_address,
secret_key: key_pair.private,
store_dir: PathBuf::from(store_dir),
}
}
pub mod built_info {
// The file has been placed there by the build script.
include!(concat!(env!("OUT_DIR"), "/built.rs"));
}
fn execute(matches: ArgMatches) -> Result<(), String> {
match matches.subcommand() {
("run", Some(m)) => Ok(run(m)),
_ => Err(usage()),
}
}
fn usage() -> String {
banner() + "usage: --help to see available options.\n\n"
}
fn banner() -> String {
format!(
r#"
_ __ _ _ _ __ ___
| '_ \| | | | '_ \ _ \
| | | | |_| | | | | | |
|_| |_|\__, |_| |_| |_|
|___/
(store-and-forward provider - version {:})
"#,
built_info::PKG_VERSION
)
}
+270
View File
@@ -0,0 +1,270 @@
use crate::provider::storage::{ClientStorage, StoreError};
use crate::provider::ClientLedger;
use curve25519_dalek::scalar::Scalar;
use futures::lock::Mutex as FMutex;
use hmac::{Hmac, Mac};
use sfw_provider_requests::requests::{
ProviderRequestError, ProviderRequests, PullRequest, RegisterRequest,
};
use sfw_provider_requests::responses::{ProviderResponse, PullResponse, RegisterResponse};
use sfw_provider_requests::AuthToken;
use sha2::Sha256;
use std::io;
use std::path::{Path, PathBuf};
use std::sync::Arc;
type HmacSha256 = Hmac<Sha256>;
#[derive(Debug)]
pub enum ClientProcessingError {
ClientDoesntExistError,
StoreError,
InvalidRequest,
WrongToken,
IOError,
}
impl From<ProviderRequestError> for ClientProcessingError {
fn from(_: ProviderRequestError) -> Self {
use ClientProcessingError::*;
InvalidRequest
}
}
impl From<StoreError> for ClientProcessingError {
fn from(e: StoreError) -> Self {
match e {
StoreError::ClientDoesntExistError => ClientProcessingError::ClientDoesntExistError,
_ => ClientProcessingError::StoreError,
}
}
}
impl From<io::Error> for ClientProcessingError {
fn from(_: io::Error) -> Self {
use ClientProcessingError::*;
IOError
}
}
#[derive(Debug)]
pub(crate) struct ClientProcessingData {
store_dir: PathBuf,
registered_clients_ledger: Arc<FMutex<ClientLedger>>,
secret_key: Scalar,
}
impl ClientProcessingData {
pub(crate) fn new(
store_dir: PathBuf,
registered_clients_ledger: Arc<FMutex<ClientLedger>>,
secret_key: Scalar,
) -> Self {
ClientProcessingData {
store_dir,
registered_clients_ledger,
secret_key,
}
}
pub(crate) fn add_arc(self) -> Arc<Self> {
Arc::new(self)
}
}
pub(crate) struct ClientRequestProcessor;
impl ClientRequestProcessor {
pub(crate) async fn process_client_request(
data: &[u8],
processing_data: Arc<ClientProcessingData>,
) -> Result<Vec<u8>, ClientProcessingError> {
let client_request = ProviderRequests::from_bytes(&data)?;
println!("Received the following request: {:?}", client_request);
match client_request {
ProviderRequests::Register(req) => Ok(ClientRequestProcessor::register_new_client(
req,
processing_data,
)
.await?
.to_bytes()),
ProviderRequests::PullMessages(req) => Ok(
ClientRequestProcessor::process_pull_messages_request(req, processing_data)
.await?
.to_bytes(),
),
}
}
async fn process_pull_messages_request(
req: PullRequest,
processing_data: Arc<ClientProcessingData>,
) -> Result<PullResponse, ClientProcessingError> {
// TODO: this lock is completely unnecessary as we're only reading the data.
// Wait for https://github.com/nymtech/nym-sfw-provider/issues/19 to resolve.
let unlocked_ledger = processing_data.registered_clients_ledger.lock().await;
println!("Processing pull!");
if unlocked_ledger.has_token(req.auth_token) {
// drop the mutex so that we could do IO without blocking others wanting to get the lock
drop(unlocked_ledger);
let retrieved_messages = ClientStorage::retrieve_client_files(
req.destination_address,
processing_data.store_dir.as_path(),
)?;
Ok(PullResponse::new(retrieved_messages))
} else {
Err(ClientProcessingError::WrongToken)
}
}
async fn register_new_client(
req: RegisterRequest,
processing_data: Arc<ClientProcessingData>,
) -> Result<RegisterResponse, ClientProcessingError> {
println!("Processing register new client request!");
let mut unlocked_ledger = processing_data.registered_clients_ledger.lock().await;
let auth_token = ClientRequestProcessor::generate_new_auth_token(
req.destination_address.to_vec(),
processing_data.secret_key,
);
if !unlocked_ledger.has_token(auth_token) {
unlocked_ledger.insert_token(auth_token, req.destination_address);
ClientRequestProcessor::create_storage_dir(
req.destination_address,
processing_data.store_dir.as_path(),
)?;
}
Ok(RegisterResponse::new(auth_token))
}
fn create_storage_dir(
client_address: sphinx::route::DestinationAddressBytes,
store_dir: &Path,
) -> io::Result<()> {
let client_dir_name = hex::encode(client_address);
let full_store_dir = store_dir.join(client_dir_name);
std::fs::create_dir_all(full_store_dir)
}
fn generate_new_auth_token(data: Vec<u8>, key: Scalar) -> AuthToken {
let mut auth_token_raw =
HmacSha256::new_varkey(&key.to_bytes()).expect("HMAC can take key of any size");
auth_token_raw.input(&data);
let mut auth_token = [0u8; 32];
auth_token.copy_from_slice(&auth_token_raw.result().code().to_vec());
auth_token
}
}
#[cfg(test)]
mod register_new_client {
// use super::*;
// TODO: those tests require being called in async context. we need to research how to test this stuff...
// #[test]
// fn registers_new_auth_token_for_each_new_client() {
// let req1 = RegisterRequest {
// destination_address: [1u8; 32],
// };
// let registered_client_ledger = ClientLedger::new();
// let store_dir = PathBuf::from("./foo/");
// let key = Scalar::from_bytes_mod_order([1u8; 32]);
// let client_processing_data = ClientProcessingData::new(store_dir, registered_client_ledger, key).add_arc_futures_mutex();
//
//
// // need to do async....
// client_processing_data.lock().await;
// assert_eq!(0, registered_client_ledger.0.len());
// ClientRequestProcessor::register_new_client(
// req1,
// client_processing_data.clone(),
// );
//
// assert_eq!(1, registered_client_ledger.0.len());
//
// let req2 = RegisterRequest {
// destination_address: [2u8; 32],
// };
// ClientRequestProcessor::register_new_client(
// req2,
// client_processing_data,
// );
// assert_eq!(2, registered_client_ledger.0.len());
// }
//
// #[test]
// fn registers_given_token_only_once() {
// let req1 = RegisterRequest {
// destination_address: [1u8; 32],
// };
// let registered_client_ledger = ClientLedger::new();
// let store_dir = PathBuf::from("./foo/");
// let key = Scalar::from_bytes_mod_order([1u8; 32]);
// let client_processing_data = ClientProcessingData::new(store_dir, registered_client_ledger, key).add_arc_futures_mutex();
//
// ClientRequestProcessor::register_new_client(
// req1,
// client_processing_data.clone(),
// );
// let req2 = RegisterRequest {
// destination_address: [1u8; 32],
// };
// ClientRequestProcessor::register_new_client(
// req2,
// client_processing_data.clone(),
// );
//
// client_processing_data.lock().await;
//
// assert_eq!(1, registered_client_ledger.0.len())
// }
}
#[cfg(test)]
mod create_storage_dir {
use super::*;
use sphinx::route::DestinationAddressBytes;
#[test]
fn it_creates_a_correct_storage_directory() {
let client_address: DestinationAddressBytes = [1u8; 32];
let store_dir = Path::new("./foo/");
ClientRequestProcessor::create_storage_dir(client_address, store_dir).unwrap();
}
}
#[cfg(test)]
mod generating_new_auth_token {
use super::*;
#[test]
fn for_the_same_input_generates_the_same_auth_token() {
let data1 = vec![1u8; 55];
let data2 = vec![1u8; 55];
let key = Scalar::from_bytes_mod_order([1u8; 32]);
let token1 = ClientRequestProcessor::generate_new_auth_token(data1, key);
let token2 = ClientRequestProcessor::generate_new_auth_token(data2, key);
assert_eq!(token1, token2);
}
#[test]
fn for_different_inputs_generates_different_auth_tokens() {
let data1 = vec![1u8; 55];
let data2 = vec![2u8; 55];
let key = Scalar::from_bytes_mod_order([1u8; 32]);
let token1 = ClientRequestProcessor::generate_new_auth_token(data1, key);
let token2 = ClientRequestProcessor::generate_new_auth_token(data2, key);
assert_ne!(token1, token2);
let data1 = vec![1u8; 50];
let data2 = vec![2u8; 55];
let key = Scalar::from_bytes_mod_order([1u8; 32]);
let token1 = ClientRequestProcessor::generate_new_auth_token(data1, key);
let token2 = ClientRequestProcessor::generate_new_auth_token(data2, key);
assert_ne!(token1, token2);
}
}
+83
View File
@@ -0,0 +1,83 @@
use crate::provider::storage::StoreData;
use curve25519_dalek::scalar::Scalar;
use sphinx::{ProcessedPacket, SphinxPacket};
use std::path::PathBuf;
use std::sync::{Arc, RwLock};
// TODO: this will probably need to be moved elsewhere I imagine
// DUPLICATE WITH MIXNODE CODE!!!
#[derive(Debug)]
pub enum MixProcessingError {
SphinxRecoveryError,
ReceivedForwardHopError,
InvalidPayload,
NonMatchingRecipient,
FileIOFailure,
}
impl From<sphinx::ProcessingError> for MixProcessingError {
// for time being just have a single error instance for all possible results of sphinx::ProcessingError
fn from(_: sphinx::ProcessingError) -> Self {
use MixProcessingError::*;
SphinxRecoveryError
}
}
impl From<std::io::Error> for MixProcessingError {
fn from(_: std::io::Error) -> Self {
use MixProcessingError::*;
FileIOFailure
}
}
// ProcessingData defines all data required to correctly unwrap sphinx packets
#[derive(Debug, Clone)]
pub(crate) struct MixProcessingData {
secret_key: Scalar,
pub(crate) store_dir: PathBuf,
}
impl MixProcessingData {
pub(crate) fn new(secret_key: Scalar, store_dir: PathBuf) -> Self {
MixProcessingData {
secret_key,
store_dir,
}
}
pub(crate) fn add_arc_rwlock(self) -> Arc<RwLock<Self>> {
Arc::new(RwLock::new(self))
}
}
pub(crate) struct MixPacketProcessor(());
impl MixPacketProcessor {
pub fn process_sphinx_data_packet(
packet_data: &[u8],
processing_data: &RwLock<MixProcessingData>,
) -> Result<StoreData, MixProcessingError> {
let packet = SphinxPacket::from_bytes(packet_data.to_vec())?;
let read_processing_data = processing_data.read().unwrap();
let (client_address, client_surb_id, payload) =
match packet.process(read_processing_data.secret_key) {
ProcessedPacket::ProcessedPacketFinalHop(client_address, surb_id, payload) => {
(client_address, surb_id, payload)
}
_ => return Err(MixProcessingError::ReceivedForwardHopError),
};
// TODO: should provider try to be recovering plaintext? this would potentially make client retrieve messages of non-constant length,
// perhaps provider should be re-padding them on retrieval or storing full data?
let (payload_destination, message) = payload
.try_recover_destination_and_plaintext()
.ok_or_else(|| MixProcessingError::InvalidPayload)?;
if client_address != payload_destination {
return Err(MixProcessingError::NonMatchingRecipient);
}
Ok(StoreData::new(client_address, client_surb_id, message))
}
}
+321
View File
@@ -0,0 +1,321 @@
use crate::provider::client_handling::{ClientProcessingData, ClientRequestProcessor};
use crate::provider::mix_handling::{MixPacketProcessor, MixProcessingData};
use crate::provider::storage::ClientStorage;
use curve25519_dalek::montgomery::MontgomeryPoint;
use curve25519_dalek::scalar::Scalar;
use futures::io::Error;
use futures::lock::Mutex as FMutex;
use nym_client::clients::directory::presence::MixProviderClient;
use sfw_provider_requests::AuthToken;
use sphinx::route::DestinationAddressBytes;
use std::collections::HashMap;
use std::net::{Shutdown, SocketAddr};
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::RwLock;
use tokio::prelude::*;
use tokio::runtime::Runtime;
mod client_handling;
mod mix_handling;
pub mod presence;
mod storage;
// TODO: if we ever create config file, this should go there
const STORED_MESSAGE_FILENAME_LENGTH: usize = 16;
const MESSAGE_RETRIEVAL_LIMIT: usize = 5;
pub struct Config {
pub client_socket_address: SocketAddr,
pub directory_server: String,
pub mix_socket_address: SocketAddr,
pub public_key: MontgomeryPoint,
pub secret_key: Scalar,
pub store_dir: PathBuf,
}
impl Config {
pub fn public_key_string(public_key: MontgomeryPoint) -> String {
let key_bytes = public_key.to_bytes().to_vec();
base64::encode_config(&key_bytes, base64::URL_SAFE)
}
}
#[derive(Debug)]
pub enum ProviderError {
TcpListenerBindingError,
TcpListenerConnectionError,
TcpListenerUnexpectedEof,
TcpListenerUnknownError,
}
impl From<io::Error> for ProviderError {
fn from(err: Error) -> Self {
use ProviderError::*;
match err.kind() {
io::ErrorKind::ConnectionRefused => TcpListenerConnectionError,
io::ErrorKind::ConnectionReset => TcpListenerConnectionError,
io::ErrorKind::ConnectionAborted => TcpListenerConnectionError,
io::ErrorKind::NotConnected => TcpListenerConnectionError,
io::ErrorKind::AddrInUse => TcpListenerBindingError,
io::ErrorKind::AddrNotAvailable => TcpListenerBindingError,
io::ErrorKind::UnexpectedEof => TcpListenerUnexpectedEof,
_ => TcpListenerUnknownError,
}
}
}
#[derive(Debug)]
pub struct ClientLedger(HashMap<AuthToken, DestinationAddressBytes>);
impl ClientLedger {
fn new() -> Self {
ClientLedger(HashMap::new())
}
fn add_arc_futures_mutex(self) -> Arc<FMutex<Self>> {
Arc::new(FMutex::new(self))
}
fn has_token(&self, auth_token: AuthToken) -> bool {
return self.0.contains_key(&auth_token);
}
fn insert_token(
&mut self,
auth_token: AuthToken,
client_address: DestinationAddressBytes,
) -> Option<DestinationAddressBytes> {
self.0.insert(auth_token, client_address)
}
fn current_clients(&self) -> Vec<MixProviderClient> {
self.0
.iter()
.map(|(_, &v)| Config::public_key_string(MontgomeryPoint(v)))
.map(|pub_key| MixProviderClient { pub_key })
.collect()
}
#[allow(dead_code)]
fn load(_file: PathBuf) -> Self {
unimplemented!()
}
}
pub struct ServiceProvider {
directory_server: String,
mix_network_address: SocketAddr,
client_network_address: SocketAddr,
secret_key: Scalar,
public_key: MontgomeryPoint,
store_dir: PathBuf,
registered_clients_ledger: ClientLedger,
}
impl ServiceProvider {
pub fn new(config: &Config) -> Self {
ServiceProvider {
mix_network_address: config.mix_socket_address,
client_network_address: config.client_socket_address,
secret_key: config.secret_key,
public_key: config.public_key,
store_dir: PathBuf::from(config.store_dir.clone()),
// TODO: load initial ledger from file
registered_clients_ledger: ClientLedger::new(),
directory_server: config.directory_server.clone(),
}
}
async fn process_mixnet_socket_connection(
mut socket: tokio::net::TcpStream,
processing_data: Arc<RwLock<MixProcessingData>>,
) {
let mut buf = [0u8; sphinx::PACKET_SIZE];
// In a loop, read data from the socket and write the data back.
loop {
match socket.read(&mut buf).await {
// socket closed
Ok(n) if n == 0 => {
println!("Remote connection closed.");
return;
}
Ok(_) => {
let store_data = match MixPacketProcessor::process_sphinx_data_packet(
buf.as_ref(),
processing_data.as_ref(),
) {
Ok(sd) => sd,
Err(e) => {
eprintln!("failed to process sphinx packet; err = {:?}", e);
return;
}
};
ClientStorage::store_processed_data(
store_data,
processing_data.read().unwrap().store_dir.as_path(),
)
.unwrap_or_else(|e| {
eprintln!("failed to store processed sphinx message; err = {:?}", e);
return;
});
}
Err(e) => {
eprintln!("failed to read from socket; err = {:?}", e);
return;
}
};
// Write the some data back
if let Err(e) = socket.write_all(b"foomp").await {
eprintln!("failed to write reply to socket; err = {:?}", e);
return;
}
}
}
async fn send_response(mut socket: tokio::net::TcpStream, data: &[u8]) {
if let Err(e) = socket.write_all(data).await {
eprintln!("failed to write reply to socket; err = {:?}", e)
}
if let Err(e) = socket.shutdown(Shutdown::Write) {
eprintln!("failed to close write part of the socket; err = {:?}", e)
}
}
// TODO: FIGURE OUT HOW TO SET READ_DEADLINES IN TOKIO
async fn process_client_socket_connection(
mut socket: tokio::net::TcpStream,
processing_data: Arc<ClientProcessingData>,
) {
let mut buf = [0; 1024];
// TODO: restore the for loop once we go back to persistent tcp socket connection
let response = match socket.read(&mut buf).await {
// socket closed
Ok(n) if n == 0 => {
println!("Remote connection closed.");
Err(())
}
Ok(n) => {
match ClientRequestProcessor::process_client_request(
buf[..n].as_ref(),
processing_data,
)
.await
{
Err(e) => {
eprintln!("failed to process client request; err = {:?}", e);
Err(())
}
Ok(res) => Ok(res),
}
}
Err(e) => {
eprintln!("failed to read from socket; err = {:?}", e);
Err(())
}
};
if let Err(e) = socket.shutdown(Shutdown::Read) {
eprintln!("failed to close read part of the socket; err = {:?}", e)
}
match response {
Ok(res) => {
println!("should send this response! {:?}", res);
ServiceProvider::send_response(socket, &res).await;
}
_ => {
println!("we failed...");
ServiceProvider::send_response(socket, b"bad foomp").await;
}
}
}
async fn start_mixnet_listening(
address: SocketAddr,
secret_key: Scalar,
store_dir: PathBuf,
) -> Result<(), ProviderError> {
let mut listener = tokio::net::TcpListener::bind(address).await?;
let processing_data = MixProcessingData::new(secret_key, store_dir).add_arc_rwlock();
loop {
let (socket, _) = listener.accept().await?;
// do note that the underlying data is NOT copied here; arc is incremented and lock is shared
// (if I understand it all correctly)
let thread_processing_data = processing_data.clone();
tokio::spawn(async move {
ServiceProvider::process_mixnet_socket_connection(socket, thread_processing_data)
.await
});
}
}
async fn start_client_listening(
address: SocketAddr,
store_dir: PathBuf,
client_ledger: Arc<FMutex<ClientLedger>>,
secret_key: Scalar,
) -> Result<(), ProviderError> {
let mut listener = tokio::net::TcpListener::bind(address).await?;
let processing_data =
ClientProcessingData::new(store_dir, client_ledger, secret_key).add_arc();
loop {
let (socket, _) = listener.accept().await?;
// do note that the underlying data is NOT copied here; arc is incremented and lock is shared
// (if I understand it all correctly)
let thread_processing_data = processing_data.clone();
tokio::spawn(async move {
ServiceProvider::process_client_socket_connection(socket, thread_processing_data)
.await
});
}
}
// Note: this now consumes the provider
pub fn start(self) -> Result<(), Box<dyn std::error::Error>> {
// Create the runtime, probably later move it to Provider struct itself?
// TODO: figure out the difference between Runtime and Handle
let mut rt = Runtime::new()?;
// let mut h = rt.handle();
let initial_client_ledger = self.registered_clients_ledger;
let thread_shareable_ledger = initial_client_ledger.add_arc_futures_mutex();
let presence_notifier = presence::Notifier::new(
self.directory_server,
self.mix_network_address.clone(),
self.public_key,
thread_shareable_ledger.clone(),
);
let presence_future = rt.spawn(presence_notifier.run());
let mix_future = rt.spawn(ServiceProvider::start_mixnet_listening(
self.mix_network_address,
self.secret_key,
self.store_dir.clone(),
));
let client_future = rt.spawn(ServiceProvider::start_client_listening(
self.client_network_address,
self.store_dir.clone(),
thread_shareable_ledger,
self.secret_key,
));
// Spawn the root task
rt.block_on(async {
let future_results =
futures::future::join3(mix_future, client_future, presence_future).await;
assert!(future_results.0.is_ok() && future_results.1.is_ok());
});
// this line in theory should never be reached as the runtime should be permanently blocked on listeners
eprintln!("The server went kaput...");
Ok(())
}
}
+64
View File
@@ -0,0 +1,64 @@
use crate::provider::{ClientLedger, Config};
use curve25519_dalek::montgomery::MontgomeryPoint;
use futures::lock::Mutex as FMutex;
use nym_client::clients::directory;
use nym_client::clients::directory::presence::MixProviderPresence;
use nym_client::clients::directory::requests::presence_providers_post::PresenceMixProviderPoster;
use nym_client::clients::directory::DirectoryClient;
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
pub struct Notifier {
pub net_client: directory::Client,
client_ledger: Arc<FMutex<ClientLedger>>,
host: String,
pub_key: String,
}
impl Notifier {
pub fn new(
directory_server_address: String,
host: SocketAddr,
pub_key: MontgomeryPoint,
client_ledger: Arc<FMutex<ClientLedger>>,
) -> Notifier {
let directory_config = directory::Config {
base_url: directory_server_address,
};
let net_client = directory::Client::new(directory_config);
Notifier {
net_client,
host: host.to_string(),
pub_key: Config::public_key_string(pub_key),
client_ledger,
}
}
async fn make_presence(&self) -> MixProviderPresence {
let unlocked_ledger = self.client_ledger.lock().await;
MixProviderPresence {
host: self.host.clone(),
pub_key: self.pub_key.clone(),
registered_clients: unlocked_ledger.current_clients(),
}
}
pub fn notify(&self, presence: MixProviderPresence) {
self.net_client
.presence_providers_post
.post(&presence)
.unwrap();
}
pub async fn run(self) {
loop {
let presence = self.make_presence().await;
self.notify(presence);
let delay_duration = Duration::from_secs(5);
tokio::time::delay_for(delay_duration).await;
}
}
}
+129
View File
@@ -0,0 +1,129 @@
use crate::provider::{MESSAGE_RETRIEVAL_LIMIT, STORED_MESSAGE_FILENAME_LENGTH};
use rand::Rng;
use sfw_provider_requests::DUMMY_MESSAGE_CONTENT;
use sphinx::route::{DestinationAddressBytes, SURBIdentifier};
use std::fs::File;
use std::io;
use std::io::Write;
use std::path::{Path, PathBuf};
pub enum StoreError {
ClientDoesntExistError,
FileIOFailure,
}
impl From<std::io::Error> for StoreError {
fn from(_: std::io::Error) -> Self {
use StoreError::*;
FileIOFailure
}
}
pub struct StoreData {
client_address: DestinationAddressBytes,
#[allow(dead_code)]
client_surb_id: SURBIdentifier,
message: Vec<u8>,
}
impl StoreData {
pub(crate) fn new(
client_address: DestinationAddressBytes,
client_surb_id: SURBIdentifier,
message: Vec<u8>,
) -> Self {
StoreData {
client_address,
client_surb_id,
message,
}
}
}
// TODO: replace with database
pub struct ClientStorage(());
// TODO: change it to some generic implementation to inject fs
impl ClientStorage {
pub(crate) fn generate_random_file_name() -> String {
rand::thread_rng()
.sample_iter(&rand::distributions::Alphanumeric)
.take(STORED_MESSAGE_FILENAME_LENGTH)
.collect::<String>()
}
fn dummy_message() -> Vec<u8> {
// TODO: should it be padded to constant length?
DUMMY_MESSAGE_CONTENT.to_vec()
}
pub fn store_processed_data(store_data: StoreData, store_dir: &Path) -> io::Result<()> {
let client_dir_name = hex::encode(store_data.client_address);
let full_store_dir = store_dir.join(client_dir_name);
let full_store_path = full_store_dir.join(ClientStorage::generate_random_file_name());
println!(
"going to store: {:?} in file: {:?}",
store_data.message, full_store_path
);
println!(
"string message: {:?}",
std::str::from_utf8(&store_data.message)
);
// TODO: what to do with surbIDs??
// we can use normal io here, no need for tokio as it's all happening in one thread per connection
let mut file = File::create(full_store_path)?;
file.write_all(store_data.message.as_ref())?;
Ok(())
}
pub fn retrieve_client_files(
client_address: DestinationAddressBytes,
store_dir: &Path,
) -> Result<Vec<Vec<u8>>, StoreError> {
let client_dir_name = hex::encode(client_address);
let full_store_dir = store_dir.join(client_dir_name);
println!("going to lookup: {:?}!", full_store_dir);
if !full_store_dir.exists() {
return Err(StoreError::ClientDoesntExistError);
}
let msgs: Vec<_> = std::fs::read_dir(full_store_dir)?
.map(|entry| entry.unwrap())
.filter(|entry| {
let is_file = entry.metadata().unwrap().is_file();
if !is_file {
eprintln!(
"potentially corrupted client inbox! - found a non-file - {:?}",
entry.path()
);
}
is_file
})
.map(|entry| {
let content = std::fs::read(entry.path()).unwrap();
ClientStorage::delete_file(entry.path()).unwrap();
content
}) // TODO: THIS MAP IS UNSAFE (BOTH FOR READING AND DELETING)!! - in the case where there are multiple requests from the same client being processed in parallel
.chain(std::iter::repeat(ClientStorage::dummy_message()))
.take(MESSAGE_RETRIEVAL_LIMIT)
.collect();
println!("retrieved the following data: {:?}", msgs);
Ok(msgs)
}
// TODO: THIS NEEDS A LOCKING MECHANISM!!! (or a db layer on top - basically 'ClientStorage' on steroids)
// TODO 2: This should only be called AFTER we sent the reply. Because if client's connection failed after sending request
// the messages would be deleted but he wouldn't have received them
fn delete_file(path: PathBuf) -> io::Result<()> {
println!("Here {:?} will be deleted!", path);
std::fs::remove_file(path) // another argument for db layer -> remove_file is NOT guaranteed to immediately get rid of the file
}
}