Pārlūkot izejas kodu

darkwikid: main.rs improvements, and cleanups.

* Use dryoc for crypto_secretbox instead of public-key encryption.

* Detach raft task to background (this needs a good rework, current
  design is suboptimal).

* Remove ctrlc and add signal-hook to handle signals

* Upon SIGHUP, reload workspace secrets from the TOML config file.
Luther Blissett 3 gadi atpakaļ
vecāks
revīzija
e7bc898923

+ 190 - 91
Cargo.lock

@@ -33,7 +33,7 @@ version = "0.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
 checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
 dependencies = [
 dependencies = [
- "getrandom",
+ "getrandom 0.2.7",
  "once_cell",
  "once_cell",
  "version_check",
  "version_check",
 ]
 ]
@@ -346,9 +346,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
 
 
 [[package]]
 [[package]]
 name = "bincode"
 name = "bincode"
-version = "2.0.0-rc.1"
+version = "2.0.0-rc.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f609ceb2c41b0d0277314a789ef0e7eb14593d5485f7c67320bed3924ebb1b33"
+checksum = "7bb50c5a2ef4b9b1e7ae73e3a73b52ea24b20312d629f9c4df28260b7ad2c3c4"
 dependencies = [
 dependencies = [
  "bincode_derive",
  "bincode_derive",
  "serde",
  "serde",
@@ -356,9 +356,9 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "bincode_derive"
 name = "bincode_derive"
-version = "2.0.0-rc.1"
+version = "2.0.0-rc.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "913287a8f3e00db4c7ae1b87e9b9b8cebd6b89217eaadfc281fa5c897da35dc3"
+checksum = "0a45a23389446d2dd25dc8e73a7a3b3c43522b630cac068927f0649d43d719d2"
 dependencies = [
 dependencies = [
  "virtue",
  "virtue",
 ]
 ]
@@ -421,6 +421,15 @@ dependencies = [
  "digest 0.10.5",
  "digest 0.10.5",
 ]
 ]
 
 
+[[package]]
+name = "block-buffer"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
+dependencies = [
+ "generic-array",
+]
+
 [[package]]
 [[package]]
 name = "block-buffer"
 name = "block-buffer"
 version = "0.10.3"
 version = "0.10.3"
@@ -619,6 +628,16 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 
+[[package]]
+name = "chacha20"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed8738f14471a99f0e316c327e68fc82a3611cc2895fcb604b89eedaf8f39d95"
+dependencies = [
+ "cipher 0.2.5",
+ "rand_core 0.5.1",
+]
+
 [[package]]
 [[package]]
 name = "chacha20"
 name = "chacha20"
 version = "0.9.0"
 version = "0.9.0"
@@ -626,7 +645,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c7fc89c7c5b9e7a02dfe45cd2367bae382f9ed31c61ca8debe5f827c420a2f08"
 checksum = "c7fc89c7c5b9e7a02dfe45cd2367bae382f9ed31c61ca8debe5f827c420a2f08"
 dependencies = [
 dependencies = [
  "cfg-if 1.0.0",
  "cfg-if 1.0.0",
- "cipher",
+ "cipher 0.4.3",
  "cpufeatures",
  "cpufeatures",
 ]
 ]
 
 
@@ -637,8 +656,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
 checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
 dependencies = [
 dependencies = [
  "aead",
  "aead",
- "chacha20",
- "cipher",
+ "chacha20 0.9.0",
+ "cipher 0.4.3",
  "poly1305",
  "poly1305",
  "zeroize",
  "zeroize",
 ]
 ]
@@ -658,6 +677,15 @@ dependencies = [
  "winapi",
  "winapi",
 ]
 ]
 
 
+[[package]]
+name = "cipher"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801"
+dependencies = [
+ "generic-array",
+]
+
 [[package]]
 [[package]]
 name = "cipher"
 name = "cipher"
 version = "0.4.3"
 version = "0.4.3"
@@ -760,13 +788,13 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "console"
 name = "console"
-version = "0.15.1"
+version = "0.15.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89eab4d20ce20cea182308bca13088fecea9c05f6776cf287205d41a0ed3c847"
+checksum = "c050367d967ced717c04b65d8c619d863ef9292ce0c5760028655a2fb298718c"
 dependencies = [
 dependencies = [
  "encode_unicode 0.3.6",
  "encode_unicode 0.3.6",
+ "lazy_static",
  "libc",
  "libc",
- "once_cell",
  "terminal_size",
  "terminal_size",
  "unicode-width",
  "unicode-width",
  "winapi",
  "winapi",
@@ -965,15 +993,14 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "crossbeam-epoch"
 name = "crossbeam-epoch"
-version = "0.9.10"
+version = "0.9.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1"
+checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348"
 dependencies = [
 dependencies = [
  "autocfg",
  "autocfg",
  "cfg-if 1.0.0",
  "cfg-if 1.0.0",
  "crossbeam-utils",
  "crossbeam-utils",
  "memoffset",
  "memoffset",
- "once_cell",
  "scopeguard",
  "scopeguard",
 ]
 ]
 
 
@@ -989,12 +1016,11 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "crossbeam-utils"
 name = "crossbeam-utils"
-version = "0.8.11"
+version = "0.8.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"
+checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac"
 dependencies = [
 dependencies = [
  "cfg-if 1.0.0",
  "cfg-if 1.0.0",
- "once_cell",
 ]
 ]
 
 
 [[package]]
 [[package]]
@@ -1061,9 +1087,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8cbf5d97a01c39dad83e20de5398748bbbb0ffd307b9612cf0db74ab3c88d551"
 checksum = "8cbf5d97a01c39dad83e20de5398748bbbb0ffd307b9612cf0db74ab3c88d551"
 dependencies = [
 dependencies = [
  "aead",
  "aead",
- "chacha20",
+ "chacha20 0.9.0",
  "chacha20poly1305",
  "chacha20poly1305",
- "salsa20",
+ "salsa20 0.10.2",
  "x25519-dalek",
  "x25519-dalek",
  "xsalsa20poly1305",
  "xsalsa20poly1305",
  "zeroize",
  "zeroize",
@@ -1228,7 +1254,7 @@ dependencies = [
  "rustls-pemfile",
  "rustls-pemfile",
  "serde",
  "serde",
  "serde_json",
  "serde_json",
- "sha2",
+ "sha2 0.10.6",
  "simplelog",
  "simplelog",
  "sled",
  "sled",
  "smol",
  "smol",
@@ -1339,26 +1365,25 @@ dependencies = [
  "async-executor",
  "async-executor",
  "async-std",
  "async-std",
  "async-trait",
  "async-trait",
+ "blake3",
  "bs58",
  "bs58",
- "chrono",
- "colored",
- "crypto_box",
- "ctrlc",
  "darkfi",
  "darkfi",
+ "dryoc",
  "easy-parallel",
  "easy-parallel",
  "futures",
  "futures",
  "fxhash",
  "fxhash",
- "hex",
+ "lazy_static",
  "log",
  "log",
- "rand",
  "serde",
  "serde",
+ "serde_derive",
  "serde_json",
  "serde_json",
- "sha2",
+ "signal-hook",
+ "signal-hook-async-std",
  "simplelog",
  "simplelog",
  "smol",
  "smol",
  "structopt",
  "structopt",
  "structopt-toml",
  "structopt-toml",
- "thiserror",
+ "toml",
  "unicode-segmentation",
  "unicode-segmentation",
  "url",
  "url",
 ]
 ]
@@ -1375,12 +1400,12 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "darling"
 name = "darling"
-version = "0.13.4"
+version = "0.14.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
+checksum = "4529658bdda7fd6769b8614be250cdcfc3aeb0ee72fe66f9e41e5e5eb73eac02"
 dependencies = [
 dependencies = [
- "darling_core 0.13.4",
- "darling_macro 0.13.4",
+ "darling_core 0.14.1",
+ "darling_macro 0.14.1",
 ]
 ]
 
 
 [[package]]
 [[package]]
@@ -1399,9 +1424,9 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "darling_core"
 name = "darling_core"
-version = "0.13.4"
+version = "0.14.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
+checksum = "649c91bc01e8b1eac09fb91e8dbc7d517684ca6be8ebc75bb9cafc894f9fdb6f"
 dependencies = [
 dependencies = [
  "fnv",
  "fnv",
  "ident_case",
  "ident_case",
@@ -1423,11 +1448,11 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "darling_macro"
 name = "darling_macro"
-version = "0.13.4"
+version = "0.14.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
+checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5"
 dependencies = [
 dependencies = [
- "darling_core 0.13.4",
+ "darling_core 0.14.1",
  "quote",
  "quote",
  "syn",
  "syn",
 ]
 ]
@@ -1492,7 +1517,7 @@ version = "0.10.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c"
 checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c"
 dependencies = [
 dependencies = [
- "block-buffer",
+ "block-buffer 0.10.3",
  "crypto-common",
  "crypto-common",
  "subtle",
  "subtle",
 ]
 ]
@@ -1603,6 +1628,26 @@ dependencies = [
  "wee_alloc",
  "wee_alloc",
 ]
 ]
 
 
+[[package]]
+name = "dryoc"
+version = "0.3.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63e3117a596ee19e5e1b00c5a7bd39a083bf631b23d52fc960428556fb9ef5d8"
+dependencies = [
+ "bitflags",
+ "chacha20 0.6.0",
+ "curve25519-dalek",
+ "generic-array",
+ "lazy_static",
+ "libc",
+ "rand_core 0.5.1",
+ "salsa20 0.7.2",
+ "sha2 0.9.9",
+ "subtle",
+ "winapi",
+ "zeroize",
+]
+
 [[package]]
 [[package]]
 name = "dwrote"
 name = "dwrote"
 version = "0.11.0"
 version = "0.11.0"
@@ -1649,12 +1694,12 @@ checksum = "6907e25393cdcc1f4f3f513d9aac1e840eb1cc341a0fccb01171f7d14d10b946"
 
 
 [[package]]
 [[package]]
 name = "ed25519-compact"
 name = "ed25519-compact"
-version = "1.0.15"
+version = "1.0.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bee9df587982575886a8682edcee11877894349a805f25629c27f63abe3e9ae8"
+checksum = "e18997d4604542d0736fae2c5ad6de987f0a50530cbcc14a7ce5a685328a252d"
 dependencies = [
 dependencies = [
  "ct-codecs",
  "ct-codecs",
- "getrandom",
+ "getrandom 0.2.7",
 ]
 ]
 
 
 [[package]]
 [[package]]
@@ -1697,20 +1742,20 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "enumset"
 name = "enumset"
-version = "1.0.11"
+version = "1.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4799cdb24d48f1f8a7a98d06b7fde65a85a2d1e42b25a889f5406aa1fbefe074"
+checksum = "19be8061a06ab6f3a6cf21106c873578bf01bd42ad15e0311a9c76161cb1c753"
 dependencies = [
 dependencies = [
  "enumset_derive",
  "enumset_derive",
 ]
 ]
 
 
 [[package]]
 [[package]]
 name = "enumset_derive"
 name = "enumset_derive"
-version = "0.6.0"
+version = "0.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea83a3fbdc1d999ccfbcbee717eab36f8edf2d71693a23ce0d7cca19e085304c"
+checksum = "03e7b551eba279bf0fa88b83a46330168c1560a52a94f5126f892f0b364ab3e0"
 dependencies = [
 dependencies = [
- "darling 0.13.4",
+ "darling 0.14.1",
  "proc-macro2",
  "proc-macro2",
  "quote",
  "quote",
  "syn",
  "syn",
@@ -2113,6 +2158,17 @@ dependencies = [
  "version_check",
  "version_check",
 ]
 ]
 
 
+[[package]]
+name = "getrandom"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
+dependencies = [
+ "cfg-if 1.0.0",
+ "libc",
+ "wasi 0.9.0+wasi-snapshot-preview1",
+]
+
 [[package]]
 [[package]]
 name = "getrandom"
 name = "getrandom"
 version = "0.2.7"
 version = "0.2.7"
@@ -2327,9 +2383,9 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "image"
 name = "image"
-version = "0.24.3"
+version = "0.24.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e30ca2ecf7666107ff827a8e481de6a132a9b687ed3bb20bb1c144a36c00964"
+checksum = "bd8e4fb07cf672b1642304e731ef8a6a4c7891d67bb4fd4f5ce58cd6ed86803c"
 dependencies = [
 dependencies = [
  "bytemuck",
  "bytemuck",
  "byteorder",
  "byteorder",
@@ -2536,9 +2592,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
 
 
 [[package]]
 [[package]]
 name = "libc"
 name = "libc"
-version = "0.2.133"
+version = "0.2.134"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966"
+checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb"
 
 
 [[package]]
 [[package]]
 name = "libloading"
 name = "libloading"
@@ -2845,9 +2901,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
 
 
 [[package]]
 [[package]]
 name = "openssl"
 name = "openssl"
-version = "0.10.41"
+version = "0.10.42"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "618febf65336490dfcf20b73f885f5651a0c89c64c2d4a8c3662585a70bf5bd0"
+checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13"
 dependencies = [
 dependencies = [
  "bitflags",
  "bitflags",
  "cfg-if 1.0.0",
  "cfg-if 1.0.0",
@@ -2877,9 +2933,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
 
 
 [[package]]
 [[package]]
 name = "openssl-sys"
 name = "openssl-sys"
-version = "0.9.75"
+version = "0.9.76"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5f9bd0c2710541a3cda73d6f9ac4f1b240de4ae261065d309dbe73d9dceb42f"
+checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce"
 dependencies = [
 dependencies = [
  "autocfg",
  "autocfg",
  "cc",
  "cc",
@@ -3005,9 +3061,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
 
 
 [[package]]
 [[package]]
 name = "pest"
 name = "pest"
-version = "2.3.1"
+version = "2.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb779fcf4bb850fbbb0edc96ff6cf34fd90c4b1a112ce042653280d9a7364048"
+checksum = "dbc7bc69c062e492337d74d59b120c274fd3d261b6bf6d3207d499b4b379c41a"
 dependencies = [
 dependencies = [
  "thiserror",
  "thiserror",
  "ucd-trie",
  "ucd-trie",
@@ -3200,9 +3256,9 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "proc-macro2"
 name = "proc-macro2"
-version = "1.0.43"
+version = "1.0.46"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
+checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b"
 dependencies = [
 dependencies = [
  "unicode-ident",
  "unicode-ident",
 ]
 ]
@@ -3311,6 +3367,9 @@ name = "rand_core"
 version = "0.5.1"
 version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
 checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+dependencies = [
+ "getrandom 0.1.16",
+]
 
 
 [[package]]
 [[package]]
 name = "rand_core"
 name = "rand_core"
@@ -3318,7 +3377,7 @@ version = "0.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
 checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
 dependencies = [
 dependencies = [
- "getrandom",
+ "getrandom 0.2.7",
 ]
 ]
 
 
 [[package]]
 [[package]]
@@ -3362,7 +3421,7 @@ checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd"
 dependencies = [
 dependencies = [
  "pem",
  "pem",
  "ring",
  "ring",
- "time 0.3.14",
+ "time 0.3.15",
  "yasna",
  "yasna",
 ]
 ]
 
 
@@ -3390,7 +3449,7 @@ version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
 checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
 dependencies = [
 dependencies = [
- "getrandom",
+ "getrandom 0.2.7",
  "redox_syscall",
  "redox_syscall",
  "thiserror",
  "thiserror",
 ]
 ]
@@ -3574,13 +3633,22 @@ version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
 checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
 
 
+[[package]]
+name = "salsa20"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "399f290ffc409596022fce5ea5d4138184be4784f2b28c62c59f0d8389059a15"
+dependencies = [
+ "cipher 0.2.5",
+]
+
 [[package]]
 [[package]]
 name = "salsa20"
 name = "salsa20"
 version = "0.10.2"
 version = "0.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213"
 checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213"
 dependencies = [
 dependencies = [
- "cipher",
+ "cipher 0.4.3",
 ]
 ]
 
 
 [[package]]
 [[package]]
@@ -3725,6 +3793,19 @@ dependencies = [
  "digest 0.10.5",
  "digest 0.10.5",
 ]
 ]
 
 
+[[package]]
+name = "sha2"
+version = "0.9.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
+dependencies = [
+ "block-buffer 0.9.0",
+ "cfg-if 1.0.0",
+ "cpufeatures",
+ "digest 0.9.0",
+ "opaque-debug",
+]
+
 [[package]]
 [[package]]
 name = "sha2"
 name = "sha2"
 version = "0.10.6"
 version = "0.10.6"
@@ -3746,6 +3827,18 @@ dependencies = [
  "signal-hook-registry",
  "signal-hook-registry",
 ]
 ]
 
 
+[[package]]
+name = "signal-hook-async-std"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c4aa94397e2023af5b7cff5b8d4785e935cfb77f0e4aab0cae3b26258ace556"
+dependencies = [
+ "async-io",
+ "futures-lite",
+ "libc",
+ "signal-hook",
+]
+
 [[package]]
 [[package]]
 name = "signal-hook-mio"
 name = "signal-hook-mio"
 version = "0.2.3"
 version = "0.2.3"
@@ -3774,7 +3867,7 @@ checksum = "48dfff04aade74dd495b007c831cd6f4e0cee19c344dd9dc0884c0289b70a786"
 dependencies = [
 dependencies = [
  "log",
  "log",
  "termcolor",
  "termcolor",
- "time 0.3.14",
+ "time 0.3.15",
 ]
 ]
 
 
 [[package]]
 [[package]]
@@ -3819,9 +3912,9 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "smallvec"
 name = "smallvec"
-version = "1.9.0"
+version = "1.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
+checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
 
 
 [[package]]
 [[package]]
 name = "smawk"
 name = "smawk"
@@ -3926,7 +4019,7 @@ dependencies = [
  "once_cell",
  "once_cell",
  "paste",
  "paste",
  "percent-encoding",
  "percent-encoding",
- "sha2",
+ "sha2 0.10.6",
  "smallvec",
  "smallvec",
  "sqlformat",
  "sqlformat",
  "sqlx-rt",
  "sqlx-rt",
@@ -3947,7 +4040,7 @@ dependencies = [
  "once_cell",
  "once_cell",
  "proc-macro2",
  "proc-macro2",
  "quote",
  "quote",
- "sha2",
+ "sha2 0.10.6",
  "sqlx-core",
  "sqlx-core",
  "sqlx-rt",
  "sqlx-rt",
  "syn",
  "syn",
@@ -4065,9 +4158,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
 
 
 [[package]]
 [[package]]
 name = "syn"
 name = "syn"
-version = "1.0.100"
+version = "1.0.101"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e"
+checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2"
 dependencies = [
 dependencies = [
  "proc-macro2",
  "proc-macro2",
  "quote",
  "quote",
@@ -4246,18 +4339,18 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "thiserror"
 name = "thiserror"
-version = "1.0.35"
+version = "1.0.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c53f98874615aea268107765aa1ed8f6116782501d18e53d08b471733bea6c85"
+checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
 dependencies = [
 dependencies = [
  "thiserror-impl",
  "thiserror-impl",
 ]
 ]
 
 
 [[package]]
 [[package]]
 name = "thiserror-impl"
 name = "thiserror-impl"
-version = "1.0.35"
+version = "1.0.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783"
+checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
 dependencies = [
 dependencies = [
  "proc-macro2",
  "proc-macro2",
  "quote",
  "quote",
@@ -4277,9 +4370,9 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "time"
 name = "time"
-version = "0.3.14"
+version = "0.3.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c3f9a28b618c3a6b9251b6908e9c99e04b9e5c02e6581ccbb67d59c34ef7f9b"
+checksum = "d634a985c4d4238ec39cacaed2e7ae552fbd3c476b552c1deac3021b7d7eaf0c"
 dependencies = [
 dependencies = [
  "itoa 1.0.3",
  "itoa 1.0.3",
  "libc",
  "libc",
@@ -4319,9 +4412,9 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "tracing"
 name = "tracing"
-version = "0.1.36"
+version = "0.1.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307"
+checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
 dependencies = [
 dependencies = [
  "cfg-if 1.0.0",
  "cfg-if 1.0.0",
  "log",
  "log",
@@ -4332,9 +4425,9 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "tracing-attributes"
 name = "tracing-attributes"
-version = "0.1.22"
+version = "0.1.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2"
+checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
 dependencies = [
 dependencies = [
  "proc-macro2",
  "proc-macro2",
  "quote",
  "quote",
@@ -4343,9 +4436,9 @@ dependencies = [
 
 
 [[package]]
 [[package]]
 name = "tracing-core"
 name = "tracing-core"
-version = "0.1.29"
+version = "0.1.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7"
+checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
 dependencies = [
 dependencies = [
  "once_cell",
  "once_cell",
 ]
 ]
@@ -4436,9 +4529,9 @@ checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
 
 
 [[package]]
 [[package]]
 name = "unicode-linebreak"
 name = "unicode-linebreak"
-version = "0.1.3"
+version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba853b89cad55680dd3cf06f2798cb1ad8d483f0e2dfa14138d7e789ecee5c4e"
+checksum = "c5faade31a542b8b35855fff6e8def199853b2da8da256da52f52f1316ee3137"
 dependencies = [
 dependencies = [
  "hashbrown 0.12.3",
  "hashbrown 0.12.3",
  "regex",
  "regex",
@@ -4555,9 +4648,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
 
 
 [[package]]
 [[package]]
 name = "virtue"
 name = "virtue"
-version = "0.0.7"
+version = "0.0.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "757cfbfe0d17ee6f22fe97e536d463047d451b47cf9d11e2b7d1398b0ef274dd"
+checksum = "7b60dcd6a64dd45abf9bd426970c9843726da7fc08f44cd6fcebf68c21220a63"
 
 
 [[package]]
 [[package]]
 name = "wait-timeout"
 name = "wait-timeout"
@@ -4585,6 +4678,12 @@ dependencies = [
  "winapi-util",
  "winapi-util",
 ]
 ]
 
 
+[[package]]
+name = "wasi"
+version = "0.9.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
+
 [[package]]
 [[package]]
 name = "wasi"
 name = "wasi"
 version = "0.10.0+wasi-snapshot-preview1"
 version = "0.10.0+wasi-snapshot-preview1"
@@ -4665,9 +4764,9 @@ checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
 
 
 [[package]]
 [[package]]
 name = "wasm-encoder"
 name = "wasm-encoder"
-version = "0.17.0"
+version = "0.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e7ca71c70a6de5b10968ae4d298e548366d9cd9588176e6ff8866f3c49c96ee"
+checksum = "c64ac98d5d61192cc45c701b7e4bd0b9aff91e2edfc7a088406cfe2288581e2c"
 dependencies = [
 dependencies = [
  "leb128",
  "leb128",
 ]
 ]
@@ -4942,9 +5041,9 @@ checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a"
 
 
 [[package]]
 [[package]]
 name = "wast"
 name = "wast"
-version = "47.0.0"
+version = "47.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "117ccfc4262e62a28a13f0548a147f19ffe71e8a08be802af23ae4ea0bedad73"
+checksum = "02b98502f3978adea49551e801a6687678e6015317d7d9470a67fe813393f2a8"
 dependencies = [
 dependencies = [
  "leb128",
  "leb128",
  "memchr",
  "memchr",
@@ -5173,7 +5272,7 @@ checksum = "472c385ee974833d7e59979eeb74175d56774be3768b5bcc581337e21396bda3"
 dependencies = [
 dependencies = [
  "aead",
  "aead",
  "poly1305",
  "poly1305",
- "salsa20",
+ "salsa20 0.10.2",
  "subtle",
  "subtle",
  "zeroize",
  "zeroize",
 ]
 ]
@@ -5184,7 +5283,7 @@ version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "346d34a236c9d3e5f3b9b74563f238f955bbd05fa0b8b4efa53c130c43982f4c"
 checksum = "346d34a236c9d3e5f3b9b74563f238f955bbd05fa0b8b4efa53c130c43982f4c"
 dependencies = [
 dependencies = [
- "time 0.3.14",
+ "time 0.3.15",
 ]
 ]
 
 
 [[package]]
 [[package]]

+ 0 - 2
bin/darkwiki/darkwikid/.gitignore

@@ -1,2 +0,0 @@
-/target
-Cargo.lock

+ 20 - 34
bin/darkwiki/darkwikid/Cargo.toml

@@ -1,50 +1,36 @@
 [package]
 [package]
 name = "darkwikid"
 name = "darkwikid"
-description = ""
 version = "0.4.0"
 version = "0.4.0"
-edition = "2021"
-authors = ["darkfi <dev@dark.fi>"]
-license = "AGPL-3.0-only"
 homepage = "https://dark.fi"
 homepage = "https://dark.fi"
+description = "Reference daemon for a decentralized P2P wiki"
+authors = ["darkfi <dev@dark.fi>"]
 repository = "https://github.com/darkrenaissance/darkfi"
 repository = "https://github.com/darkrenaissance/darkfi"
-keywords = []
-categories = []
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+license = "AGPL-3.0-only"
+edition = "2021"
 
 
 [dependencies]
 [dependencies]
-darkfi = {path = "../../../", features = ["raft", "net", "rpc", "bs58"]}
-
-
-# Async
-smol = "1.2.5"
-async-std = {version = "1.12.0", features = ["attributes"]}
-async-trait = "0.1.57"
 async-channel = "1.7.1"
 async-channel = "1.7.1"
 async-executor = "1.4.1"
 async-executor = "1.4.1"
+async-std = "1.12.0"
+async-trait = "0.1.57"
+blake3 = "1.3.1"
+bs58 = "0.4.0"
+darkfi = {path = "../../../", features = ["raft", "rpc", "util"]}
+dryoc = "0.3.14"
 easy-parallel = "3.2.0"
 easy-parallel = "3.2.0"
 futures = "0.3.24"
 futures = "0.3.24"
-
-# Misc
-log = "0.4.17"
-simplelog = "0.12.0"
-rand = "0.8.5"
-chrono = "0.4.22"
-thiserror = "1.0.35"
-ctrlc = { version = "3.2.3", features = ["termination"] }
-url = "2.3.1"
 fxhash = "0.2.1"
 fxhash = "0.2.1"
-colored = "2.0.0"
-
-# Encoding and parsing
-serde = {version = "1.0.145", features = ["derive"]}
+lazy_static = "1.4.0"
+log = "0.4.17"
+serde = "1.0.145"
+serde_derive = "1.0.145"
 serde_json = "1.0.85"
 serde_json = "1.0.85"
+signal-hook-async-std = "0.2.2"
+signal-hook = "0.3.14"
+simplelog = "0.12.0"
 structopt = "0.3.26"
 structopt = "0.3.26"
 structopt-toml = "0.5.1"
 structopt-toml = "0.5.1"
+smol = "1.2.5"
+toml = "0.5.9"
 unicode-segmentation = "1.10.0"
 unicode-segmentation = "1.10.0"
-crypto_box = {version = "0.8.1", features = ["std"]}
-hex = "0.4.3"
-bs58 = "0.4.0"
-sha2 = "0.10.6"
-
-
+url = "2.3.1"

+ 0 - 0
bin/darkwiki/darkwiki.toml → bin/darkwiki/darkwikid/darkwikid_config.toml


+ 1 - 1
bin/darkwiki/darkwikid/src/lcs.rs

@@ -1,4 +1,4 @@
-use crate::{patch::OpMethod, str_to_chars};
+use crate::{patch::OpMethod, util::str_to_chars};
 
 
 pub struct Lcs<'a> {
 pub struct Lcs<'a> {
     a: Vec<&'a str>,
     a: Vec<&'a str>,

+ 292 - 333
bin/darkwiki/darkwikid/src/main.rs

@@ -1,200 +1,107 @@
 use std::{
 use std::{
-    fs::{create_dir_all, read_dir, remove_dir_all, remove_file},
+    fs::{create_dir_all, read_dir, remove_file},
     io::stdin,
     io::stdin,
     path::{Path, PathBuf},
     path::{Path, PathBuf},
+    process::exit,
 };
 };
 
 
 use async_executor::Executor;
 use async_executor::Executor;
-use async_std::sync::{Arc, Mutex};
-use crypto_box::{
-    aead::{Aead, AeadCore},
-    rand_core::OsRng,
-    SalsaBox, SecretKey,
+use async_std::{
+    stream::StreamExt,
+    sync::{Arc, Mutex, RwLock},
+    task,
 };
 };
+use dryoc::classic::crypto_secretbox::{crypto_secretbox_keygen, Key};
 use futures::{select, FutureExt};
 use futures::{select, FutureExt};
 use fxhash::FxHashMap;
 use fxhash::FxHashMap;
-use log::{error, info, warn};
-use serde::Deserialize;
-use sha2::Digest;
+use lazy_static::lazy_static;
+use log::{debug, error, info, warn};
+use signal_hook::consts::{SIGHUP, SIGINT, SIGQUIT, SIGTERM};
+use signal_hook_async_std::Signals;
 use smol::future;
 use smol::future;
-use structopt::StructOpt;
-use structopt_toml::StructOptToml;
-use unicode_segmentation::UnicodeSegmentation;
+use structopt_toml::{serde::Deserialize, structopt::StructOpt, StructOptToml};
 use url::Url;
 use url::Url;
 
 
 use darkfi::{
 use darkfi::{
-    async_daemonize,
-    net::{self, settings::SettingsOpt},
+    async_daemonize, cli_desc, net,
     raft::{NetMsg, ProtocolRaft, Raft, RaftSettings},
     raft::{NetMsg, ProtocolRaft, Raft, RaftSettings},
     rpc::server::listen_and_serve,
     rpc::server::listen_and_serve,
-    serial::{deserialize, serialize, SerialDecodable, SerialEncodable},
     util::{
     util::{
         cli::{get_log_config, get_log_level, spawn_config},
         cli::{get_log_config, get_log_level, spawn_config},
         file::{load_file, load_json_file, save_file, save_json_file},
         file::{load_file, load_json_file, save_file, save_json_file},
         path::{expand_path, get_config_path},
         path::{expand_path, get_config_path},
     },
     },
-    Error, Result,
+    Result,
 };
 };
 
 
 mod jsonrpc;
 mod jsonrpc;
-mod lcs;
-mod patch;
-
 use jsonrpc::JsonRpcInterface;
 use jsonrpc::JsonRpcInterface;
+mod lcs;
 use lcs::Lcs;
 use lcs::Lcs;
-use patch::{OpMethod, Patch};
+mod patch;
+use patch::{EncryptedPatch, OpMethod, Patch};
+mod util;
+use util::{decrypt_patch, encrypt_patch, get_docs_paths, parse_workspaces, path_to_id};
 
 
 type Patches = (Vec<Patch>, Vec<Patch>, Vec<Patch>, Vec<Patch>);
 type Patches = (Vec<Patch>, Vec<Patch>, Vec<Patch>, Vec<Patch>);
 
 
-pub const CONFIG_FILE: &str = "darkwiki.toml";
-pub const CONFIG_FILE_CONTENTS: &str = include_str!("../../darkwiki.toml");
-
-/// darkwikid cli
-#[derive(Clone, Debug, Deserialize, StructOpt, StructOptToml)]
-#[serde(default)]
-#[structopt(name = "darkwikid")]
-pub struct Args {
-    /// Sets a custom config file
-    #[structopt(long)]
-    pub config: Option<String>,
-    /// Sets Docs Path
-    #[structopt(long, default_value = "~/darkwiki")]
-    pub docs: String,
-    /// Sets Author Name for Patch
-    #[structopt(long, default_value = "NONE")]
-    pub author: String,
-    /// Secret Key To Encrypt/Decrypt Patches
-    #[structopt(long)]
-    pub workspaces: Vec<String>,
-    /// Generate A New Secret Key
-    #[structopt(long)]
-    pub generate: bool,
-    ///  Clean all the local data in docs path
-    /// (BE CAREFULL) Check the docs path in the config file before running this
-    #[structopt(long)]
-    pub refresh: bool,
-    /// JSON-RPC Listen URL
-    #[structopt(long = "rpc", default_value = "tcp://127.0.0.1:24330")]
-    pub rpc_listen: Url,
-    #[structopt(flatten)]
-    pub net: SettingsOpt,
-    /// Increase Verbosity
-    #[structopt(short, parse(from_occurrences))]
-    pub verbose: u8,
+lazy_static! {
+    /// This is where we hold our workspaces, so we are also able to refresh them on SIGHUP.
+    static ref WORKSPACES: RwLock<FxHashMap<String, Key>> = RwLock::new(FxHashMap::default());
 }
 }
 
 
-#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
-pub struct EncryptedPatch {
-    nonce: Vec<u8>,
-    payload: Vec<u8>,
-}
+pub const CONFIG_FILE: &str = "darkwikid_config.toml";
+pub const CONFIG_FILE_CONTENTS: &str = include_str!("../darkwikid_config.toml");
 
 
-fn get_workspaces(settings: &Args, docs_path: &Path) -> Result<FxHashMap<String, SalsaBox>> {
-    let mut workspaces = FxHashMap::default();
+const SYNC_ID_PATH: &str = "sync";
+const LOCAL_ID_PATH: &str = "local";
 
 
-    for workspace in settings.workspaces.iter() {
-        let workspace: Vec<&str> = workspace.split(':').collect();
-        let (workspace, secret) = (workspace[0], workspace[1]);
+#[derive(Clone, Debug, Deserialize, StructOpt, StructOptToml)]
+#[serde(default)]
+#[structopt(name = "darkwikid", about = cli_desc!())]
+struct Args {
+    /// Increase verbosity (-vvv supported)
+    #[structopt(short, parse(from_occurrences))]
+    verbose: u8,
 
 
-        let bytes: [u8; 32] = bs58::decode(secret)
-            .into_vec()?
-            .try_into()
-            .map_err(|_| Error::ParseFailed("Parse secret key failed"))?;
+    /// Configuration file to use
+    #[structopt(short, long)]
+    config: Option<String>,
 
 
-        let secret = crypto_box::SecretKey::from(bytes);
-        let public = secret.public_key();
-        let salsa_box = crypto_box::SalsaBox::new(&public, &secret);
-        workspaces.insert(workspace.to_string(), salsa_box);
-        create_dir_all(docs_path.join(workspace))?;
-    }
+    /// Workspace configuration (repeatable flag)
+    #[structopt(short, long)]
+    workspace: Vec<String>,
 
 
-    Ok(workspaces)
-}
+    /// Path where to store wiki's files
+    #[structopt(short, long, default_value = "~/darkwiki")]
+    docs: String,
 
 
-fn encrypt_patch(
-    patch: &Patch,
-    salsa_box: &SalsaBox,
-    rng: &mut crypto_box::rand_core::OsRng,
-) -> Result<EncryptedPatch> {
-    let nonce = SalsaBox::generate_nonce(rng);
-    let payload = &serialize(patch)[..];
-    let payload = salsa_box
-        .encrypt(&nonce, payload)
-        .map_err(|_| Error::ParseFailed("Encrypting Patch failed"))?;
-
-    let nonce = nonce.to_vec();
-    Ok(EncryptedPatch { nonce, payload })
-}
+    /// Sets author's name for patches
+    #[structopt(long, default_value = "Anonymous")]
+    author: String,
 
 
-fn decrypt_patch(encrypt_patch: &EncryptedPatch, salsa_box: &SalsaBox) -> Result<Patch> {
-    let nonce = encrypt_patch.nonce.as_slice();
-    let decrypted_patch = salsa_box
-        .decrypt(nonce.into(), &encrypt_patch.payload[..])
-        .map_err(|_| Error::ParseFailed("Decrypting Patch failed"))?;
+    /// Generate a new secret for a workspace
+    #[structopt(long)]
+    gen_secret: bool,
 
 
-    let patch = deserialize(&decrypted_patch)?;
+    /// JSON-RPC listen URL
+    #[structopt(long, default_value = "tcp://localhost:24330")]
+    rpc_listen: Url,
 
 
-    Ok(patch)
+    /// Network settings
+    #[structopt(flatten)]
+    net: net::settings::SettingsOpt,
 }
 }
 
 
-pub struct DarkWikiSettings {
+/// Settings struct used to hold some metadata for DarkWiki
+struct DarkWikiSettings {
     author: String,
     author: String,
     docs_path: PathBuf,
     docs_path: PathBuf,
-    datastore_path: PathBuf,
-}
-
-fn str_to_chars(s: &str) -> Vec<&str> {
-    s.graphemes(true).collect::<Vec<&str>>()
-}
-
-fn path_to_id(path: &str, workspace: &str) -> String {
-    let mut hasher = sha2::Sha256::new();
-    hasher.update(&format!("{}{}", path, workspace));
-    bs58::encode(hex::encode(hasher.finalize())).into_string()
-}
-
-fn get_docs_paths(files: &mut Vec<PathBuf>, path: &Path, parent: Option<&Path>) -> Result<()> {
-    let docs = read_dir(&path)?;
-    let docs = docs.filter(|d| d.is_ok()).map(|d| d.unwrap().path()).collect::<Vec<PathBuf>>();
-
-    for doc in docs {
-        if let Some(f) = doc.file_name() {
-            let file_name = PathBuf::from(f);
-            let file_name =
-                if let Some(parent) = parent { parent.join(file_name) } else { file_name };
-            if doc.is_file() {
-                if let Some(ext) = doc.extension() {
-                    if ext == "md" {
-                        files.push(file_name);
-                    }
-                }
-            } else if doc.is_dir() {
-                if f == ".log" {
-                    continue
-                }
-                get_docs_paths(files, &doc, Some(&file_name))?;
-            }
-        }
-    }
-
-    Ok(())
+    store_path: PathBuf,
 }
 }
 
 
-fn is_delete_patch(patch: &Patch) -> bool {
-    if patch.ops().0.len() != 1 {
-        return false
-    }
-
-    if let OpMethod::Delete(d) = patch.ops().0[0] {
-        if patch.base.len() as u64 == d {
-            return true
-        }
-    }
-
-    false
-}
-
-struct Darkwiki {
+/// DarkWiki object
+struct DarkWiki {
     settings: DarkWikiSettings,
     settings: DarkWikiSettings,
     #[allow(clippy::type_complexity)]
     #[allow(clippy::type_complexity)]
     rpc: (
     rpc: (
@@ -202,43 +109,69 @@ struct Darkwiki {
         async_channel::Receiver<(String, bool, Vec<String>)>,
         async_channel::Receiver<(String, bool, Vec<String>)>,
     ),
     ),
     raft: (async_channel::Sender<EncryptedPatch>, async_channel::Receiver<EncryptedPatch>),
     raft: (async_channel::Sender<EncryptedPatch>, async_channel::Receiver<EncryptedPatch>),
-    workspaces: FxHashMap<String, SalsaBox>,
 }
 }
 
 
-impl Darkwiki {
+impl DarkWiki {
     async fn start(&self) -> Result<()> {
     async fn start(&self) -> Result<()> {
-        let mut rng = crypto_box::rand_core::OsRng;
         loop {
         loop {
             select! {
             select! {
                 val = self.rpc.1.recv().fuse() => {
                 val = self.rpc.1.recv().fuse() => {
-                    let (cmd, dry, files) = val?;
+                    let (cmd, dry, files) = match val {
+                        Ok(v) => v,
+                        Err(e) => {
+                            error!("Failed unwrapping val received from RPC: {}", e);
+                            continue
+                        }
+                    };
+
                     match cmd.as_str() {
                     match cmd.as_str() {
                         "update" => {
                         "update" => {
-                            self.on_receive_update(dry, files, &mut rng).await?;
-                        },
+                            if let Err(e) = self.on_receive_update(dry, files).await {
+                                error!("on_receive_update returned error: {}", e);
+                                continue
+                            }
+                        }
+
                         "restore" => {
                         "restore" => {
-                            self.on_receive_restore(dry, files).await?;
-                        },
-                        _ => {}
+                            if let Err(e) = self.on_receive_restore(dry, files).await {
+                                error!("on_receive_restore returned error: {}", e);
+                                continue
+                            }
+                        }
+
+                        x => {
+                            warn!("Received unsupported command: {}", x);
+                            continue
+                        }
                     }
                     }
                 }
                 }
+
                 patch = self.raft.1.recv().fuse() => {
                 patch = self.raft.1.recv().fuse() => {
-                    for (workspace, salsa_box) in self.workspaces.iter() {
-                        if let Ok(mut patch) = decrypt_patch(&patch.clone()?, salsa_box) {
+                    let patch = match patch {
+                        Ok(v) => v,
+                        Err(e) => {
+                            error!("Failed unwrapping patch received from raft: {}", e);
+                            continue
+                        }
+                    };
+
+                    for (workspace, key) in WORKSPACES.read().await.iter() {
+                        if let Ok(mut patch) = decrypt_patch(&patch, key) {
                             info!("[{}] Receive a {:?}", workspace, patch);
                             info!("[{}] Receive a {:?}", workspace, patch);
                             patch.workspace = workspace.clone();
                             patch.workspace = workspace.clone();
-                            self.on_receive_patch(&patch)?;
+                            if let Err(e) = self.on_receive_patch(&patch) {
+                                error!("on_receive_patch returned error: {}", e);
+                            }
                         }
                         }
                     }
                     }
                 }
                 }
-
             }
             }
         }
         }
     }
     }
 
 
     fn on_receive_patch(&self, received_patch: &Patch) -> Result<()> {
     fn on_receive_patch(&self, received_patch: &Patch) -> Result<()> {
-        let sync_id_path = self.settings.datastore_path.join("sync").join(&received_patch.id);
-        let local_id_path = self.settings.datastore_path.join("local").join(&received_patch.id);
+        let sync_id_path = self.settings.store_path.join(SYNC_ID_PATH).join(&received_patch.id);
+        let local_id_path = self.settings.store_path.join(LOCAL_ID_PATH).join(&received_patch.id);
 
 
         if let Ok(mut sync_patch) = load_json_file::<Patch>(&sync_id_path) {
         if let Ok(mut sync_patch) = load_json_file::<Patch>(&sync_id_path) {
             if sync_patch.timestamp == received_patch.timestamp {
             if sync_patch.timestamp == received_patch.timestamp {
@@ -252,8 +185,6 @@ impl Darkwiki {
                 } else {
                 } else {
                     sync_patch.extend_ops(received_patch.ops());
                     sync_patch.extend_ops(received_patch.ops());
                 }
                 }
-            } else {
-                sync_patch.extend_ops(received_patch.ops());
             }
             }
 
 
             sync_patch.timestamp = received_patch.timestamp;
             sync_patch.timestamp = received_patch.timestamp;
@@ -266,17 +197,10 @@ impl Darkwiki {
         Ok(())
         Ok(())
     }
     }
 
 
-    async fn on_receive_update(
-        &self,
-        dry: bool,
-        files: Vec<String>,
-        rng: &mut OsRng,
-    ) -> Result<()> {
-        let mut local: Vec<Patch> = vec![];
-        let mut sync: Vec<Patch> = vec![];
-        let mut merge: Vec<Patch> = vec![];
-
-        for (workspace, salsa_box) in self.workspaces.iter() {
+    async fn on_receive_update(&self, dry: bool, files: Vec<String>) -> Result<()> {
+        let (mut local, mut sync, mut merge) = (vec![], vec![], vec![]);
+
+        for (workspace, key) in WORKSPACES.read().await.iter() {
             let (patches, l, s, m) = self.update(
             let (patches, l, s, m) = self.update(
                 dry,
                 dry,
                 &self.settings.docs_path.join(workspace),
                 &self.settings.docs_path.join(workspace),
@@ -291,32 +215,29 @@ impl Darkwiki {
             if !dry {
             if !dry {
                 for patch in patches {
                 for patch in patches {
                     info!("Send a {:?}", patch);
                     info!("Send a {:?}", patch);
-                    let encrypt_patch = encrypt_patch(&patch, salsa_box, rng)?;
+                    let encrypt_patch = encrypt_patch(&patch, key)?;
                     self.raft.0.send(encrypt_patch).await?;
                     self.raft.0.send(encrypt_patch).await?;
                 }
                 }
             }
             }
         }
         }
 
 
         self.rpc.0.send(vec![local, sync, merge]).await?;
         self.rpc.0.send(vec![local, sync, merge]).await?;
-
         Ok(())
         Ok(())
     }
     }
 
 
-    async fn on_receive_restore(&self, dry: bool, files_name: Vec<String>) -> Result<()> {
-        let mut patches: Vec<Patch> = vec![];
+    async fn on_receive_restore(&self, dry: bool, filenames: Vec<String>) -> Result<()> {
+        let mut patches = vec![];
 
 
-        for (workspace, _) in self.workspaces.iter() {
-            let ps = self.restore(
+        for (workspace, _) in WORKSPACES.read().await.iter() {
+            patches.extend(self.restore(
                 dry,
                 dry,
                 &self.settings.docs_path.join(workspace),
                 &self.settings.docs_path.join(workspace),
-                &files_name,
+                &filenames,
                 workspace,
                 workspace,
-            )?;
-            patches.extend(ps);
+            )?);
         }
         }
 
 
         self.rpc.0.send(vec![patches]).await?;
         self.rpc.0.send(vec![patches]).await?;
-
         Ok(())
         Ok(())
     }
     }
 
 
@@ -324,11 +245,10 @@ impl Darkwiki {
         &self,
         &self,
         dry: bool,
         dry: bool,
         docs_path: &Path,
         docs_path: &Path,
-        files_name: &[String],
+        filenames: &[String],
         workspace: &str,
         workspace: &str,
     ) -> Result<Vec<Patch>> {
     ) -> Result<Vec<Patch>> {
-        let local_path = self.settings.datastore_path.join("local");
-
+        let local_path = self.settings.store_path.join(LOCAL_ID_PATH);
         let mut patches = vec![];
         let mut patches = vec![];
 
 
         let local_files = read_dir(&local_path)?;
         let local_files = read_dir(&local_path)?;
@@ -341,7 +261,8 @@ impl Darkwiki {
                 continue
                 continue
             }
             }
 
 
-            if !files_name.is_empty() && !files_name.contains(&local_patch.path.to_string()) {
+            // TODO: FIXME: Simplify this logic, what is this? Add comments.
+            if !filenames.is_empty() && !filenames.contains(&local_patch.path.to_string()) {
                 continue
                 continue
             }
             }
 
 
@@ -361,72 +282,70 @@ impl Darkwiki {
         Ok(patches)
         Ok(patches)
     }
     }
 
 
+    // TODO: Add debug/info statements and refactor this function, there's too many things going on here.
     fn update(
     fn update(
         &self,
         &self,
         dry: bool,
         dry: bool,
         docs_path: &Path,
         docs_path: &Path,
-        files_name: Vec<String>,
+        filenames: Vec<String>,
         workspace: &str,
         workspace: &str,
     ) -> Result<Patches> {
     ) -> Result<Patches> {
-        let mut patches: Vec<Patch> = vec![];
-        let mut local_patches: Vec<Patch> = vec![];
-        let mut sync_patches: Vec<Patch> = vec![];
-        let mut merge_patches: Vec<Patch> = vec![];
+        let (mut patches, mut local_patches, mut sync_patches, mut merge_patches) =
+            (vec![], vec![], vec![], vec![]);
 
 
-        let local_path = self.settings.datastore_path.join("local");
-        let sync_path = self.settings.datastore_path.join("sync");
+        let local_path = self.settings.store_path.join(LOCAL_ID_PATH);
+        let sync_path = self.settings.store_path.join(SYNC_ID_PATH);
 
 
-        // save and compare docs in darkwiki and local dirs
-        // then merged with sync patches if any received
+        // Save and compare docs in darkwiki and local dirs, then
+        // merge with sync patches if any have been received.
         let mut docs = vec![];
         let mut docs = vec![];
         get_docs_paths(&mut docs, docs_path, None)?;
         get_docs_paths(&mut docs, docs_path, None)?;
         for doc in docs {
         for doc in docs {
             let doc_path = doc.to_str().unwrap();
             let doc_path = doc.to_str().unwrap();
 
 
-            if !files_name.is_empty() && !files_name.contains(&doc_path.to_string()) {
+            // FIXME: IDGI
+            if !filenames.is_empty() && !filenames.contains(&doc_path.to_string()) {
                 continue
                 continue
             }
             }
 
 
-            // load doc content
+            // Load doc content
             let edit = load_file(&docs_path.join(doc_path))?;
             let edit = load_file(&docs_path.join(doc_path))?;
-
             if edit.is_empty() {
             if edit.is_empty() {
                 continue
                 continue
             }
             }
 
 
             let doc_id = path_to_id(doc_path, workspace);
             let doc_id = path_to_id(doc_path, workspace);
 
 
-            // create new patch
+            // Create new patch
             let mut new_patch = Patch::new(doc_path, &doc_id, &self.settings.author, workspace);
             let mut new_patch = Patch::new(doc_path, &doc_id, &self.settings.author, workspace);
 
 
-            // check for any changes found with local doc and darkwiki doc
+            // Check for any changes found with local doc and darkwiki doc
             if let Ok(local_patch) = load_json_file::<Patch>(&local_path.join(&doc_id)) {
             if let Ok(local_patch) = load_json_file::<Patch>(&local_path.join(&doc_id)) {
-                // no changes found
+                // No changes found
                 if local_patch.to_string() == edit {
                 if local_patch.to_string() == edit {
                     continue
                     continue
                 }
                 }
 
 
-                // check the differences with LCS algorithm
+                // Check the differences with LCS algorithm
                 let local_patch_str = local_patch.to_string();
                 let local_patch_str = local_patch.to_string();
                 let lcs = Lcs::new(&local_patch_str, &edit);
                 let lcs = Lcs::new(&local_patch_str, &edit);
                 let lcs_ops = lcs.ops();
                 let lcs_ops = lcs.ops();
 
 
-                // add the change ops to the new patch
+                // Add the change ops to the new patch
                 for op in lcs_ops {
                 for op in lcs_ops {
                     new_patch.add_op(&op);
                     new_patch.add_op(&op);
                 }
                 }
 
 
                 new_patch.base = local_patch.to_string();
                 new_patch.base = local_patch.to_string();
-
                 local_patches.push(new_patch.clone());
                 local_patches.push(new_patch.clone());
 
 
                 let mut b_patch = new_patch.clone();
                 let mut b_patch = new_patch.clone();
                 b_patch.base = "".to_string();
                 b_patch.base = "".to_string();
                 patches.push(b_patch);
                 patches.push(b_patch);
 
 
-                // check if the same doc has received patch from the network
+                // Check if the same doc has received a patch from the network
                 if let Ok(sync_patch) = load_json_file::<Patch>(&sync_path.join(&doc_id)) {
                 if let Ok(sync_patch) = load_json_file::<Patch>(&sync_path.join(&doc_id)) {
-                    if !is_delete_patch(&sync_patch) {
+                    if !Self::is_delete_patch(&sync_patch) {
                         if sync_patch.timestamp != local_patch.timestamp {
                         if sync_patch.timestamp != local_patch.timestamp {
                             sync_patches.push(sync_patch.clone());
                             sync_patches.push(sync_patch.clone());
 
 
@@ -450,12 +369,12 @@ impl Darkwiki {
 
 
             if !dry {
             if !dry {
                 save_json_file(&local_path.join(&doc_id), &new_patch)?;
                 save_json_file(&local_path.join(&doc_id), &new_patch)?;
-                save_json_file(&sync_path.join(doc_id), &new_patch)?;
+                save_json_file(&sync_path.join(&doc_id), &new_patch)?;
             }
             }
         }
         }
 
 
-        // check if a new patch received
-        // and save the new changes in both local and darkwiki dirs
+        // Check if a new patch is received and save the new changes
+        // in both local and darkwiki dirs.
         let sync_files = read_dir(&sync_path)?;
         let sync_files = read_dir(&sync_path)?;
         for file in sync_files {
         for file in sync_files {
             let file_id = file?.file_name();
             let file_id = file?.file_name();
@@ -466,15 +385,15 @@ impl Darkwiki {
                 continue
                 continue
             }
             }
 
 
-            if is_delete_patch(&sync_patch) {
+            if Self::is_delete_patch(&sync_patch) {
                 if local_path.join(&sync_patch.id).exists() {
                 if local_path.join(&sync_patch.id).exists() {
                     sync_patches.push(sync_patch.clone());
                     sync_patches.push(sync_patch.clone());
                 }
                 }
 
 
                 if !dry {
                 if !dry {
-                    remove_file(docs_path.join(&sync_patch.path)).unwrap_or(());
-                    remove_file(local_path.join(&sync_patch.id)).unwrap_or(());
-                    remove_file(file_path).unwrap_or(());
+                    remove_file(docs_path.join(&sync_patch.path))?;
+                    remove_file(local_path.join(&sync_patch.id))?;
+                    remove_file(file_path)?;
                 }
                 }
 
 
                 continue
                 continue
@@ -486,7 +405,8 @@ impl Darkwiki {
                 }
                 }
             }
             }
 
 
-            if !files_name.is_empty() && !files_name.contains(&sync_patch.path.to_string()) {
+            // TODO: FIXME: IDGI AGAIN, HALP
+            if !filenames.is_empty() && !filenames.contains(&sync_patch.path.to_string()) {
                 continue
                 continue
             }
             }
 
 
@@ -500,7 +420,7 @@ impl Darkwiki {
             }
             }
         }
         }
 
 
-        // check if any doc removed from ~/darkwiki
+        // Check if any doc is removed from darkwiki filesystem.
         let local_files = read_dir(&local_path)?;
         let local_files = read_dir(&local_path)?;
         for file in local_files {
         for file in local_files {
             let file_id = file?.file_name();
             let file_id = file?.file_name();
@@ -511,7 +431,8 @@ impl Darkwiki {
                 continue
                 continue
             }
             }
 
 
-            if !files_name.is_empty() && !files_name.contains(&local_patch.path.to_string()) {
+            // TODO: FIXME: Is it just supposed to check that filenames doesn't contain the local_patch?
+            if !filenames.is_empty() && !filenames.contains(&local_patch.path.to_string()) {
                 continue
                 continue
             }
             }
 
 
@@ -529,7 +450,7 @@ impl Darkwiki {
                 local_patches.push(new_patch);
                 local_patches.push(new_patch);
 
 
                 if !dry {
                 if !dry {
-                    remove_file(file_path).unwrap_or(());
+                    remove_file(file_path)?;
                 }
                 }
             }
             }
         }
         }
@@ -546,151 +467,189 @@ impl Darkwiki {
         }
         }
         save_file(&path, edit)
         save_file(&path, edit)
     }
     }
-}
 
 
-async_daemonize!(realmain);
-async fn realmain(settings: Args, executor: Arc<Executor<'_>>) -> Result<()> {
-    let docs_path = expand_path(&settings.docs)?;
-    let datastore_path = expand_path(docs_path.join(".log").to_str().unwrap())?;
-
-    if settings.refresh {
-        println!("Removing local docs in: {:?} (yes/no)? ", docs_path);
-        let mut confirm = String::new();
-        stdin().read_line(&mut confirm).expect("Failed to read line");
-
-        let confirm = confirm.to_lowercase();
-        let confirm = confirm.trim();
-
-        if confirm == "yes" || confirm == "y" {
-            remove_dir_all(docs_path).unwrap_or(());
-            println!("Local data removed successfully.");
-        } else {
-            error!("Unexpected Value: {}", confirm);
+    fn is_delete_patch(patch: &Patch) -> bool {
+        if patch.ops().0.len() != 1 {
+            return false
         }
         }
 
 
-        return Ok(())
+        if let OpMethod::Delete(d) = patch.ops().0[0] {
+            if patch.base.len() as u64 == d {
+                return true
+            }
+        }
+
+        false
     }
     }
+}
 
 
-    create_dir_all(docs_path.clone())?;
-    create_dir_all(datastore_path.clone())?;
-    create_dir_all(datastore_path.join("local"))?;
-    create_dir_all(datastore_path.join("sync"))?;
+async fn handle_signals(
+    mut signals: Signals,
+    cfg_path: PathBuf,
+    term_tx: async_channel::Sender<()>,
+) {
+    debug!("Started signal handler");
+    while let Some(signal) = signals.next().await {
+        match signal {
+            SIGHUP => {
+                info!("Caught SIGHUP");
+                let toml_contents = match std::fs::read_to_string(cfg_path.clone()) {
+                    Ok(v) => v,
+                    Err(e) => {
+                        error!("Couldn't load configuration file: {}", e);
+                        continue
+                    }
+                };
+
+                *WORKSPACES.write().await = parse_workspaces(&toml_contents);
+                info!("Reloaded workspaces");
+            }
 
 
-    if settings.generate {
-        println!("Generating a new workspace");
+            SIGTERM | SIGINT | SIGQUIT => {
+                term_tx.send(()).await.unwrap();
+            }
+
+            _ => unreachable!(),
+        }
+    }
+}
+
+async_daemonize!(realmain);
+async fn realmain(args: Args, executor: Arc<Executor<'_>>) -> Result<()> {
+    let cfg_path = get_config_path(args.config, CONFIG_FILE)?;
+    let docs_path = expand_path(&args.docs)?;
+    let store_path = expand_path(docs_path.join(".log").to_str().unwrap())?;
 
 
+    create_dir_all(docs_path.clone())?;
+    create_dir_all(store_path.clone())?;
+    create_dir_all(store_path.join(LOCAL_ID_PATH))?;
+    create_dir_all(store_path.join(SYNC_ID_PATH))?;
+
+    if args.gen_secret {
+        eprintln!("Generating a new workspace");
         loop {
         loop {
-            println!("Name for the new workspace: ");
+            eprint!("Input the name for the new workspace (use ascii chars): ");
             let mut workspace = String::new();
             let mut workspace = String::new();
-            stdin().read_line(&mut workspace).expect("Failed to read line");
-            let workspace = workspace.to_lowercase();
-            let workspace = workspace.trim();
-            if workspace.is_empty() && workspace.len() < 3 {
-                error!("Wrong workspace try again");
+            stdin().read_line(&mut workspace)?;
+            // Non-exhaustive
+            let workspace = workspace
+                .replace('\n', "")
+                .replace('\t', "_")
+                .replace('\r', "_")
+                .replace(' ', "_")
+                .replace('/', "_")
+                .replace('\\', "_")
+                .replace('\'', "_")
+                .replace('&', "_")
+                .replace('~', "_")
+                .replace(':', "_");
+
+            if workspace.is_empty() || workspace.len() < 3 {
+                eprintln!("Error: Workspace name is empty or less than 3 characters. Try again.");
                 continue
                 continue
             }
             }
-            let mut rng = crypto_box::rand_core::OsRng;
-            let secret_key = SecretKey::generate(&mut rng);
-            let encoded = bs58::encode(secret_key.as_bytes());
 
 
-            create_dir_all(docs_path.join(workspace))?;
+            let secret = bs58::encode(crypto_secretbox_keygen()).into_string();
+            create_dir_all(docs_path.join(workspace.clone()))?;
 
 
-            println!("workspace: {}:{}", workspace, encoded.into_string());
-            println!("Please add it to the config file.");
-            break
+            println!("Created workspace: {}:{}", workspace, secret);
+            eprintln!("Please add it to the config file.");
+            return Ok(())
         }
         }
-
-        return Ok(())
     }
     }
 
 
-    let workspaces = get_workspaces(&settings, &docs_path)?;
-
-    if workspaces.is_empty() {
-        error!("Please add at least on workspace to the config file.");
-        println!("Run `$ darkwikid --generate` to generate new workspace.");
-        return Ok(())
+    // Signal handling for config reload and graceful termination.
+    let signals = Signals::new(&[SIGHUP, SIGTERM, SIGINT, SIGQUIT])?;
+    let handle = signals.handle();
+    let (term_tx, term_rx) = async_channel::bounded::<()>(1);
+    let signals_task = task::spawn(handle_signals(signals, cfg_path.clone(), term_tx));
+    info!("Set up signal handling");
+
+    {
+        info!("Parsing configuration file for workspaces");
+        let toml_contents = std::fs::read_to_string(cfg_path.clone())?;
+        *WORKSPACES.write().await = parse_workspaces(&toml_contents);
+        if WORKSPACES.read().await.is_empty() {
+            eprintln!("Please add atleast one workspace to the config file.");
+            eprintln!("Run \"$ darkwikid --gen-secret\" to create a new workspace.");
+            exit(1);
+        }
     }
     }
 
 
-    let (rpc_sx, rpc_rv) = async_channel::unbounded::<(String, bool, Vec<String>)>();
-    let (notify_sx, notify_rv) = async_channel::unbounded::<Vec<Vec<Patch>>>();
+    let (rpc_tx, rpc_rx) = async_channel::unbounded::<(String, bool, Vec<String>)>();
+    let (notify_tx, notify_rx) = async_channel::unbounded::<Vec<Vec<Patch>>>();
 
 
-    //
-    // RPC
-    //
-    let rpc_interface = Arc::new(JsonRpcInterface::new(rpc_sx, notify_rv));
-    executor.spawn(listen_and_serve(settings.rpc_listen.clone(), rpc_interface)).detach();
+    // ===============
+    // JSON-RPC server
+    // ===============
+    let rpc_iface = Arc::new(JsonRpcInterface::new(rpc_tx, notify_rx));
+    executor.spawn(listen_and_serve(args.rpc_listen, rpc_iface)).detach();
 
 
-    //
+    // ====
     // Raft
     // Raft
-    //
+    // ====
     let seen_net_msgs = Arc::new(Mutex::new(FxHashMap::default()));
     let seen_net_msgs = Arc::new(Mutex::new(FxHashMap::default()));
-
-    let datastore_raft = datastore_path.join("darkwiki.db");
-    let raft_settings = RaftSettings { datastore_path: datastore_raft, ..RaftSettings::default() };
-
-    let mut raft = Raft::<EncryptedPatch>::new(raft_settings, seen_net_msgs.clone())?;
-
-    //
-    // P2p setup
-    //
-    let mut net_settings = settings.net.clone();
+    let store_raft = store_path.join("darkwiki.db");
+    let raft_settings = RaftSettings { datastore_path: store_raft, ..RaftSettings::default() };
+    // FIXME: This is a bad design, and needs a proper rework.
+    let raft =
+        Arc::new(Mutex::new(Raft::<EncryptedPatch>::new(raft_settings, seen_net_msgs.clone())?));
+
+    // =========
+    // P2P setup
+    // =========
+    let mut net_settings = args.net.clone();
     net_settings.app_version = Some(option_env!("CARGO_PKG_VERSION").unwrap_or("").to_string());
     net_settings.app_version = Some(option_env!("CARGO_PKG_VERSION").unwrap_or("").to_string());
-    let (p2p_send_channel, p2p_recv_channel) = async_channel::unbounded::<NetMsg>();
-
+    let (p2p_tx, p2p_rx) = async_channel::unbounded::<NetMsg>();
     let p2p = net::P2p::new(net_settings.into()).await;
     let p2p = net::P2p::new(net_settings.into()).await;
-    let p2p = p2p.clone();
-
     let registry = p2p.protocol_registry();
     let registry = p2p.protocol_registry();
 
 
-    let raft_node_id = raft.id();
-    registry
-        .register(net::SESSION_ALL, move |channel, p2p| {
-            let raft_node_id = raft_node_id.clone();
-            let sender = p2p_send_channel.clone();
-            let seen_net_msgs_cloned = seen_net_msgs.clone();
-            async move {
-                ProtocolRaft::init(raft_node_id, channel, sender, p2p, seen_net_msgs_cloned).await
-            }
-        })
-        .await;
+    let raft_node_id = raft.lock().await.id();
+    registry.register(net::SESSION_ALL, move | channel, p2p| {
+        let raft_node_id = raft_node_id.clone();
+        let sender = p2p_tx.clone();
+        let seen_net_msgs = seen_net_msgs.clone();
+        async move {
+            ProtocolRaft::init(raft_node_id, channel, sender, p2p, seen_net_msgs).await
+        }
+    }).await;
 
 
     p2p.clone().start(executor.clone()).await?;
     p2p.clone().start(executor.clone()).await?;
-
     executor.spawn(p2p.clone().run(executor.clone())).detach();
     executor.spawn(p2p.clone().run(executor.clone())).detach();
 
 
-    //
+    // ==============
     // Darkwiki start
     // Darkwiki start
-    //
-    let raft_sx = raft.sender();
-    let raft_rv = raft.receiver();
+    // ==============
+    let raft_tx = raft.lock().await.sender();
+    let raft_rx = raft.lock().await.receiver();
     executor
     executor
         .spawn(async move {
         .spawn(async move {
-            let darkwiki_settings =
-                DarkWikiSettings { author: settings.author, datastore_path, docs_path };
-            let darkwiki = Darkwiki {
-                settings: darkwiki_settings,
-                raft: (raft_sx, raft_rv),
-                rpc: (notify_sx, rpc_rv),
-                workspaces,
-            };
-            darkwiki.start().await.unwrap_or(());
+            let settings = DarkWikiSettings { author: args.author, store_path, docs_path };
+            let dw = DarkWiki { settings, raft: (raft_tx, raft_rx), rpc: (notify_tx, rpc_rx) };
+            dw.start().await.unwrap();
         })
         })
         .detach();
         .detach();
 
 
-    //
-    // Waiting Exit signal
-    //
-    let (signal, shutdown) = async_channel::bounded::<()>(1);
-    ctrlc::set_handler(move || {
-        warn!(target: "darkwiki", "Catch exit signal");
-        // cleaning up tasks running in the background
-        if let Err(e) = async_std::task::block_on(signal.send(())) {
-            error!("Error on sending exit signal: {}", e);
-        }
-    })
-    .unwrap();
+    let (raft_term_tx, raft_term_rx) = async_channel::bounded::<()>(1);
+    let _p2p = p2p.clone();
+    let _ex = executor.clone();
+    executor
+        .spawn(async move { raft.lock().await.run(_p2p, p2p_rx, _ex, raft_term_rx).await.unwrap() })
+        .detach();
+
+    // Wait for termination signal
+    term_rx.recv().await?;
+    eprint!("\r");
+    info!("Caught termination signal, cleaning up and exiting...");
+    handle.close();
+    signals_task.await;
+
+    info!("Stopping Raft...");
+    raft_term_tx.send(()).await.unwrap();
 
 
-    raft.run(p2p.clone(), p2p_recv_channel.clone(), executor.clone(), shutdown.clone()).await?;
+    info!("Stopping P2P network...");
+    p2p.stop().await;
 
 
+    info!("Bye.");
     Ok(())
     Ok(())
 }
 }

+ 15 - 6
bin/darkwiki/darkwikid/src/patch.rs

@@ -1,14 +1,17 @@
 use std::{cmp::Ordering, io};
 use std::{cmp::Ordering, io};
 
 
-use colored::Colorize;
+use dryoc::constants::CRYPTO_SECRETBOX_NONCEBYTES;
 use serde::{Deserialize, Serialize};
 use serde::{Deserialize, Serialize};
 
 
 use darkfi::{
 use darkfi::{
     serial::{Decodable, Encodable, SerialDecodable, SerialEncodable, VarInt},
     serial::{Decodable, Encodable, SerialDecodable, SerialEncodable, VarInt},
-    util::time::Timestamp,
+    util::{
+        cli::{fg_green, fg_red},
+        time::Timestamp,
+    },
 };
 };
 
 
-use crate::str_to_chars;
+use crate::util::str_to_chars;
 
 
 #[derive(PartialEq, Eq, Serialize, Deserialize, Clone, Debug)]
 #[derive(PartialEq, Eq, Serialize, Deserialize, Clone, Debug)]
 pub enum OpMethod {
 pub enum OpMethod {
@@ -20,6 +23,12 @@ pub enum OpMethod {
 #[derive(PartialEq, Eq, Serialize, Deserialize, Clone, Debug)]
 #[derive(PartialEq, Eq, Serialize, Deserialize, Clone, Debug)]
 pub struct OpMethods(pub Vec<OpMethod>);
 pub struct OpMethods(pub Vec<OpMethod>);
 
 
+#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
+pub struct EncryptedPatch {
+    pub nonce: [u8; CRYPTO_SECRETBOX_NONCEBYTES],
+    pub ciphertext: Vec<u8>,
+}
+
 #[derive(PartialEq, Eq, SerialEncodable, SerialDecodable, Serialize, Deserialize, Clone, Debug)]
 #[derive(PartialEq, Eq, SerialEncodable, SerialDecodable, Serialize, Deserialize, Clone, Debug)]
 pub struct Patch {
 pub struct Patch {
     pub path: String,
     pub path: String,
@@ -369,7 +378,7 @@ impl Patch {
 
 
     pub fn colorize(&self) -> String {
     pub fn colorize(&self) -> String {
         if self.ops.0.is_empty() {
         if self.ops.0.is_empty() {
-            return format!("{}", self.base.green())
+            return fg_green(&self.base)
         }
         }
 
 
         let mut st = vec![];
         let mut st = vec![];
@@ -393,11 +402,11 @@ impl Patch {
                             deleted_part.push(s.to_string());
                             deleted_part.push(s.to_string());
                         }
                         }
                     }
                     }
-                    colorized_str.push(format!("{}", deleted_part.join("").red()));
+                    colorized_str.push(fg_red(&deleted_part.join("")));
                 }
                 }
                 OpMethod::Insert(insert) => {
                 OpMethod::Insert(insert) => {
                     let chars = str_to_chars(insert);
                     let chars = str_to_chars(insert);
-                    colorized_str.push(format!("{}", chars.join("").green()));
+                    colorized_str.push(fg_green(&chars.join("")))
                 }
                 }
             }
             }
         }
         }

+ 157 - 0
bin/darkwiki/darkwikid/src/util.rs

@@ -0,0 +1,157 @@
+use std::{
+    fs::{create_dir_all, read_dir},
+    path::{Path, PathBuf},
+};
+
+use dryoc::{
+    classic::crypto_secretbox::{crypto_secretbox_easy, crypto_secretbox_open_easy, Key, Nonce},
+    constants::CRYPTO_SECRETBOX_MACBYTES,
+    dryocbox::NewByteArray,
+};
+use fxhash::FxHashMap;
+use log::{error, info, warn};
+use unicode_segmentation::UnicodeSegmentation;
+
+use darkfi::{
+    serial::{deserialize, serialize},
+    util::path::expand_path,
+    Error, Result,
+};
+
+use crate::{Args, EncryptedPatch, Patch};
+
+/// Split a `&str` into a vector of each of its chars.
+pub fn str_to_chars(s: &str) -> Vec<&str> {
+    s.graphemes(true).collect::<Vec<&str>>()
+}
+
+/// Parse a base58 string for a `crypto_secretbox` secret.
+fn parse_b58_secret(s: &str) -> Result<[u8; 32]> {
+    match bs58::decode(s).into_vec() {
+        Ok(v) => {
+            if v.len() != 32 {
+                return Err(Error::Custom("Secret is not 32 bytes long".to_string()))
+            }
+
+            Ok(v.try_into().unwrap())
+        }
+        Err(e) => Err(Error::Custom(format!("Unable to parse secret from base58: {}", e))),
+    }
+}
+
+/// Parse a TOML string for configured workspaces and return an `FxHashMap`
+/// of parsed data. Does not error on failures, just warns if something is
+/// misconfigured.
+pub fn parse_workspaces(toml_str: &str) -> FxHashMap<String, Key> {
+    let mut ret = FxHashMap::default();
+
+    let settings: Args = match toml::from_str(toml_str) {
+        Ok(v) => v,
+        Err(e) => {
+            error!("Failed parsing TOML from string: {}", e);
+            return ret
+        }
+    };
+
+    for workspace in settings.workspace {
+        let wrk: Vec<&str> = workspace.split(':').collect();
+        if wrk.len() != 2 {
+            warn!("Invalid workspace: {}", workspace);
+            continue
+        }
+
+        let secret = match parse_b58_secret(wrk[1]) {
+            Ok(v) => v,
+            Err(e) => {
+                warn!("Failed parsing secret for workspace {}: {}", wrk[0], e);
+                continue
+            }
+        };
+
+        let docs_path = match expand_path(&settings.docs) {
+            Ok(v) => v,
+            Err(e) => {
+                warn!("Failed expanding docs path for workspace {}: {}", wrk[0], e);
+                continue
+            }
+        };
+
+        if let Err(e) = create_dir_all(docs_path.join(wrk[0])) {
+            warn!("Failed creating directory for workspace {}: {}", wrk[0], e);
+            continue
+        }
+
+        info!("Added parsed workspace: {}", wrk[0]);
+        ret.insert(wrk[0].to_string(), secret);
+    }
+
+    ret
+}
+
+/// Encrypt a patch using a NaCl crypto_secretbox given a `Patch` and a `Key`.
+pub fn encrypt_patch(patch: &Patch, key: &Key) -> Result<EncryptedPatch> {
+    let nonce = Nonce::gen();
+    let payload = serialize(patch);
+
+    let mut ciphertext = vec![0u8; payload.len() + CRYPTO_SECRETBOX_MACBYTES];
+
+    if let Err(e) = crypto_secretbox_easy(&mut ciphertext, &payload, &nonce, key) {
+        error!("encrypt_patch: Failed encrypting patch: {}", e);
+        return Err(Error::Custom(format!("Failed encrypting darkwiki patch: {}", e)))
+    }
+
+    Ok(EncryptedPatch { nonce, ciphertext })
+}
+
+/// Decrypt a patch using a NaCl crypto_secretbox given an `EncryptedPatch` and a `Key`.
+pub fn decrypt_patch(patch: &EncryptedPatch, key: &Key) -> Result<Patch> {
+    let nonce = &patch.nonce;
+    let ciphertext = &patch.ciphertext;
+
+    let mut decrypted = vec![0u8; ciphertext.len() - CRYPTO_SECRETBOX_MACBYTES];
+    if let Err(e) = crypto_secretbox_open_easy(&mut decrypted, ciphertext, nonce, key) {
+        error!("decrypt_patch: Failed decrypting patch: {}", e);
+        return Err(Error::Custom(format!("Failed decrypting darkwiki patch: {}", e)))
+    }
+
+    Ok(deserialize(&decrypted)?)
+}
+
+/// TODO: DOCUMENT ME
+/// FIXME: There's checking of file extensions here. Take care that the rest of the code
+/// is robust against this attack.
+pub fn get_docs_paths(files: &mut Vec<PathBuf>, path: &Path, parent: Option<&Path>) -> Result<()> {
+    let docs = read_dir(&path)?;
+    let docs = docs.filter(|d| d.is_ok()).map(|d| d.unwrap().path()).collect::<Vec<PathBuf>>();
+
+    for doc in docs {
+        if let Some(f) = doc.file_name() {
+            let filename = PathBuf::from(f);
+            let filename = if let Some(parent) = parent { parent.join(filename) } else { filename };
+
+            if doc.is_file() {
+                if let Some(ext) = doc.extension() {
+                    if ext == "md" || ext == "markdown" {
+                        files.push(filename);
+                    }
+                }
+            } else if doc.is_dir() {
+                if f == ".log" {
+                    continue
+                }
+
+                get_docs_paths(files, &doc, Some(&filename))?;
+            }
+        }
+    }
+
+    Ok(())
+}
+
+/// Hash a path and workspace, and encode with base58, providing an ID.
+pub fn path_to_id(path: &str, workspace: &str) -> String {
+    let mut hasher = blake3::Hasher::new();
+    hasher.update(path.as_bytes());
+    hasher.update(workspace.as_bytes());
+    bs58::encode(hasher.finalize().as_bytes()).into_string()
+}

+ 5 - 0
src/error.rs

@@ -319,8 +319,13 @@ pub enum Error {
     // ==============
     // ==============
     // DHT errors
     // DHT errors
     // ==============
     // ==============
+    // FIXME: This is out of context, be specific when writing errors.
     #[error("Did not find key")]
     #[error("Did not find key")]
     UnknownKey,
     UnknownKey,
+
+    // Catch-all
+    #[error("{0}")]
+    Custom(String),
 }
 }
 
 
 /// Transaction verification errors
 /// Transaction verification errors

+ 2 - 2
src/raft/consensus.rs

@@ -87,6 +87,7 @@ impl<T: Decodable + Encodable + Clone> Raft<T> {
         let id = match datastore.id.get_last()? {
         let id = match datastore.id.get_last()? {
             Some(_id) => _id,
             Some(_id) => _id,
             None => {
             None => {
+                // FIXME: This should be a big number, like a hash.
                 let id = NodeId(gen_id(30));
                 let id = NodeId(gen_id(30));
                 datastore.id.insert(&id)?;
                 datastore.id.insert(&id)?;
                 id
                 id
@@ -133,8 +134,6 @@ impl<T: Decodable + Encodable + Clone> Raft<T> {
         let prune_nodes_id_task =
         let prune_nodes_id_task =
             executor.spawn(prune_map::<NodeId>(self.nodes.clone(), self.settings.prun_duration));
             executor.spawn(prune_map::<NodeId>(self.nodes.clone(), self.settings.prun_duration));
 
 
-        let mut rng = rand::thread_rng();
-
         let (id_sx, id_rv) = async_channel::unbounded::<()>();
         let (id_sx, id_rv) = async_channel::unbounded::<()>();
         let (heartbeat_sx, heartbeat_rv) = async_channel::unbounded::<()>();
         let (heartbeat_sx, heartbeat_rv) = async_channel::unbounded::<()>();
         let (timeout_sx, timeout_rv) = async_channel::unbounded::<()>();
         let (timeout_sx, timeout_rv) = async_channel::unbounded::<()>();
@@ -145,6 +144,7 @@ impl<T: Decodable + Encodable + Clone> Raft<T> {
         let heartbeat_timeout = Duration::from_millis(self.settings.heartbeat_timeout);
         let heartbeat_timeout = Duration::from_millis(self.settings.heartbeat_timeout);
         let send_heartbeat_task = executor.spawn(send_loop(heartbeat_sx, heartbeat_timeout));
         let send_heartbeat_task = executor.spawn(send_loop(heartbeat_sx, heartbeat_timeout));
 
 
+        let rng = &mut OsRng;
         let timeout =
         let timeout =
             Duration::from_secs(rng.gen_range(0..self.settings.timeout) + self.settings.timeout);
             Duration::from_secs(rng.gen_range(0..self.settings.timeout) + self.settings.timeout);
         let send_timeout_task = executor.spawn(send_loop(timeout_sx, timeout));
         let send_timeout_task = executor.spawn(send_loop(timeout_sx, timeout));

+ 18 - 6
src/util/cli.rs

@@ -89,19 +89,19 @@ pub fn get_log_config() -> simplelog::Config {
 ///
 ///
 /// The Cargo.toml dependencies needed for this are:
 /// The Cargo.toml dependencies needed for this are:
 /// ```text
 /// ```text
-/// async-channel = "1.6.1"
+/// async-channel = "1.7.1"
 /// async-executor = "1.4.1"
 /// async-executor = "1.4.1"
-/// async-std = "1.11.0"
+/// async-std = "1.12.0"
 /// darkfi = { path = "../../", features = ["util"] }
 /// darkfi = { path = "../../", features = ["util"] }
 /// easy-parallel = "3.2.0"
 /// easy-parallel = "3.2.0"
 /// futures-lite = "1.12.0"
 /// futures-lite = "1.12.0"
-/// simplelog = "0.12.0-alpha1"
+/// simplelog = "0.12.0"
 ///
 ///
 /// # Argument parsing
 /// # Argument parsing
-/// serde = "1.0.136"
-/// serde_derive = "1.0.136"
+/// serde = "1.0.135"
+/// serde_derive = "1.0.145"
 /// structopt = "0.3.26"
 /// structopt = "0.3.26"
-/// structopt-toml = "0.5.0"
+/// structopt-toml = "0.5.1"
 /// ```
 /// ```
 ///
 ///
 /// Example usage:
 /// Example usage:
@@ -217,3 +217,15 @@ pub fn fg_red(message: &str) -> String {
 pub fn fg_green(message: &str) -> String {
 pub fn fg_green(message: &str) -> String {
     format!("{}{}{}", color::Fg(color::Green), message, color::Fg(color::Reset))
     format!("{}{}{}", color::Fg(color::Green), message, color::Fg(color::Reset))
 }
 }
+
+pub fn start_fg_red() -> String {
+    format!("{}", color::Fg(color::Red))
+}
+
+pub fn start_fg_green() -> String {
+    format!("{}", color::Fg(color::Green))
+}
+
+pub fn fg_reset() -> String {
+    format!("{}", color::Fg(color::Reset))
+}