diff --git a/.gitignore b/.gitignore index f06b49a0e10c9eacff6fce8f7b95d7c1178fd8cd..7b5ce765893a9af1f088be19e906acc40bf4ee94 100644 --- a/.gitignore +++ b/.gitignore @@ -43,4 +43,14 @@ typedoc/ app/**/*.js* app/**/*.d.ts test/**/*.d.ts -test/**/*.js* \ No newline at end of file +test/**/*.js* + +# files generated by neon tests +test2.bin.gz + +# rust intermediate binaries +target + +# files generated by rust tests +neon/native/tests/*.txt +neon/native/tests/wotb-* diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000000000000000000000000000000000000..4231a086a501edfab6b11677e8bca2bfd4a74a73 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,677 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "adler32" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" + +[[package]] +name = "aho-corasick" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" +dependencies = [ + "memchr", +] + +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" + +[[package]] +name = "bincode" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5753e2a71534719bf3f4e57006c3a4f0d2c672a4b676eec84161f763eca87dbf" +dependencies = [ + "byteorder", + "serde", +] + +[[package]] +name = "bs58" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "476e9cd489f9e121e02ffa6014a8ef220ecb15c05ed23fc34cca13925dc283fb" + +[[package]] +name = "bumpalo" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "cc" +version = "1.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d87b23d6a92cd03af510a5ade527033f6aa6fa92161e2d5863a907d4c5e31d" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "crc32fast" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", + "maybe-uninit", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "lazy_static", + "maybe-uninit", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-queue" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg", + "cfg-if", + "lazy_static", +] + +[[package]] +name = "cslice" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "697c714f50560202b1f4e2e09cd50a421881c83e9025db75d15f276616f04f40" + +[[package]] +name = "curve25519-dalek" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26778518a7f6cffa1d25a44b602b62b979bd88adb9e99ffec546998cf3404839" +dependencies = [ + "byteorder", + "digest", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "dubp-wot" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0f76dd4b734d34be5613c470402ccdb21cb21b84ecd75acdc81cf80c618201a" +dependencies = [ + "log", + "rayon", + "serde", +] + +[[package]] +name = "duniteroxyde" +version = "0.2.9" +dependencies = [ + "bincode", + "bs58", + "dubp-wot", + "dup-crypto", + "flate2", + "neon", + "neon-build", +] + +[[package]] +name = "dup-crypto" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d1f07e794fd7bbe2851c9c2538b470ab5f99ef46b470b1ec28a2dc565d52127" +dependencies = [ + "base64", + "bs58", + "byteorder", + "curve25519-dalek", + "ring", + "thiserror", + "unwrap", + "zeroize", +] + +[[package]] +name = "either" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" + +[[package]] +name = "flate2" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42" +dependencies = [ + "cfg-if", + "crc32fast", + "libc", + "miniz_oxide", +] + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +dependencies = [ + "typenum", +] + +[[package]] +name = "getrandom" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hermit-abi" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a27d435371a2fa5b6d2b028a74bbdb1234f308da363226a2854ca3ff8ba7055" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" + +[[package]] +name = "memchr" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" + +[[package]] +name = "memoffset" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa679ff6578b1cddee93d7e82e263b94a575e0bfced07284eb0c037c1d2416a5" +dependencies = [ + "adler32", +] + +[[package]] +name = "neon" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cac4691701b686e6c07b2eb5b51a9f26f5c11179c5d7924b78100dd387fc99d" +dependencies = [ + "cslice", + "neon-build", + "neon-runtime", + "semver", +] + +[[package]] +name = "neon-build" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9ed332afd4711b84f4f83d334428a1fd9ce53620b62b87595934297c5ede2ed" +dependencies = [ + "cfg-if", + "neon-sys", +] + +[[package]] +name = "neon-runtime" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2beea093a60c08463f65e1da4cda68149986f60d8d2177489b44589463c782a6" +dependencies = [ + "cfg-if", + "neon-sys", +] + +[[package]] +name = "neon-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69a6c1ba6b926746f4d3f596de18ce49d062d78fd9f35f636080232aa77a0e16" +dependencies = [ + "cc", + "regex", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "proc-macro2" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098" +dependencies = [ + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9" +dependencies = [ + "crossbeam-deque", + "crossbeam-queue", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "regex" +version = "1.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" + +[[package]] +name = "ring" +version = "0.16.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba5a8ec64ee89a76c98c549af81ff14813df09c3e6dc4766c3856da48597a0c" +dependencies = [ + "cc", + "lazy_static", + "libc", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "subtle" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941" + +[[package]] +name = "syn" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "thiserror" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b3d3d2ff68104100ab257bb6bb0cb26c901abe4bd4ba15961f3bf867924012" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca972988113b7715266f91250ddb98070d033c62a011fa0fcc57434a649310dd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + +[[package]] +name = "untrusted" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60369ef7a31de49bcb3f6ca728d4ba7300d9a1658f94c727d4cab8c8d9f4aece" + +[[package]] +name = "unwrap" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e33648dd74328e622c7be51f3b40a303c63f93e6fa5f08778b6203a4c25c20f" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasm-bindgen" +version = "0.2.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cc57ce05287f8376e998cbddfb4c8cb43b84a7ec55cf4551d7c00eef317a47f" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d967d37bf6c16cca2973ca3af071d0a2523392e4a594548155d89a678f4237cd" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bd151b63e1ea881bb742cd20e1d6127cef28399558f3b5d415289bc41eee3a4" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d68a5b36eef1be7868f668632863292e37739656a80fc4b9acec7b0bd35a4931" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf76fe7d25ac79748a37538b7daeed1c7a6867c92d3245c12c6222e4a20d639" + +[[package]] +name = "web-sys" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d6f51648d8c56c366144378a33290049eafdd784071077f6fe37dae64c1c4cb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "zeroize" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de251eec69fc7c1bc3923403d18ececb929380e016afe103da75f396704f8ca2" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..551b803b8e95fa86068ce5cce35a3c0d1ba91003 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["neon/native"] diff --git a/app/lib/blockchain/DuniterBlockchain.ts b/app/lib/blockchain/DuniterBlockchain.ts index e5659a1d2cfa428f496c62338b7419dcd4d6404d..faeec03d731e958e4fa3faae4dc22f06b6a93264 100644 --- a/app/lib/blockchain/DuniterBlockchain.ts +++ b/app/lib/blockchain/DuniterBlockchain.ts @@ -38,7 +38,7 @@ import { DBTx } from "../db/DBTx"; import { Underscore } from "../common-libs/underscore"; import { OtherConstants } from "../other_constants"; import { MonitorExecutionTime } from "../debug/MonitorExecutionTime"; -import { Wot } from "duniteroxyde"; +import { Wot } from "../../../neon/lib"; import { Directory } from "../system/directory"; export class DuniterBlockchain { diff --git a/app/lib/common-libs/crypto/keyring.ts b/app/lib/common-libs/crypto/keyring.ts index 200e32b2822fca6e48ec02cbde66c4b0af245391..720762f23dbdde2bd59015570cc2ed6a682b7b43 100644 --- a/app/lib/common-libs/crypto/keyring.ts +++ b/app/lib/common-libs/crypto/keyring.ts @@ -15,7 +15,7 @@ import { KeyPairBuilder, generateRandomSeed, seedToSecretKey, -} from "duniteroxyde"; +} from "../../../../neon/lib"; export class Key { constructor(readonly pub: string, readonly sec: string) {} diff --git a/app/lib/common.ts b/app/lib/common.ts index db15a6444e6d6c428eeaf84d803ebf6d541fb583..4d254483693ae6b5d57977708932e68e139478d9 100644 --- a/app/lib/common.ts +++ b/app/lib/common.ts @@ -11,7 +11,7 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. -import { sha256 } from "duniteroxyde"; +import { sha256 } from "../../neon/lib"; export const hashf = function hashf(str: string) { return sha256(str); diff --git a/app/lib/dal/fileDAL.ts b/app/lib/dal/fileDAL.ts index 3bed0ef10f6e19e69451cc94793710ffecdcda65..7929b72b4acfe25bbb1cfdd201debc08f61f123c 100644 --- a/app/lib/dal/fileDAL.ts +++ b/app/lib/dal/fileDAL.ts @@ -43,7 +43,7 @@ import { MetaDAL } from "./sqliteDAL/MetaDAL"; import { DataErrors } from "../common-libs/errors"; import { BasicRevocableIdentity, IdentityDTO } from "../dto/IdentityDTO"; import { FileSystem } from "../system/directory"; -import { Wot } from "duniteroxyde"; +import { Wot } from "../../../neon/lib"; import { IIndexDAO } from "./indexDAL/abstract/IIndexDAO"; import { BIndexDAO } from "./indexDAL/abstract/BIndexDAO"; import { MIndexDAO } from "./indexDAL/abstract/MIndexDAO"; diff --git a/app/lib/dto/TransactionDTO.ts b/app/lib/dto/TransactionDTO.ts index d58d660fe7afba0867124594fdc73bc4da41a9a3..71dfa660f6176053edb6f5eba203ea2e7e34e46a 100644 --- a/app/lib/dto/TransactionDTO.ts +++ b/app/lib/dto/TransactionDTO.ts @@ -13,7 +13,7 @@ import { hashf } from "../common"; import { Cloneable } from "./Cloneable"; -import { verify } from "duniteroxyde"; +import { verify } from "../../../neon/lib"; export interface BaseDTO { base: number; diff --git a/app/lib/indexer.ts b/app/lib/indexer.ts index 8ce3c8f368baa6591a7b992740c9c7d45d89541b..c691672f4c65e06431fbe83894f2d28104eb0f9e 100644 --- a/app/lib/indexer.ts +++ b/app/lib/indexer.ts @@ -18,7 +18,7 @@ import { RevocationDTO } from "./dto/RevocationDTO"; import { CertificationDTO } from "./dto/CertificationDTO"; import { TransactionDTO } from "./dto/TransactionDTO"; import { DBHead } from "./db/DBHead"; -import { verify } from "duniteroxyde"; +import { verify } from "../../neon/lib"; import { rawer, txunlock } from "./common-libs/index"; import { CommonConstants } from "./common-libs/constants"; import { MembershipDTO } from "./dto/MembershipDTO"; @@ -31,7 +31,7 @@ import { Underscore } from "./common-libs/underscore"; import { DataErrors } from "./common-libs/errors"; import { MonitorExecutionTime } from "./debug/MonitorExecutionTime"; import { NewLogger } from "./logger"; -import { WotBuilder } from "duniteroxyde"; +import { WotBuilder } from "../../neon/lib"; const constants = CommonConstants; diff --git a/app/lib/rules/global_rules.ts b/app/lib/rules/global_rules.ts index dd6d1fa5e90c29ac15bab80ed30f1d3affcfb9f1..c168807bffda3c74c90285a777039281cee9cb0a 100644 --- a/app/lib/rules/global_rules.ts +++ b/app/lib/rules/global_rules.ts @@ -16,7 +16,7 @@ import { FileDAL } from "../dal/fileDAL"; import { DBBlock } from "../db/DBBlock"; import { TransactionDTO, TxSignatureResult } from "../dto/TransactionDTO"; import { BlockDTO } from "../dto/BlockDTO"; -import { verify } from "duniteroxyde"; +import { verify } from "../../../neon/lib"; import { rawer, txunlock } from "../common-libs/index"; import { CommonConstants } from "../common-libs/constants"; import { IdentityDTO } from "../dto/IdentityDTO"; diff --git a/app/lib/rules/local_rules.ts b/app/lib/rules/local_rules.ts index 81e52bee163b2f61729cb2cb30f97198ca7d7829..ea5f8a578e143bc9ad9dfd5c5eb39cb1a492846d 100644 --- a/app/lib/rules/local_rules.ts +++ b/app/lib/rules/local_rules.ts @@ -22,7 +22,7 @@ import { } from "../indexer"; import { BaseDTO, TransactionDTO } from "../dto/TransactionDTO"; import { DBBlock } from "../db/DBBlock"; -import { verify } from "duniteroxyde"; +import { verify } from "../../../neon/lib"; import { hashf } from "../common"; import { CommonConstants } from "../common-libs/constants"; import { IdentityDTO } from "../dto/IdentityDTO"; diff --git a/app/lib/system/directory.ts b/app/lib/system/directory.ts index 3a5e7c9ba3bfc0934e1cbe556ea34db5feed5f68..9c364b5c5489ec787ed90462b8be47a1ac5349da 100644 --- a/app/lib/system/directory.ts +++ b/app/lib/system/directory.ts @@ -15,7 +15,7 @@ import * as path from "path"; import * as fs from "fs"; import { SQLiteDriver } from "../dal/drivers/SQLiteDriver"; import { CFSCore } from "../dal/fileDALs/CFSCore"; -import { Wot, WotBuilder } from "duniteroxyde"; +import { Wot, WotBuilder } from "../../../neon/lib"; import { FileDALParams } from "../dal/fileDAL"; import { cliprogram } from "../common-libs/programOptions"; import { LevelDBDriver } from "../dal/drivers/LevelDBDriver"; diff --git a/app/modules/crawler/lib/req2fwd.ts b/app/modules/crawler/lib/req2fwd.ts index 39b71672546cc8d07a9bae52bb3484d1ca23b306..14bee1a634139c3ea270bba8b9229d6c3297851b 100644 --- a/app/modules/crawler/lib/req2fwd.ts +++ b/app/modules/crawler/lib/req2fwd.ts @@ -12,7 +12,7 @@ // GNU Affero General Public License for more details. import { Contacter } from "./contacter"; -import { verify } from "duniteroxyde"; +import { verify } from "../../../../neon/lib"; import { rawer } from "../../../lib/common-libs/index"; import { HttpRequirements } from "../../bma/lib/dtos"; diff --git a/app/modules/crawler/lib/sync/v2/GlobalIndexStream.ts b/app/modules/crawler/lib/sync/v2/GlobalIndexStream.ts index b4c222601ac1f293f6e980ab79bd8340bf9386c8..c28345513b657b605e01e032a312b83954e4b013 100644 --- a/app/modules/crawler/lib/sync/v2/GlobalIndexStream.ts +++ b/app/modules/crawler/lib/sync/v2/GlobalIndexStream.ts @@ -18,7 +18,7 @@ import { import { BlockDTO } from "../../../../../lib/dto/BlockDTO"; import { Underscore } from "../../../../../lib/common-libs/underscore"; import { MonitorExecutionTime } from "../../../../../lib/debug/MonitorExecutionTime"; -import { Wot } from "duniteroxyde"; +import { Wot } from "../../../../../../neon/lib"; import { NewLogger } from "../../../../../lib/logger"; import { CommonConstants } from "../../../../../lib/common-libs/constants"; import { DBBlock } from "../../../../../lib/db/DBBlock"; diff --git a/app/modules/keypair/lib/scrypt.ts b/app/modules/keypair/lib/scrypt.ts index d66eba7fa7e74e2f464523b29103dcf5d4784a61..3135ecad6609695a3a0c43684155613337bbae2e 100644 --- a/app/modules/keypair/lib/scrypt.ts +++ b/app/modules/keypair/lib/scrypt.ts @@ -12,7 +12,7 @@ // GNU Affero General Public License for more details. import * as crypto from "crypto"; -import { KeyPairBuilder, seedToSecretKey } from "duniteroxyde"; +import { KeyPairBuilder, seedToSecretKey } from "../../../../neon/lib"; const SEED_LENGTH = 32; // Length of the key diff --git a/app/modules/prover/lib/blockGenerator.ts b/app/modules/prover/lib/blockGenerator.ts index 58917e2f1c99ed937c82f8b61a94979e0fe7051c..66c1aa75167741e95efc5517bf98c7271f9f3ba7 100644 --- a/app/modules/prover/lib/blockGenerator.ts +++ b/app/modules/prover/lib/blockGenerator.ts @@ -19,7 +19,7 @@ import { GLOBAL_RULES_HELPERS } from "../../../lib/rules/global_rules"; import { LOCAL_RULES_HELPERS } from "../../../lib/rules/local_rules"; import { Indexer } from "../../../lib/indexer"; import { DBBlock } from "../../../lib/db/DBBlock"; -import { verify } from "duniteroxyde"; +import { verify } from "../../../../neon/lib"; import { rawer } from "../../../lib/common-libs/index"; import { hashf } from "../../../lib/common"; import { CommonConstants } from "../../../lib/common-libs/constants"; diff --git a/app/modules/prover/lib/proof.ts b/app/modules/prover/lib/proof.ts index 2f5e68d1cfa9a818f67e8a88bf63ae518889a709..f24d30571026488f51a3e663b20fdb2c9a5546c7 100644 --- a/app/modules/prover/lib/proof.ts +++ b/app/modules/prover/lib/proof.ts @@ -17,7 +17,7 @@ import { hashf } from "../../../lib/common"; import { DBBlock } from "../../../lib/db/DBBlock"; import { ConfDTO } from "../../../lib/dto/ConfDTO"; import { ProverConstants } from "./constants"; -import { Ed25519Signator, KeyPairBuilder } from "duniteroxyde"; +import { Ed25519Signator, KeyPairBuilder } from "../../../../neon/lib"; import { dos2unix } from "../../../lib/common-libs/dos2unix"; import { rawer } from "../../../lib/common-libs/index"; import { ProcessCpuProfiler } from "../../../ProcessCpuProfiler"; diff --git a/app/modules/ws2p/lib/WS2PCluster.ts b/app/modules/ws2p/lib/WS2PCluster.ts index 535eea0191873f3db571ba47b6efd171611c3825..fe2d29e3cc184d9e6083966b902bc7049869e9ff 100644 --- a/app/modules/ws2p/lib/WS2PCluster.ts +++ b/app/modules/ws2p/lib/WS2PCluster.ts @@ -25,7 +25,7 @@ import { PeerDTO, WS2PEndpoint } from "../../../lib/dto/PeerDTO"; import { GlobalFifoPromise } from "../../../service/GlobalFifoPromise"; import { OtherConstants } from "../../../lib/other_constants"; import { Key } from "../../../lib/common-libs/crypto/keyring"; -import { verify } from "duniteroxyde"; +import { verify } from "../../../../neon/lib"; import { WS2PServerMessageHandler } from "./interface/WS2PServerMessageHandler"; import { WS2PMessageHandler } from "./impl/WS2PMessageHandler"; import { CommonConstants } from "../../../lib/common-libs/constants"; diff --git a/app/modules/ws2p/lib/WS2PConnection.ts b/app/modules/ws2p/lib/WS2PConnection.ts index a049abbf55851c1b4ed0e9b9a1fdf44a809a5e15..e186327f196c8cd57512cd58f52668dc8544b403 100644 --- a/app/modules/ws2p/lib/WS2PConnection.ts +++ b/app/modules/ws2p/lib/WS2PConnection.ts @@ -12,7 +12,7 @@ // GNU Affero General Public License for more details. import { Key } from "../../../lib/common-libs/crypto/keyring"; -import { verify } from "duniteroxyde"; +import { verify } from "../../../../neon/lib"; import { WS2PMessageHandler } from "./impl/WS2PMessageHandler"; import { BlockDTO } from "../../../lib/dto/BlockDTO"; import { IdentityDTO } from "../../../lib/dto/IdentityDTO"; diff --git a/app/service/IdentityService.ts b/app/service/IdentityService.ts index 9833f05d4c4df44c6b195352bdca0757a0d195d8..676b8602a57b183a8582444c9be37e77ca39cd12 100644 --- a/app/service/IdentityService.ts +++ b/app/service/IdentityService.ts @@ -24,7 +24,7 @@ import { RevocationDTO } from "../lib/dto/RevocationDTO"; import { BasicIdentity, IdentityDTO } from "../lib/dto/IdentityDTO"; import { CertificationDTO } from "../lib/dto/CertificationDTO"; import { DBCert } from "../lib/dal/sqliteDAL/CertDAL"; -import { verify } from "duniteroxyde"; +import { verify } from "../../neon/lib"; import { FIFOService } from "./FIFOService"; import { MindexEntry } from "../lib/indexer"; import { DataErrors } from "../lib/common-libs/errors"; diff --git a/app/service/PeeringService.ts b/app/service/PeeringService.ts index 0e2eb198b9971d9996ad276aa1d778c4a0119a97..6fd0a104effdeeaaed010a21e95a6a303a10a5f2 100755 --- a/app/service/PeeringService.ts +++ b/app/service/PeeringService.ts @@ -16,7 +16,7 @@ import { FileDAL } from "../lib/dal/fileDAL"; import { DBBlock } from "../lib/db/DBBlock"; import { Multicaster } from "../lib/streams/multicaster"; import { PeerDTO } from "../lib/dto/PeerDTO"; -import { verify } from "duniteroxyde"; +import { verify } from "../../neon/lib"; import { dos2unix } from "../lib/common-libs/dos2unix"; import { rawer } from "../lib/common-libs/index"; import { Server } from "../../server"; diff --git a/neon/README.md b/neon/README.md new file mode 100644 index 0000000000000000000000000000000000000000..72e50726ae403764e4d24075d8625f9ec81a0663 --- /dev/null +++ b/neon/README.md @@ -0,0 +1,39 @@ +# Duniteroxyde + +`duniteroxyde` is a binding for [Duniter] of the [dubp-rs-libs] libraries, a set of libraries to implement the [DUBP] protocol. + +[Duniter]: https://duniter.org/en/ +[dubp-rs-libs]: https://git.duniter.org/libs/dubp-rs-libs +[DUBP]: https://git.duniter.org/documents/rfcs/-/blob/master/rfc/0010_Duniter_Blockchain_Protocol_V12.md + +## How to set up your development environment (or to compile manually) + +### Requirements + +You'll need nvm, yarn, and rust: + +- [yarn install instructions](https://classic.yarnpkg.com/en/docs/install) +- [nvm install instructions](https://github.com/nvm-sh/nvm#installing-and-updating) +- [rust install instructions](https://www.rust-lang.org/learn/get-started) + +Once these tools are installed, use nvm to install node 10: + + nvm install 10 + +Finally, before each work session on Duniteroxide, select Node 10: + + nvm use 10 + +### Compile + +WARNING: It takes a long time and consumes high cpu ! + +Run the following command at the root of the repository: + + yarn + +### Test + +Run the following command at the root of the repository: + + yarn test diff --git a/neon/lib/crypto.js b/neon/lib/crypto.js new file mode 100644 index 0000000000000000000000000000000000000000..18537334af2af98897fb0606c86521de3c20891f --- /dev/null +++ b/neon/lib/crypto.js @@ -0,0 +1,20 @@ +const addon = require('../native/index.node'); + +const { Ed25519Signator, generateRandomSeed, seedToSecretKey, sha256, verify } = addon; + +class KeyPairBuilder { + + static fromSeed(seed) { + return new Ed25519Signator(seed); + } + + static fromSecretKey(secretKey) { + return new Ed25519Signator(secretKey); + } + + static random() { + return addon.generateRandomSeed(); + } +} + +module.exports = { Ed25519Signator, KeyPairBuilder, generateRandomSeed, seedToSecretKey, sha256, verify }; diff --git a/neon/lib/index.d.ts b/neon/lib/index.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..7a5ef5ad420af12593e60df59a7aefb88cdf924b --- /dev/null +++ b/neon/lib/index.d.ts @@ -0,0 +1,91 @@ +/* tslint:disable */ + +export class KeyPairBuilder { + + static fromSeed(seed: Buffer): Ed25519Signator; + + static fromSecretKey(secretKey: string): Ed25519Signator; + + static random(): Ed25519Signator; +} + +export class Ed25519Signator { + + getPublicKey(): string; + + sign(message: Buffer | string): string; +} + +export function generateRandomSeed(): Buffer; +export function seedToSecretKey(seed: Buffer): string; +export function sha256(data: string): string; +export function verify(message: Buffer | string, sig: string, pubkey: string): boolean; + +export class DetailedDistance { + nbSentries: number; + nbSuccess: number; + nbSuccessAtBorder: number; + nbReached: number; + nbReachedAtBorder: number; + isOutdistanced: number; +} + +export class WotBuilder { + static fromWot(wot: Wot): Wot; + + static fromFile(filePath: string): Wot; +} + +export class Wot { + constructor(maxCert: number); + + clear(): void; + + getMaxCert(): number; + + setMaxCert(maxCert: number): void; + + addNode(): number; + + removeNode(): number; + + getWoTSize(): number; + + isEnabled(node_id: number): boolean; + + getEnabled(): number[]; + + setEnabled(enabled: boolean, node_id: number): boolean; + + getDisabled(): number[]; + + getSentries(sentry_requirement: number): number[]; + + getNonSentries(sentry_requirement: number): number[]; + + addLink(source: number, target: number): number; + + existsLink(source: number, target: number): boolean; + + removeLink(source: number, target: number): number; + + isOutdistanced( + node_id: number, + sentry_requirement: number, + step_max: number, + x_percent: number + ): boolean; + + detailedDistance( + nde_id: number, + sentry_requirement: number, + step_max: number, + x_percent: number + ): DetailedDistance; + + getPaths(source: number, target: number, step_max: number): number[][]; + + writeInFile(file_path: string): boolean; + + dump(): string; +} diff --git a/neon/lib/index.js b/neon/lib/index.js new file mode 100644 index 0000000000000000000000000000000000000000..7ebb99a4cf67244f5fc17cfc96799a3baec2ed43 --- /dev/null +++ b/neon/lib/index.js @@ -0,0 +1,4 @@ +const { Wot, WotBuilder } = require('./wot'); +const { Ed25519Signator, KeyPairBuilder, generateRandomSeed, seedToSecretKey, sha256, verify } = require('./crypto'); + +module.exports = { Ed25519Signator, KeyPairBuilder, generateRandomSeed, seedToSecretKey, sha256, verify, Wot, WotBuilder }; diff --git a/neon/lib/wot.js b/neon/lib/wot.js new file mode 100644 index 0000000000000000000000000000000000000000..f2ae9703d6f841a80fb1efebb56b680bd34638f6 --- /dev/null +++ b/neon/lib/wot.js @@ -0,0 +1,16 @@ +const addon = require('../native/index.node'); + +const { Wot } = addon; + +class WotBuilder { + + static fromWot(wot) { + return new Wot(wot.toBytes()); + } + + static fromFile(filePath) { + return new Wot(filePath) + } +} + +module.exports = { Wot, WotBuilder }; diff --git a/neon/native/Cargo.toml b/neon/native/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..7331a0a471d4617948e4b9b10b3531428ad46393 --- /dev/null +++ b/neon/native/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "duniteroxyde" +version = "0.2.9" +authors = ["librelois <elois@ifee.fr>"] +license = "AGPL-3.0" +build = "build.rs" +edition = "2018" +exclude = ["artifacts.json", "index.node"] + +[lib] +name = "duniteroxyde" +crate-type = ["cdylib"] + +[build-dependencies] +neon-build = "0.4.0" + +[dependencies] +bincode = "1.2.1" +bs58 = "0.3.0" +dup-crypto = { version = "0.15.0", default-features = false, features = ["rand"] } +dubp-wot = "0.11.0" +flate2 = "1.0.14" +neon = "0.4.0" diff --git a/neon/native/artifacts.json b/neon/native/artifacts.json new file mode 100644 index 0000000000000000000000000000000000000000..3f346e098060aa63ab49162d2e1b63335e8b2b02 --- /dev/null +++ b/neon/native/artifacts.json @@ -0,0 +1 @@ +{"active":"release","targets":{"debug":{"rustc":"","env":{"npm_config_target":null,"npm_config_arch":null,"npm_config_target_arch":null,"npm_config_disturl":null,"npm_config_runtime":null,"npm_config_build_from_source":null,"npm_config_devdir":null}},"release":{"rustc":"","env":{"npm_config_target":null,"npm_config_arch":null,"npm_config_target_arch":null,"npm_config_disturl":null,"npm_config_runtime":null,"npm_config_build_from_source":null,"npm_config_devdir":null}}}} \ No newline at end of file diff --git a/neon/native/build.rs b/neon/native/build.rs new file mode 100644 index 0000000000000000000000000000000000000000..687a6619460e768d374855902515d54e52d23a56 --- /dev/null +++ b/neon/native/build.rs @@ -0,0 +1,7 @@ +extern crate neon_build; + +fn main() { + neon_build::setup(); // must be called in build.rs + + // add project-specific build logic here... +} diff --git a/neon/native/index.node b/neon/native/index.node new file mode 100644 index 0000000000000000000000000000000000000000..449c33850699110c7b5d0dddeefc2dc2ab218779 Binary files /dev/null and b/neon/native/index.node differ diff --git a/neon/native/src/crypto.rs b/neon/native/src/crypto.rs new file mode 100644 index 0000000000000000000000000000000000000000..76414e8dfbdc51a91b14a6dc7acf7bef828a2186 --- /dev/null +++ b/neon/native/src/crypto.rs @@ -0,0 +1,210 @@ +// Copyright (C) 2020 Éloïs SANCHEZ. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +use crate::into_neon_res; +use dup_crypto::bases::b58::ToBase58; +use dup_crypto::hashs::Hash; +use dup_crypto::keys::{ + ed25519::{ + Ed25519KeyPair, KeyPairFromSeed32Generator, PublicKey as Ed25519PublicKey, + Signator as Ed25519Signator, Signature as Ed25519Signature, + }, + KeyPair, PublicKey, Signator, Signature, +}; +use dup_crypto::seeds::Seed32; +use neon::declare_types; +use neon::prelude::*; +use std::ops::Deref; + +pub fn generate_random_seed(mut cx: FunctionContext) -> JsResult<JsBuffer> { + let seed = into_neon_res( + &mut cx, + Seed32::random().map_err(|_| "fail to generate random seed"), + )?; + + let mut js_buffer = JsBuffer::new(&mut cx, 32)?; + + cx.borrow_mut(&mut js_buffer, |data| { + let slice = data.as_mut_slice::<u8>(); + slice.copy_from_slice(seed.as_ref()); + }); + + Ok(js_buffer) +} + +pub fn seed_to_expanded_base58_secret_key(mut cx: FunctionContext) -> JsResult<JsString> { + let seed_js_buffer = cx.argument::<JsBuffer>(0)?; + + let mut seed_bytes = [0u8; 32]; + cx.borrow(&seed_js_buffer, |data| { + seed_bytes.copy_from_slice(data.as_slice::<u8>()); + }); + let keypair = KeyPairFromSeed32Generator::generate(Seed32::new(seed_bytes)); + + let mut expanded_secret_key_bytes = [0u8; 64]; + expanded_secret_key_bytes[0..32].copy_from_slice(seed_bytes.as_ref()); + expanded_secret_key_bytes[32..64].copy_from_slice(&keypair.public_key().as_ref()[..32]); + + let expanded_base58_secret_key = bs58::encode(expanded_secret_key_bytes.as_ref()).into_string(); + + Ok(cx.string(expanded_base58_secret_key)) +} + +pub fn sha256(mut cx: FunctionContext) -> JsResult<JsString> { + let str_datas = cx.argument::<JsString>(0)?.value(); + Ok(cx.string(Hash::compute_str(&str_datas).to_hex().to_uppercase())) +} + +pub fn verify(mut cx: FunctionContext) -> JsResult<JsBoolean> { + let message = cx.argument::<JsValue>(0)?; + let sig_base58 = cx.argument::<JsString>(1)?.value(); + let public_key_base58 = cx.argument::<JsString>(2)?.value(); + + match Ed25519Signature::from_base64(&sig_base58) { + Ok(signature) => match Ed25519PublicKey::from_base58(&public_key_base58) { + Ok(public_key) => apply_to_js_message(&mut cx, message, |cx, bytes| { + Ok(cx.boolean(public_key.verify(bytes, &signature).is_ok())) + }), + Err(_) => Ok(cx.boolean(false)), + }, + Err(_) => Ok(cx.boolean(false)), + } +} + +declare_types! { + pub class JsKeyPair for Ed25519Signator { + init(mut cx) { + if let Some(arg0) = cx.argument_opt(0) { + if arg0.is_a::<JsString>() { + let expanded_base58_secret_key = arg0 + .downcast::<JsString>() + .or_throw(&mut cx)? + .value(); + into_neon_res(&mut cx, keypair_from_expanded_base58_secret_key(&expanded_base58_secret_key)) + } else if arg0.is_a::<JsBuffer>() { + let seed_js_buffer = arg0 + .downcast::<JsBuffer>() + .or_throw(&mut cx)?; + let mut seed_bytes = [0u8; 32]; + cx.borrow(&seed_js_buffer, |data| { + seed_bytes.copy_from_slice(data.as_slice::<u8>()); + }); + let keypair = KeyPairFromSeed32Generator::generate(Seed32::new(seed_bytes)); + Ok(keypair.generate_signator()) + } else { + cx.throw_type_error("arg0 must be a string") + } + } else { + match Ed25519KeyPair::generate_random() { + Ok(keypair) => Ok(keypair.generate_signator()), + Err(_) => cx.throw_error("fail to generate random keypair"), + } + } + + } + + method getPublicKey(mut cx) { + let this = cx.this(); + let public_key = { + let guard = cx.lock(); + let keypair = this.borrow(&guard); + keypair.public_key() + }; + + Ok(cx.string(public_key.to_base58()).upcast()) + } + + method sign(mut cx) { + let message = cx.argument::<JsValue>(0)?; + apply_to_js_message(&mut cx, message, |cx, bytes| { + sign_bytes(cx, bytes) + }) + } + } +} + +fn keypair_from_expanded_base58_secret_key( + expanded_base58_secret_key: &str, +) -> Result<Ed25519Signator, &'static str> { + let bytes = bs58::decode(expanded_base58_secret_key) + .into_vec() + .map_err(|_| "fail to decode b58")?; + + let mut seed = [0u8; 32]; + seed.copy_from_slice(&bytes[..32]); + let mut pubkey_bytes = [0u8; 32]; + pubkey_bytes.copy_from_slice(&bytes[32..64]); + + let keypair = KeyPairFromSeed32Generator::generate(Seed32::new(seed)); + + //let expected_pubkey = Ed25519PublicKey::try_from(pubkey_bytes.as_ref()); + + if keypair.public_key().as_ref()[..32] == pubkey_bytes { + Ok(keypair.generate_signator()) + } else { + Err("corrupted keypair") + } +} + +fn apply_to_js_message<'c, C: Context<'c>, T, F: FnOnce(&mut C, &[u8]) -> NeonResult<T>>( + cx: &mut C, + message: Handle<'c, JsValue>, + f: F, +) -> NeonResult<T> { + if message.is_a::<JsString>() { + let message_str = message.downcast::<JsString>().or_throw(cx)?.value(); + f(cx, message_str.as_bytes()) + } else if message.is_a::<JsBuffer>() { + let js_buffer = message.downcast::<JsBuffer>().or_throw(cx)?; + let bytes = cx.borrow(&js_buffer, |data| data.as_slice::<u8>()); + f(cx, bytes) + } else { + cx.throw_type_error("Message must be a string or buffer") + } +} + +fn sign_bytes<'c>( + cx: &mut MethodContext<'c, JsKeyPair>, + bytes: &[u8], +) -> NeonResult<Handle<'c, JsValue>> { + let this = cx.this(); + let sig = { + let guard = cx.lock(); + let keypair_box = this.borrow(&guard); + let keypair: &Ed25519Signator = keypair_box.deref(); + keypair.sign(bytes) + }; + + Ok(cx.string(sig.to_base64()).upcast()) +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn test_keypair_from_expanded_base58_secret_key() { + let expanded_base58_secret_key = "51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP"; + + let signator = keypair_from_expanded_base58_secret_key(expanded_base58_secret_key) + .expect("fail to generate keypair"); + + assert_eq!( + "HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd", + signator.public_key().to_base58(), + ) + } +} diff --git a/neon/native/src/lib.rs b/neon/native/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..94aa1964de9374642d5aff07763705765f6ce4d5 --- /dev/null +++ b/neon/native/src/lib.rs @@ -0,0 +1,42 @@ +// Copyright (C) 2020 Éloïs SANCHEZ. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +mod crypto; +mod wot; + +use neon::{prelude::*, register_module}; + +fn into_neon_res<'c, C: Context<'c>, T, S: AsRef<str>>( + context: &mut C, + rust_result: Result<T, S>, +) -> NeonResult<T> { + match rust_result { + Ok(value) => Ok(value), + Err(e) => context.throw_error(e), + } +} + +register_module!(mut cx, { + cx.export_function("generateRandomSeed", crate::crypto::generate_random_seed)?; + cx.export_function( + "seedToSecretKey", + crate::crypto::seed_to_expanded_base58_secret_key, + )?; + cx.export_function("sha256", crate::crypto::sha256)?; + cx.export_function("verify", crate::crypto::verify)?; + cx.export_class::<crate::crypto::JsKeyPair>("Ed25519Signator")?; + cx.export_class::<crate::wot::JsWoT>("Wot")?; + Ok(()) +}); diff --git a/neon/native/src/wot.rs b/neon/native/src/wot.rs new file mode 100644 index 0000000000000000000000000000000000000000..5390df0ce9e11c3b27cd754ca4ae638da198f1f7 --- /dev/null +++ b/neon/native/src/wot.rs @@ -0,0 +1,441 @@ +// Copyright (C) 2020 Éloïs SANCHEZ. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +mod read_from_file; +mod write_in_file; + +use dubp_wot::{ + data::{ + rusty::RustyWebOfTrust, HasLinkResult, NewLinkResult, RemLinkResult, WebOfTrust, WotId, + }, + operations::distance::{ + DistanceCalculator, RustyDistanceCalculator, WotDistance, WotDistanceParameters, + }, + operations::path::{PathFinder, RustyPathFinder}, +}; +use neon::declare_types; +use neon::prelude::*; +use std::ops::Deref; + +declare_types! { + pub class JsWoT for RustyWebOfTrust { + init(mut cx) { + let arg0 = cx.argument::<JsValue>(0)?; + + if arg0.is_a::<JsNumber>() { + let max_links = arg0 + .downcast::<JsNumber>() + .or_throw(&mut cx)? + .value(); + Ok(RustyWebOfTrust::new(max_links as usize)) + } else if arg0.is_a::<JsString>() { + let file_path = arg0 + .downcast::<JsString>() + .or_throw(&mut cx)? + .value(); + match read_from_file::wot_from_file(file_path) { + Ok(wot) => Ok(wot), + Err(e) => cx.throw_error(e), + } + } else if arg0.is_a::<JsArrayBuffer>() { + let js_buffer = arg0 + .downcast::<JsArrayBuffer>() + .or_throw(&mut cx)?; + + let bytes = cx.borrow(&js_buffer, |data| { + let slice = data.as_slice::<u8>(); + Vec::from(slice) + }); + + match bincode::deserialize(&bytes) { + Ok(wot) => Ok(wot), + Err(e) => cx.throw_error(e.to_string()), + } + } else { + panic!("Expected file path or max links."); + } + } + + method clear(mut cx) { + let mut this = cx.this(); + { + let guard = cx.lock(); + let mut wot = this.borrow_mut(&guard); + wot.clear(); + } + + Ok(cx.undefined().upcast()) + } + + method getMaxCert(mut cx) { + let this = cx.this(); + let max_link = { + let guard = cx.lock(); + let wot = this.borrow(&guard); + wot.get_max_link() + }; + Ok(cx.number(max_link as f64).upcast()) + } + + method setMaxCert(mut cx) { + let max_links = cx.argument::<JsNumber>(0)?.value() as usize; + let mut this = cx.this(); + { + let guard = cx.lock(); + let mut wot = this.borrow_mut(&guard); + wot.set_max_link(max_links); + } + Ok(cx.undefined().upcast()) + } + + method addNode(mut cx) { + let mut this = cx.this(); + let wot_id = { + let guard = cx.lock(); + let mut wot = this.borrow_mut(&guard); + wot.add_node() + }; + Ok(cx.number(wot_id.0 as f64).upcast()) + } + + method removeNode(mut cx) { + let mut this = cx.this(); + let wot_id_opt = { + let guard = cx.lock(); + let mut wot = this.borrow_mut(&guard); + wot.rem_node() + }; + + if let Some(wot_id) = wot_id_opt { + Ok(cx.number(wot_id.0 as f64).upcast()) + } else { + cx.throw_error("empty wot") + } + } + + method getWoTSize(mut cx) { + let this = cx.this(); + let wot_size = { + let guard = cx.lock(); + let wot = this.borrow(&guard); + wot.size() + }; + Ok(cx.number(wot_size as f64).upcast()) + } + + method isEnabled(mut cx) { + let wot_id = WotId(cx.argument::<JsNumber>(0)?.value() as usize); + let this = cx.this(); + let is_enabled_opt = { + let guard = cx.lock(); + let wot = this.borrow(&guard); + wot.is_enabled(wot_id) + }; + + if let Some(is_enabled) = is_enabled_opt { + Ok(cx.boolean(is_enabled).upcast()) + } else { + cx.throw_error(format!("node '{}' not exist.", wot_id.0)) + } + } + + method getEnabled(mut cx) { + let this = cx.this(); + let enabled = { + let guard = cx.lock(); + let wot = this.borrow(&guard); + wot.get_enabled() + }; + + vec_wot_id_to_js_array(cx, enabled) + } + + method setEnabled(mut cx) { + let enabled = cx.argument::<JsBoolean>(0)?.value(); + let wot_id = WotId(cx.argument::<JsNumber>(1)?.value() as usize); + + let mut this = cx.this(); + let enabled_opt = { + let guard = cx.lock(); + let mut wot = this.borrow_mut(&guard); + wot.set_enabled(wot_id, enabled) + }; + + if let Some(enabled) = enabled_opt { + Ok(cx.boolean(enabled).upcast()) + } else { + cx.throw_error(format!("node '{}' not exist.", wot_id.0)) + } + } + + method getDisabled(mut cx) { + let this = cx.this(); + let disabled = { + let guard = cx.lock(); + let wot = this.borrow(&guard); + wot.get_disabled() + }; + + vec_wot_id_to_js_array(cx, disabled) + } + + method getSentries(mut cx) { + let sentry_requirement = cx.argument::<JsNumber>(0)?.value(); + let this = cx.this(); + let sentries = { + let guard = cx.lock(); + let wot = this.borrow(&guard); + wot.get_sentries(sentry_requirement as usize) + }; + + vec_wot_id_to_js_array(cx, sentries) + } + + method getNonSentries(mut cx) { + let sentry_requirement = cx.argument::<JsNumber>(0)?.value(); + let this = cx.this(); + let non_sentries = { + let guard = cx.lock(); + let wot = this.borrow(&guard); + wot.get_non_sentries(sentry_requirement as usize) + }; + + vec_wot_id_to_js_array(cx, non_sentries) + } + + method addLink(mut cx) { + let source = WotId(cx.argument::<JsNumber>(0)?.value() as usize); + let target = WotId(cx.argument::<JsNumber>(1)?.value() as usize); + + let mut this = cx.this(); + let new_link_result = { + let guard = cx.lock(); + let mut wot = this.borrow_mut(&guard); + wot.add_link(source, target) + }; + + match new_link_result { + NewLinkResult::Ok(count_target_received_certs) | + NewLinkResult::AllCertificationsUsed(count_target_received_certs) => + Ok(cx.number(count_target_received_certs as f64).upcast()), + NewLinkResult::SelfLinkingForbidden() => cx.throw_error( "self linking forbidden"), + NewLinkResult::UnknownSource() => cx.throw_error(format!("fail to add link {}->{}: unknown source", source.0, target.0)), + NewLinkResult::UnknownTarget() => cx.throw_error(format!("fail to add link {}->{}: unknown target", source.0, target.0)), + } + } + + method existsLink(mut cx) { + let source = WotId(cx.argument::<JsNumber>(0)?.value() as usize); + let target = WotId(cx.argument::<JsNumber>(1)?.value() as usize); + + let this = cx.this(); + let has_link_result = { + let guard = cx.lock(); + let wot = this.borrow(&guard); + wot.has_link(source, target) + }; + + match has_link_result { + HasLinkResult::UnknownSource() => cx.throw_error(format!("fail to check link {}->{}: unknown source", source.0, target.0)), + HasLinkResult::UnknownTarget() => cx.throw_error(format!("fail to check link {}->{}: unknown target", source.0, target.0)), + HasLinkResult::Link(has_link) => Ok(cx.boolean(has_link).upcast()), + } + } + + method removeLink(mut cx) { + let source = WotId(cx.argument::<JsNumber>(0)?.value() as usize); + let target = WotId(cx.argument::<JsNumber>(1)?.value() as usize); + + let mut this = cx.this(); + let rem_link_result = { + let guard = cx.lock(); + let mut wot = this.borrow_mut(&guard); + wot.rem_link(source, target) + }; + + match rem_link_result { + RemLinkResult::Removed(count_target_received_certs) | + RemLinkResult::UnknownCert(count_target_received_certs) => + Ok(cx.number(count_target_received_certs as f64).upcast()), + RemLinkResult::UnknownSource() => cx.throw_error("unknown source"), + RemLinkResult::UnknownTarget() => cx.throw_error("unknown target"), + } + } + + method isOutdistanced(mut cx) { + let distance_params = get_distance_params_from_js(&mut cx)?; + + let this = cx.this(); + let distance_response_opt = { + let guard = cx.lock(); + let wot_box = this.borrow(&guard); + let wot: &RustyWebOfTrust = wot_box.deref(); + RustyDistanceCalculator {}.compute_distance(wot, distance_params) + }; + + if let Some(distance_response) = distance_response_opt { + Ok(cx.boolean(distance_response.outdistanced).upcast()) + } else { + cx.throw_error(format!("node '{}' not exist.", distance_params.node.0)) + } + } + + method detailedDistance(mut cx) { + let distance_params = get_distance_params_from_js(&mut cx)?; + + let this = cx.this(); + let distance_response_opt = { + let guard = cx.lock(); + let wot_box = this.borrow(&guard); + let wot: &RustyWebOfTrust = wot_box.deref(); + RustyDistanceCalculator {}.compute_distance(wot, distance_params) + }; + + if let Some(distance_response) = distance_response_opt { + distance_response_to_js_object(cx, distance_response) + } else { + cx.throw_error(format!("node '{}' not exist.", distance_params.node.0)) + } + } + + method getPaths(mut cx) { + // Get parameters + let from = WotId(cx.argument::<JsNumber>(0)?.value() as usize); + let to = WotId(cx.argument::<JsNumber>(1)?.value() as usize); + let k_max = cx.argument::<JsNumber>(2)?.value() as u32; + + // Call rust PathFinder + let this = cx.this(); + let paths = { + let guard = cx.lock(); + let wot_box = this.borrow(&guard); + let wot: &RustyWebOfTrust = wot_box.deref(); + RustyPathFinder {}.find_paths(wot, from, to, k_max) + }; + + // Convert Vec<Vec<WotId>> to JsArray<JsArray<JsNumber>> + let js_array_paths = JsArray::new(&mut cx, paths.len() as u32); + for (i, path) in paths.iter().enumerate() { + let js_array_path = JsArray::new(&mut cx, path.len() as u32); + for (j, wot_id) in path.iter().enumerate() { + let js_number = cx.number(wot_id.0 as f64); + js_array_path.set(&mut cx, j as u32, js_number)?; + } + js_array_paths.set(&mut cx, i as u32, js_array_path)?; + } + + Ok(js_array_paths.upcast()) + } + + method toBytes(mut cx) { + let this = cx.this(); + let ser_res = { + let guard = cx.lock(); + let wot_box = this.borrow(&guard); + let wot: &RustyWebOfTrust = wot_box.deref(); + bincode::serialize(wot) + }; + + match ser_res { + Ok(bytes) => { + let mut js_buffer = cx.array_buffer(bytes.len() as u32)?; + cx.borrow_mut(&mut js_buffer, |data| { + data.as_mut_slice::<u8>().copy_from_slice(&bytes) + }); + Ok(js_buffer.upcast()) + }, + Err(e) => cx.throw_error(e.to_string()) + } + } + + method writeInFile(mut cx) { + let file_path_str = cx.argument::<JsString>(0)?.value(); + + let this = cx.this(); + let res = { + let guard = cx.lock(); + let wot = this.borrow(&guard); + write_in_file::wot_in_file(file_path_str, wot.deref()) + }; + + match res { + Ok(()) => Ok(cx.boolean(true).upcast()), + Err(e) => cx.throw_error(e), + } + } + + method dump(mut cx) { + let mut dump_wot_chars = Vec::new(); + + let this = cx.this(); + let res = { + let guard = cx.lock(); + let wot = this.borrow(&guard); + wot.dump(&mut dump_wot_chars) + }; + match res { + Ok(()) => match String::from_utf8(dump_wot_chars) { + Ok(dump_wot2_str) => Ok(cx.string(dump_wot2_str).upcast()), + Err(e) => cx.throw_error(e.to_string()), + }, + Err(e) => cx.throw_error(e.to_string()), + } + } + } +} + +fn vec_wot_id_to_js_array( + mut cx: MethodContext<JsWoT>, + vec: Vec<WotId>, +) -> NeonResult<Handle<JsValue>> { + let js_array = JsArray::new(&mut cx, vec.len() as u32); + for (i, wot_id) in vec.iter().enumerate() { + let js_number = cx.number(wot_id.0 as f64); + js_array.set(&mut cx, i as u32, js_number)?; + } + Ok(js_array.upcast()) +} + +fn get_distance_params_from_js(cx: &mut MethodContext<JsWoT>) -> NeonResult<WotDistanceParameters> { + Ok(WotDistanceParameters { + node: WotId(cx.argument::<JsNumber>(0)?.value() as usize), + sentry_requirement: cx.argument::<JsNumber>(1)?.value() as u32, + step_max: cx.argument::<JsNumber>(2)?.value() as u32, + x_percent: cx.argument::<JsNumber>(3)?.value(), + }) +} + +fn distance_response_to_js_object( + mut cx: MethodContext<JsWoT>, + distance_response: WotDistance, +) -> NeonResult<Handle<JsValue>> { + let object = JsObject::new(&mut cx); + + let sentries = cx.number(distance_response.sentries as f64); + let success = cx.number(distance_response.success as f64); + let success_at_border = cx.number(distance_response.success_at_border as f64); + let reached = cx.number(distance_response.reached as f64); + let reached_at_border = cx.number(distance_response.reached_at_border as f64); + let outdistanced = cx.boolean(distance_response.outdistanced); + + object.set(&mut cx, "nbSentries", sentries)?; + object.set(&mut cx, "nbSuccess", success)?; + object.set(&mut cx, "nbSuccessAtBorder", success_at_border)?; + object.set(&mut cx, "nbReached", reached)?; + object.set(&mut cx, "nbReachedAtBorder", reached_at_border)?; + object.set(&mut cx, "isOutdistanced", outdistanced)?; + + Ok(object.upcast()) +} diff --git a/neon/native/src/wot/read_from_file.rs b/neon/native/src/wot/read_from_file.rs new file mode 100644 index 0000000000000000000000000000000000000000..b9d2a75819935886da4adc10c2b13b260bc9b117 --- /dev/null +++ b/neon/native/src/wot/read_from_file.rs @@ -0,0 +1,139 @@ +// Copyright (C) 2020 Éloïs SANCHEZ. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +use dubp_wot::data::{rusty::RustyWebOfTrust, WebOfTrust, WotId}; +use flate2::read::ZlibDecoder; +use std::convert::TryFrom; +use std::fs::File; +use std::io::prelude::*; +use std::io::BufReader; +use std::path::{Path, PathBuf}; + +pub(crate) fn wot_from_file(file_path_str: String) -> Result<RustyWebOfTrust, String> { + let file_path = PathBuf::try_from(&file_path_str).map_err(|e| format!("{}", e))?; + if file_path_str.ends_with(".gz") { + let bytes = read_and_decompress_bytes_from_file(file_path.as_path()) + .map_err(|e| format!("{}", e))?; + if bytes.is_empty() { + Ok(RustyWebOfTrust::default()) + } else { + Ok(bincode::deserialize::<RustyWebOfTrust>(&bytes).map_err(|e| format!("{}", e))?) + } + } else { + let bytes = read_bytes_from_file(file_path.as_path()).map_err(|e| format!("{}", e))?; + from_cpp_wot(&bytes) + } +} + +/// Read bytes from file +fn read_bytes_from_file(file_path: &Path) -> Result<Vec<u8>, std::io::Error> { + let file = File::open(file_path)?; + + let mut buf_reader = BufReader::new(file); + + let mut decompressed_bytes = Vec::new(); + buf_reader.read_to_end(&mut decompressed_bytes)?; + + Ok(decompressed_bytes) +} + +/// Read and decompress bytes from file +fn read_and_decompress_bytes_from_file(file_path: &Path) -> Result<Vec<u8>, std::io::Error> { + if !file_path.exists() { + if let Some(parent) = file_path.parent() { + std::fs::create_dir_all(parent)? + } + File::create(file_path)?; + } + if std::fs::metadata(file_path)?.len() > 0 { + let file = File::open(file_path)?; + let mut z = ZlibDecoder::new(file); + let mut decompressed_bytes = Vec::new(); + z.read_to_end(&mut decompressed_bytes)?; + + Ok(decompressed_bytes) + } else { + Ok(vec![]) + } +} + +fn from_cpp_wot(bytes: &[u8]) -> Result<RustyWebOfTrust, String> { + if bytes.len() < 8 { + return Err("wot file is corrupted".to_owned()); + } + + let mut buffer = [0u8; 4]; + let mut cursor = 0; + + // Read max_links and create empty wot + buffer.copy_from_slice(&bytes[cursor..cursor + 4]); + cursor += 4; + let max_links = u32::from_le_bytes(buffer); + let mut wot = RustyWebOfTrust::new(max_links as usize); + + // Read nodes count + buffer.copy_from_slice(&bytes[cursor..cursor + 4]); + cursor += 4; + let nodes_count = u32::from_le_bytes(buffer); + + for _ in 0..nodes_count { + let wot_id = wot.add_node(); + + // Read enabled + let enabled = bytes[cursor]; + cursor += 1; + if enabled == 0 { + wot.set_enabled(wot_id, false); + } + + // Read certs_count + buffer.copy_from_slice(&bytes[cursor..cursor + 4]); + cursor += 4; + let certs_count = u32::from_le_bytes(buffer); + + // Read certs + for _ in 0..certs_count { + buffer.copy_from_slice(&bytes[cursor..cursor + 4]); + cursor += 4; + let cert_source = WotId(u32::from_le_bytes(buffer) as usize); + wot.add_link(cert_source, wot_id); + } + } + + Ok(wot) +} + +#[cfg(test)] +mod tests { + + use super::*; + use dubp_wot::data::HasLinkResult; + + #[test] + fn test_from_cpp_wot() -> Result<(), std::io::Error> { + let bytes = read_bytes_from_file(PathBuf::from("tests/wotb.bin").as_path())?; + + let wot = from_cpp_wot(&bytes).expect("fail to read cpp wot"); + + assert_eq!(wot.get_max_link(), 100); + assert_eq!(wot.size(), 3394); + assert_eq!( + wot.has_link(WotId(33), WotId(35)), + HasLinkResult::Link(true), + ); + + Ok(()) + } +} diff --git a/neon/native/src/wot/write_in_file.rs b/neon/native/src/wot/write_in_file.rs new file mode 100644 index 0000000000000000000000000000000000000000..746dc40e62a2a714922ccede8b559b41e05a54b1 --- /dev/null +++ b/neon/native/src/wot/write_in_file.rs @@ -0,0 +1,46 @@ +// Copyright (C) 2020 Éloïs SANCHEZ. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +use dubp_wot::data::rusty::RustyWebOfTrust; +use flate2::write::ZlibEncoder; +use flate2::Compression; +use std::convert::TryFrom; +use std::fs::File; +use std::io::prelude::*; +use std::path::{Path, PathBuf}; + +pub(crate) fn wot_in_file(file_path_str: String, wot: &RustyWebOfTrust) -> Result<(), String> { + let file_path = PathBuf::try_from(file_path_str).map_err(|e| format!("{}", e))?; + let bytes = bincode::serialize(wot).map_err(|e| format!("{}", e))?; + + write_and_compress_bytes_in_file(file_path.as_path(), &bytes, flate2::Compression::default()) + .map_err(|e| format!("{}", e))?; + + Ok(()) +} + +/// Write and compress bytes in file +pub(crate) fn write_and_compress_bytes_in_file( + file_path: &Path, + datas: &[u8], + compression: Compression, +) -> Result<(), std::io::Error> { + let file = File::create(file_path)?; + let mut e = ZlibEncoder::new(file, compression); + e.write_all(&datas[..])?; + e.finish()?; + + Ok(()) +} diff --git a/neon/native/tests/g1_genesis.bin.gz b/neon/native/tests/g1_genesis.bin.gz new file mode 100644 index 0000000000000000000000000000000000000000..29617bd5ccb38685e0b06479fabfae0bb8394060 Binary files /dev/null and b/neon/native/tests/g1_genesis.bin.gz differ diff --git a/neon/native/tests/wotb.bin b/neon/native/tests/wotb.bin new file mode 100644 index 0000000000000000000000000000000000000000..6f9678b1dd4ec45e70b649891e212ec6f30de6ca Binary files /dev/null and b/neon/native/tests/wotb.bin differ diff --git a/package.json b/package.json index 91791f494d3cf6d7a0f307bbd7e7d5df0f30fd86..1150aae52f86aca05f17fe8ff450892c6232d560 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,8 @@ "doc": "typedoc --out typedoc/ index.ts app/ --mode file --readme README.md --includeDeclarations --sourcefile-url-prefix \"https://git.duniter.org/nodes/typescript/duniter/blob/loki/\"", "test": "nyc --reporter html mocha", "start": "node bin/duniter start", - "build": "tsc && cd \"node_modules/duniter-ui\" && npm install && npm run build", + "build": "cd neon && neon build --release && cd.. && tsc && cd \"../node_modules/duniter-ui\" && npm install && npm run build", + "install": "cd neon && neon build --release", "lint": "lint-staged", "prettier": "prettier --write app/**/*/*.ts", "reformat": "eslint --cache --fix app/**/*/*.ts", @@ -74,7 +75,6 @@ "commander": "2.9.0", "cors": "2.8.5", "daemonize2": "0.4.2", - "duniteroxyde": "0.2.9", "ddos": "0.2.1", "errorhandler": "1.5.1", "event-stream": "4.0.1", @@ -91,10 +91,11 @@ "morgan": "1.10.0", "multimeter": "0.1.1", "nat-upnp": "^1.1.1", - "prettier": "^2.0.4", + "neon-cli": "^0.4.0", "node-pre-gyp": "0.14.0", "node-uuid": "1.4.8", "optimist": "0.6.1", + "prettier": "^2.0.4", "q-io": "1.13.6", "querablep": "0.1.0", "request": "2.88.2", diff --git a/release/arch/arm/build-arm.sh b/release/arch/arm/build-arm.sh index 31df3047e249f95de65cae00ec006ce0f81723f1..5fe1f1f2178c839ecda63bb11d14f219ce5c4233 100755 --- a/release/arch/arm/build-arm.sh +++ b/release/arch/arm/build-arm.sh @@ -80,9 +80,9 @@ rm -Rf node_modules/duniter-ui/node_modules # Remove non production folders rm -rf coverage test -# Remove unused duniteroxyde intermediate binaries -rm -rf node_modules/duniteroxyde/target -rm -rf node_modules/duniteroxyde/native/target +# Remove unused rust intermediate binaries +rm -rf target +rm -rf neon/native/target cd .. mkdir -p duniter_release diff --git a/release/arch/linux/build-lin.sh b/release/arch/linux/build-lin.sh index 1c509b53b18bfa5ad277fd86baac4330251a83a5..b42b3dc04961d9b1c9f93a838dff9870e8fba989 100644 --- a/release/arch/linux/build-lin.sh +++ b/release/arch/linux/build-lin.sh @@ -190,9 +190,9 @@ cp "${ROOT}/release/resources/leveldown-fix.json" "${RELEASES}/duniter/node_modu # Remove non production folders rm -rf coverage release test -# Remove unused duniteroxyde intermediate binaries -rm -rf node_modules/duniteroxyde/target -rm -rf node_modules/duniteroxyde/native/target +# Remove unused rust intermediate binaries +rm -rf target +rm -rf neon/native/target cp -r "${RELEASES}/duniter" "${RELEASES}/desktop_" || exit 1 cp -r "${RELEASES}/duniter" "${RELEASES}/server_" || exit 1 diff --git a/release/docker/Dockerfile b/release/docker/Dockerfile index 3b8cd3098a3535fdc95df6cb484c8c0244b27480..40d5c49049d44ef495f2ac39256293fc174ab0d9 100644 --- a/release/docker/Dockerfile +++ b/release/docker/Dockerfile @@ -28,8 +28,8 @@ RUN export PATH="$HOME/.cargo/bin:$PATH" && \ yarn add duniter-ui@${DUNITER_UI_VER} && \ mv release/docker/duniter.sh docker.sh && \ rm -rf coverage release test && \ - rm -rf node_modules/duniteroxyde/target && \ - rm -rf node_modules/duniteroxyde/native/target + rm -rf target && \ + rm -rf neon/native/target # ------------------------------------------------------------------------------ # Final Stage diff --git a/test/fast/crypto/crypto.ts b/test/fast/crypto/crypto.ts index a1cf7df733dc4e227c5ea7cd5e05b5510598d05f..655d56c51d6a8199b59ade24fb6831f06eb54e77 100644 --- a/test/fast/crypto/crypto.ts +++ b/test/fast/crypto/crypto.ts @@ -13,7 +13,7 @@ "use strict"; import {Key} from "../../../app/lib/common-libs/crypto/keyring" -import {verify} from "duniteroxyde" +import {verify} from "../../../neon/lib" const should = require('should'); diff --git a/test/fast/crypto/randomKey.ts b/test/fast/crypto/randomKey.ts index 398d37e27f2c0c44db63d24ee3cc0b7a143aded4..be51e29353e07af83219bc811e8ef21422dc3ff5 100644 --- a/test/fast/crypto/randomKey.ts +++ b/test/fast/crypto/randomKey.ts @@ -12,7 +12,7 @@ // GNU Affero General Public License for more details. import {Key, randomKey} from "../../../app/lib/common-libs/crypto/keyring" -import {verify} from "duniteroxyde" +import {verify} from "../../../neon/lib" const should = require('should'); diff --git a/test/fast/modules/common/common-crypto-test.ts b/test/fast/modules/common/common-crypto-test.ts index 6b6b64390b3509a6017cbf0d5c07b4cff11d3262..edb67c66a5e22409bcf62c6105b786b3752f64bc 100644 --- a/test/fast/modules/common/common-crypto-test.ts +++ b/test/fast/modules/common/common-crypto-test.ts @@ -12,7 +12,7 @@ // GNU Affero General Public License for more details. import {Key} from "../../../../app/lib/common-libs/crypto/keyring" -import {verify} from "duniteroxyde" +import {verify} from "../../../../neon/lib" const should = require('should'); diff --git a/test/fast/modules/common/common-random-key.ts b/test/fast/modules/common/common-random-key.ts index 57ac443d9495c93df16857ef21e48fda8110af9e..3d06a0ae52db28017f9884f60032587b9c2ca607 100644 --- a/test/fast/modules/common/common-random-key.ts +++ b/test/fast/modules/common/common-random-key.ts @@ -12,7 +12,7 @@ // GNU Affero General Public License for more details. import {Key, randomKey} from "../../../../app/lib/common-libs/crypto/keyring" -import {verify} from "duniteroxyde" +import {verify} from "../../../../neon/lib" const should = require('should'); diff --git a/test/integration/wot/wotb.ts b/test/integration/wot/wotb.ts index dc35fb631eb98d05c324288683ae565fb5049d70..64ac82aec3478a52c671f28bb19c6a69aba6edd2 100644 --- a/test/integration/wot/wotb.ts +++ b/test/integration/wot/wotb.ts @@ -14,7 +14,7 @@ import {TestUser} from "../tools/TestUser" import {NewTestingServer, TestingServer} from "../tools/toolbox" import {BmaDependency} from "../../../app/modules/bma/index" -import {Wot} from "duniteroxyde" +import {Wot} from "../../../neon/lib" import {Underscore} from "../../../app/lib/common-libs/underscore" import {shutDownEngine} from "../tools/shutdown-engine" import {CommonConstants} from "../../../app/lib/common-libs/constants" diff --git a/test/integration/ws2p/ws2p_connection.ts b/test/integration/ws2p/ws2p_connection.ts index 3e39d94b5b96bc745185cb9761e2bcb2770626a4..9ceb8ccd677d28f5d41888ef58e90ce239fef138 100644 --- a/test/integration/ws2p/ws2p_connection.ts +++ b/test/integration/ws2p/ws2p_connection.ts @@ -19,7 +19,7 @@ import { WS2PRemoteAuth } from "../../../app/modules/ws2p/lib/WS2PConnection" import {Key} from "../../../app/lib/common-libs/crypto/keyring" -import {verify} from "duniteroxyde" +import {verify} from "../../../neon/lib" import {getNewTestingPort} from "../tools/toolbox" import {WS2PMessageHandler} from "../../../app/modules/ws2p/lib/impl/WS2PMessageHandler" import {WS2PResponse} from "../../../app/modules/ws2p/lib/impl/WS2PResponse" diff --git a/test/neon/g1_genesis.bin.gz b/test/neon/g1_genesis.bin.gz new file mode 100644 index 0000000000000000000000000000000000000000..29617bd5ccb38685e0b06479fabfae0bb8394060 Binary files /dev/null and b/test/neon/g1_genesis.bin.gz differ diff --git a/test/neon/test2.bin.gz b/test/neon/test2.bin.gz new file mode 100644 index 0000000000000000000000000000000000000000..e627908dc3f179d4da7918fdaf7d9ec3f97ee572 Binary files /dev/null and b/test/neon/test2.bin.gz differ diff --git a/test/neon/test_crypto.ts b/test/neon/test_crypto.ts new file mode 100644 index 0000000000000000000000000000000000000000..5a6ac3bd333a785fdb44c5b03aa2be48a3efb990 --- /dev/null +++ b/test/neon/test_crypto.ts @@ -0,0 +1,110 @@ +"use strict"; + +import { Ed25519Signator, KeyPairBuilder, sha256, verify, generateRandomSeed, seedToSecretKey } from "../../neon/lib"; +import * as assert from "assert"; + + +let keyPair: Ed25519Signator, rawPub:string, rawSec:string + +describe('ed25519 tests:', function(){ + + before(async () => { + // Generate the keypair + keyPair = KeyPairBuilder.fromSecretKey('51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'); + rawPub = keyPair.getPublicKey(); + //rawSec = keyPair.getSecretKey(); + assert.equal(rawPub, 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd'); + //assert.equal(rawSec, '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'); + }) + + it('sha256 hello', function(done){ + const msg = "hello"; + const hash = sha256(msg); + assert.equal(hash, "2CF24DBA5FB0A30E26E83B2AC5B9E29E1B161E5C1FA7425E73043362938B9824") + done(); + }); + + it('good signature from generated key should be verified', function(done){ + const msg = "Some message to be signed"; + const sig = keyPair.sign(msg); + const verified = verify(msg, sig, rawPub); + assert.equal(verified, true) + done(); + }); + + it('wrong signature from generated key should NOT be verified', function(done){ + const msg = "Some message to be signed"; + const sig = keyPair.sign(msg); + const verified = verify(msg + 'delta', sig, rawPub); + assert.equal(verified, false) + done(); + }); + + it('good signature on a Peer document with just BMA should be verified', function(done){ + const msg = "Version: 10\n" + + "Type: Peer\n" + + "Currency: g1\n" + + "PublicKey: 3AF7bhGQRt6ymcBZgZTBMoDsEtSwruSarjNG8kDnaueX\n" + + "Block: 33291-0000088375C232A4DDAE171BB3D3C51347CB6DC8B7AA8BE4CD4DAEEADF26FEB8\n" + + "Endpoints:\n" + + "BASIC_MERKLED_API g1.duniter.org 10901\n" + const verified = verify(msg, "u8t1IoWrB/C7T+2rS0rKYJfjPG4FN/HkKGFiUO5tILIzjFDvxxQiVC+0o/Vaz805SMmqJvXqornI71U7//+wCg==", "3AF7bhGQRt6ymcBZgZTBMoDsEtSwruSarjNG8kDnaueX"); + assert.equal(verified, true) + done(); + }); + + it('good signature on a Peer document with just BMA + BMAS should be verified', function(done){ + const msg = "Version: 10\n" + + "Type: Peer\n" + + "Currency: g1\n" + + "PublicKey: Com8rJukCozHZyFao6AheSsfDQdPApxQRnz7QYFf64mm\n" + + "Block: 33291-0000088375C232A4DDAE171BB3D3C51347CB6DC8B7AA8BE4CD4DAEEADF26FEB8\n" + + "Endpoints:\n" + + "BASIC_MERKLED_API g1.duniter.tednet.fr 37.187.0.204 8999\n" + + "BMAS g1.duniter.tednet.fr 9000\n" + const verified = verify(msg, "ImvQDdpGv2M6CxSnBuseM/azJhBUGzWVgQhIvb5L2oGLm2GyLk/Sbi5wkb4IjbjbQfdRPdlcx5zxaHhvZCiWAA==", "Com8rJukCozHZyFao6AheSsfDQdPApxQRnz7QYFf64mm"); + assert.equal(verified, true) + done(); + }); + + it('wrong block signature due to oldest tweetnacl should NOT be verified with verify', function(done){ + const msg = "InnerHash: 8B194B5C38CF0A38D16256405AC3E5FA5C2ABD26BE4DCC0C7ED5CC9824E6155B\nNonce: 30400000119992\n"; + const rawSig = "fJusVDRJA8akPse/sv4uK8ekUuvTGj1OoKYVdMQQAACs7OawDfpsV6cEMPcXxrQTCTRMrTN/rRrl20hN5zC9DQ=="; + const verified = verify(msg, rawSig, "D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx"); + assert.equal(verified, false) + done(); + }); + + it('rectified block signature should be verified with verify', function(done){ + const msg = "InnerHash: 8B194B5C38CF0A38D16256405AC3E5FA5C2ABD26BE4DCC0C7ED5CC9824E6155B\nNonce: 30400000119992\n"; + const rawSig = "aZusVDRJA8akPse/sv4uK8ekUuvTGj1OoKYVdMQQ/3+VMaDJ02I795GBBaLgjypZFEKYlPMssJMn/X+F/pxgAw=="; + const verified = verify(msg, rawSig, "D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx"); + assert.equal(verified, true) + done(); + }); + it('generate random keypair', function (done) { + const seed = generateRandomSeed(); + const secretKey = seedToSecretKey(seed); + const keyPair = KeyPairBuilder.fromSecretKey(secretKey); + const msg = "Some message to be signed"; + const sig = keyPair.sign(msg); + const verified = verify(msg, sig, keyPair.getPublicKey()); + assert.equal(verified, true) + done(); + }); + it('membership: should not accept wrong signature', function(done){ + const msg = 'Version: 10\n' + + 'Type: Membership\n' + + 'Currency: bb\n' + + 'Issuer: 6upqFiJ66WV6N3bPc8x8y7rXT3syqKRmwnVyunCtEj7o\n' + + 'Block: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855\n' + + 'Membership: IN\n' + + 'UserID: someuid\n' + + 'CertTS: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855\n'; + const rawSig = "cJohoG/qmxm7KwqCB71RXRSIvHu7IcYB1zWE33OpPLGmedH mdPWad32S7G9j9IDpI8QpldalhdT4BUIHlAtCw=="; + const verified = verify(msg, rawSig, "6upqFiJ66WV6N3bPc8x8y7rXT3syqKRmwnVyunCtEj7o"); + assert.equal(verified, false) + done(); + }); +}); + diff --git a/test/neon/test_wot.ts b/test/neon/test_wot.ts new file mode 100644 index 0000000000000000000000000000000000000000..dbaf0f0e02ebef45515561f3abdd6f1a69582507 --- /dev/null +++ b/test/neon/test_wot.ts @@ -0,0 +1,518 @@ +"use strict"; + +import { Wot, WotBuilder } from "../../neon/lib"; +import * as assert from "assert"; +import * as path from "path"; + +const G1_GENESIS = path.join(__dirname, 'g1_genesis.bin.gz'); +const CPP_FILE = path.join(__dirname, 'wotb.bin'); +const X_PERCENT = 1.0; +const _100_PERCENT = 1.0; +const MAX_DISTANCE_1 = 1; +const MAX_DISTANCE_2 = 2; +const MAX_DISTANCE_3 = 3; +const MAX_DISTANCE_4 = 4; +const MAX_DISTANCE_5 = 5; +const FROM_1_LINK_SENTRIES = 1; +const FROM_2_LINKS_SENTRIES = 2; +const FROM_3_LINKS_SENTRIES = 3; +const __OUTDISTANCED__ = true; +const __OK__ = false; + + function newInstance(launchTest: (arg0: Wot) => any) { + return () => { + let wot = new Wot(3); + launchTest(wot); + } + } + + describe("wotb-rs binding tests", () => { + describe('Basic operations', newInstance((wot) => { + + it('should have 3 max links', function() { + assert.equal(wot.getMaxCert(), 3) + }); + + it('should have an initial size of 0', function() { + assert.equal(wot.getWoTSize(), 0); + }); + + it('should give number 0 if we add a node', function() { + // Add a node + assert.equal(wot.addNode(), 0); + assert.equal(wot.getWoTSize(), 1); + assert.equal(wot.isEnabled(0), true); + var enabled = wot.getEnabled(); + assert.equal(enabled.length, 1); + assert.equal(enabled[0], 0); + assert.equal(wot.getDisabled().length, 0); + // Add another + assert.equal(wot.addNode(), 1); + assert.equal(wot.getWoTSize(), 2); + var enabled2 = wot.getEnabled(); + assert.equal(enabled2.length, 2); + assert.equal(enabled2[1], 1); + assert.equal(wot.getDisabled().length, 0); + // Add 10 nodes + for (let i = 0; i < 10; i++) { + assert.equal(wot.addNode(), i + 2); + } + assert.equal(wot.getWoTSize(), 2 + 10); + assert.equal(wot.getEnabled().length, 2 + 10); + }); + + it('should add certs only in the boundaries of maxCert', () => { + wot.addLink(0, 1); + wot.addLink(0, 2); + wot.addLink(0, 3); + //wot.addLink(0, 4); + assert.equal(wot.getMaxCert(), 3); + assert.equal(wot.existsLink(0, 1), true); + assert.equal(wot.existsLink(0, 2), true); + assert.equal(wot.existsLink(0, 3), true); + assert.equal(wot.existsLink(0, 4), false); + wot.setMaxCert(4); + assert.equal(wot.getMaxCert(), 4); + assert.equal(wot.existsLink(0, 4), false); + wot.addLink(0, 4); + assert.equal(wot.existsLink(0, 4), true); + wot.removeLink(0,1); + wot.removeLink(0,2); + wot.removeLink(0,3); + wot.removeLink(0,4); + }); + + it('should not throw if testing existsLink() with inbounds link', function() { + assert.equal(wot.existsLink(4, 6), false); + }); + + it('first 4 nodes should be enabled', function() { + assert.equal(wot.isEnabled(0), true); + assert.equal(wot.isEnabled(1), true); + assert.equal(wot.isEnabled(2), true); + assert.equal(wot.isEnabled(3), true); + }); + + it('last node should be enabled', function() { + assert.equal(wot.isEnabled(11), true); + }); + + it('should be able to disable some nodes', function() { + assert.equal(wot.setEnabled(false, 0), false); + assert.equal(wot.setEnabled(false, 1), false); + assert.equal(wot.setEnabled(false, 2), false); + assert.equal(wot.getDisabled().length, 3); + assert.equal(wot.setEnabled(true, 1), true); + }); + + it('nodes 0 and 2 should be disabled', function() { + assert.equal(wot.isEnabled(0), false); + assert.equal(wot.isEnabled(1), true); + assert.equal(wot.isEnabled(2), false); + assert.equal(wot.isEnabled(3), true); + // Set enabled again + assert.equal(wot.setEnabled(true, 0), true); + assert.equal(wot.setEnabled(true, 1), true); + assert.equal(wot.setEnabled(true, 2), true); + assert.equal(wot.setEnabled(true, 1), true); + assert.equal(wot.getDisabled().length, 0); + }); + + it('should not exist a link from 2 to 0', function() { + assert.equal(wot.existsLink(2, 0), false); + }); + + it('should be able to add some links', function() { + assert.equal(wot.addLink(2, 0), 1); + assert.equal(wot.addLink(4, 0), 2); + //assert.equal(wot.addLink(4, 0), 2); + assert.equal(wot.addLink(5, 0), 3); + }); + + it('should exist new links', function() { + /** + * WoT is: + * + * 2 --> 0 + * 4 --> 0 + * 5 --> 0 + */ + assert.equal(wot.existsLink(2, 0), true); + assert.equal(wot.existsLink(4, 0), true); + assert.equal(wot.existsLink(5, 0), true); + assert.equal(wot.existsLink(2, 1), false); + }); + + it('should be able to remove some links', function() { + assert.equal(wot.removeLink(4, 0), 2); + /** + * WoT is now: + * + * 2 --> 0 + * 5 --> 0 + */ + }); + + it('should exist less links', function() { + assert.equal(wot.existsLink(2, 0), true); + assert.equal(wot.existsLink(4, 0), false); + assert.equal(wot.existsLink(5, 0), true); + assert.equal(wot.existsLink(2, 1), false); + }); + + it('should successfully use distance rule', function() { + assert.equal(wot.isOutdistanced(0, FROM_1_LINK_SENTRIES, MAX_DISTANCE_1, X_PERCENT), __OK__); // No because 2,4,5 have certified him + assert.equal(wot.isOutdistanced(0, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_1, X_PERCENT), __OK__); // No because only member 2 has 2 certs, and has certified him + assert.equal(wot.isOutdistanced(0, FROM_3_LINKS_SENTRIES, MAX_DISTANCE_1, X_PERCENT), __OK__); // No because no member has issued 3 certifications + // We add links from member 3 + assert.equal(wot.addLink(3, 1), 1); + assert.equal(wot.addLink(3, 2), 1); + /** + * WoT is now: + * + * 2 --> 0 + * 5 --> 0 + * 3 --> 1 + * 3 --> 2 + */ + assert.equal(wot.getWoTSize(), 12); + assert.equal(wot.getSentries(FROM_1_LINK_SENTRIES).length, 1); + assert.equal(wot.getSentries(FROM_1_LINK_SENTRIES)[0], 2); + assert.equal(wot.getSentries(FROM_2_LINKS_SENTRIES).length, 0); + assert.equal(wot.getSentries(FROM_3_LINKS_SENTRIES).length, 0); + assert.equal(wot.getNonSentries(FROM_1_LINK_SENTRIES).length, 11); // 12 - 1 = 11 + assert.equal(wot.getNonSentries(FROM_2_LINKS_SENTRIES).length, 12); // 12 - 0 = 12 + assert.equal(wot.getNonSentries(FROM_3_LINKS_SENTRIES).length, 12); // 12 - 0 = 12 + assert.equal(wot.isOutdistanced(0, FROM_1_LINK_SENTRIES, MAX_DISTANCE_1, X_PERCENT), __OK__); // OK: 2 --> 0 + assert.equal(wot.isOutdistanced(0, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_1, X_PERCENT), __OK__); // OK: 2 --> 0 + assert.equal(wot.isOutdistanced(0, FROM_3_LINKS_SENTRIES, MAX_DISTANCE_1, X_PERCENT), __OK__); // OK: no sentry with 3 links issued + assert.equal(wot.isOutdistanced(0, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_2, X_PERCENT), __OK__); // OK: 2 --> 0 + + wot.addLink(1, 3); + wot.addLink(2, 3); + + assert.equal(wot.getWoTSize(), 12); + assert.equal(wot.getSentries(FROM_1_LINK_SENTRIES).length, 3); + assert.equal(wot.getSentries(FROM_1_LINK_SENTRIES)[0], 1); + assert.equal(wot.getSentries(FROM_1_LINK_SENTRIES)[1], 2); + assert.equal(wot.getSentries(FROM_1_LINK_SENTRIES)[2], 3); + assert.equal(wot.getSentries(FROM_2_LINKS_SENTRIES).length, 1); + assert.equal(wot.getSentries(FROM_2_LINKS_SENTRIES)[0], 3); + assert.equal(wot.getSentries(FROM_3_LINKS_SENTRIES).length, 0); + assert.equal(wot.getNonSentries(FROM_1_LINK_SENTRIES).length, 9); // 12 - 3 = 9 + assert.equal(wot.getNonSentries(FROM_2_LINKS_SENTRIES).length, 11); // 12 - 1 = 11 + assert.equal(wot.getNonSentries(FROM_3_LINKS_SENTRIES).length, 12); // 12 - 0 = 12 + assert.equal(wot.getPaths(3, 0, MAX_DISTANCE_1).length, 0); // KO + assert.equal(wot.getPaths(3, 0, MAX_DISTANCE_2).length, 1); // It exists 3 --> 2 --> 0 + assert.equal(wot.getPaths(3, 0, MAX_DISTANCE_2)[0].length, 3); // It exists 3 --> 2 --> 0 + assert.equal(wot.isOutdistanced(0, FROM_1_LINK_SENTRIES, MAX_DISTANCE_1, X_PERCENT), __OUTDISTANCED__); // KO: No path 3 --> 0 + //assert.equal(wot.isOutdistanced(0, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_1, X_PERCENT), __OUTDISTANCED__); // KO: No path 3 --> 0 + assert.equal(wot.isOutdistanced(0, FROM_3_LINKS_SENTRIES, MAX_DISTANCE_1, X_PERCENT), __OK__); // OK: no sentry with 3 links issued + assert.equal(wot.isOutdistanced(0, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_2, X_PERCENT), __OK__); // OK: 3 --> 2 --> 0 + }); + + it('should have 12 nodes', function() { + assert.equal(wot.getWoTSize(), 12); + }); + + it('delete top node', function() { + assert.equal(wot.removeNode(), 11); + }); + + it('should have 11 nodes', function() { + assert.equal(wot.getWoTSize(), 11); + }); + + it('should work with member 3 disabled', function() { + // With member 3 disabled (non-member) + assert.equal(wot.setEnabled(false, 3), false); + let disabled_nodes = wot.getDisabled(); + assert.equal(disabled_nodes.length, 1); + assert.equal(disabled_nodes[0], 3); + assert.equal(wot.isOutdistanced(0, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_1, X_PERCENT), __OK__); // OK: No path 3 --> 0, but is disabled + }); + + it('should be able to make a mem copy', function() { + const copy = WotBuilder.fromWot(wot); + assert.equal(copy.setEnabled(false, 3), false); + assert.equal(wot.getDisabled().length, 1); + assert.equal(copy.addNode(), 11); + assert.equal(copy.getWoTSize(), 12); + assert.equal(wot.getWoTSize(), 11); + assert.equal(copy.isOutdistanced(0, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_1, X_PERCENT), __OK__); // OK: No path 3 --> 0, but is disabled + }); + })); + + + + describe('Building a larger WoT', newInstance((wot) => { + it('should build a larget WoT', function() { + /* We build WoT: + * + * 0 --> 1 --> 2 --> 4 --> 5 <==> 6 --> 7 + * ^ + * || + * ##==> 3 <-- 8 <-- 9 <========## + * | || + * `> 10 <==> 11 <===## + */ + // Add nodes + for (let i = 0; i < 12; i++) { + assert.equal(wot.addNode(), i); + } + // First line + assert.equal(wot.addLink(0, 1), 1); + assert.equal(wot.addLink(1, 2), 1); + assert.equal(wot.addLink(2, 4), 1); + assert.equal(wot.addLink(4, 5), 1); + assert.equal(wot.addLink(5, 6), 1); + assert.equal(wot.addLink(6, 7), 1); + // 2n level + assert.equal(wot.addLink(2, 3), 1); + assert.equal(wot.addLink(3, 2), 2); + assert.equal(wot.addLink(8, 3), 2); + assert.equal(wot.addLink(9, 8), 1); + // 3rd level + assert.equal(wot.addLink(8, 10), 1); + assert.equal(wot.addLink(10, 11), 1); + assert.equal(wot.addLink(11, 10), 2); + assert.equal(wot.addLink(11, 9), 1); + assert.equal(wot.addLink(9, 11), 2); + + assert.equal(wot.getWoTSize(), 12); + return Promise.resolve(); + }); + + it('should can dump wot', function() { + assert.equal("max_links=3\nnodes_count=12\n000: []\n001: [0]\n002: [1, 3]\n003: [2, 8]\n004: [2]\n005: [4]\n006: [5]\n" + + "007: [6]\n008: [9]\n009: [11]\n010: [8, 11]\n011: [9, 10]\n", wot.dump()); + }); + + describe('testing around 2 with d = 1', () => { + + /** + * Sentries of 1 link (X are not sentries): + * + * X --> 1 --> 2 --> 4 --> 5 <==> 6 --> X + * ^ + * || + * ##==> 3 <-- 8 <-- 9 <========## + * | || + * `> 10 <==> 11 <===## + */ + // => It can be seen 1..6, 8..11 = 10 sentries + // => MINUS the sentry #2 (which is tested and is not to be included) + // => 9 sentries TESTED against member#2 + + it('should have 10 sentries', function() { + assert.equal(wot.getSentries(FROM_1_LINK_SENTRIES).length, 10); + }); + + it('distance k = 1', function() { + assert.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_1, _100_PERCENT), __OUTDISTANCED__); + assert.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_1, 0.5), __OUTDISTANCED__); + // 20% of the sentries: OK + // => 20% x 9 = 2 sentries to reach + // => we have 1 --> 2 + // => we have 3 --> 2 + // => OK (1,3) + assert.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_1, 0.2), __OK__); + // Who can pass 20% can pass 10% + assert.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_1, 0.1), __OK__); + // Can pass 23% (1,98 => 2 sentries) + assert.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_1, 0.22), __OK__); + // But cannot pass 23% (2,07 => 3 sentries) + //assert.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_1, 0.23), __OUTDISTANCED__); + }); + + it('distance k = 2', function() { + assert.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_2, _100_PERCENT), __OUTDISTANCED__); + //assert.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_2, 0.5), __OUTDISTANCED__); + // 33% of the sentries: OK + // => 33% x 9 = 3 sentries to reach + // With k = 2 we have the following paths: + // 1 --> 2 + // 8 --> 3 --> 2 + // => OK (1,8,3) + assert.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_2, 0.33), __OK__); + assert.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_2, 0.3), __OK__); + assert.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_2, 0.2), __OK__); + assert.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_2, 0.1), __OK__); + // But cannot pass 34% (3,06 => 4 sentries) + //assert.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_2, 0.34), __OUTDISTANCED__); + }); + + it('distance k = 5', function() { + assert.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_5, _100_PERCENT), __OUTDISTANCED__); + // 66% of the sentries: OK + // => 66% x 9 = 6 sentries to reach + // With k = 5 we have the following paths: + // 1 --> 2 + // 10 --> 11 --> 9 --> 8 --> 3 --> 2 + // => OK (1,10,11,9,8,3) + assert.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_5, 0.66), __OK__); + assert.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_5, 0.3), __OK__); + assert.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_5, 0.2), __OK__); + assert.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_5, 0.1), __OK__); + // But cannot pass 67% (6,03 => 7 sentries) + //assert.equal(wot.isOutdistanced(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_5, 0.67), __OUTDISTANCED__); + assert.deepEqual(wot.detailedDistance(2, FROM_1_LINK_SENTRIES, MAX_DISTANCE_5, 0.67), { + nbReached: 7, // +1 compared to reached sentries, because of member `0` + nbReachedAtBorder: 1, + nbSuccess: 6, + nbSuccessAtBorder: 1, + nbSentries: 9, + isOutdistanced: false + }); + }); + }); + + describe('testing around 2 with d = 2', () => { + /** + * Sentries of 2 links (X are not sentries): + * + * X --> X --> 2 --> X --> X <==> X --> X + * ^ + * || + * ##==> X <-- X <-- X <========## + * | || + * `> X <==> 11 <===## + */ + // => It can be seen 2,6,8,9,11 = 5 sentries + // => MINUS the sentry #2 (which is tested and is not to be included) + // => 4 sentries + + it('should have 2 sentries', function() { + assert.equal(wot.getSentries(FROM_2_LINKS_SENTRIES).length, 2); + }); + + it('distance k = 1', function() { + //assert.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_1, _100_PERCENT), __OUTDISTANCED__); + // With k = 1 we have no paths + // => ALWAYS KO + //assert.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_1, 0.99), __OUTDISTANCED__); + //assert.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_1, 0.5), __OUTDISTANCED__); + //assert.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_1, 0.01), __OUTDISTANCED__); + }); + + it('distance k = 2', function() { + // Always distanced with k = 2 + //assert.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_2, _100_PERCENT), __OUTDISTANCED__); + //assert.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_2, 0.25), __OUTDISTANCED__); + //assert.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_2, 0.24), __OUTDISTANCED__); + //assert.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_2, 0.251), __OUTDISTANCED__); + }); + + it('distance k = 3', function() { + // Always distanced with k = 2 + //assert.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_3, _100_PERCENT), __OUTDISTANCED__); + //assert.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_3, 0.50), __OUTDISTANCED__); + //assert.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_3, 0.49), __OUTDISTANCED__); + //assert.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_3, 0.51), __OUTDISTANCED__); + }); + + it('distance k = 4', function() { + // Only 1 sentry at distance 4: always OK + assert.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_4, _100_PERCENT), __OK__); + assert.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_4, 0.75), __OK__); + assert.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_4, 0.01), __OK__); + assert.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_4, 0.99), __OK__); + }); + + it('distance k = 5', function() { + // Only 1 sentry at distance 4: always OK + assert.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_5, _100_PERCENT), __OK__); + assert.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_5, 0.75), __OK__); + assert.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_5, 0.01), __OK__); + assert.equal(wot.isOutdistanced(2, FROM_2_LINKS_SENTRIES, MAX_DISTANCE_5, 0.99), __OK__); + }); + }); + + describe('testing around 2 with d = 3', () => { + /** + * Sentries of 3 links (X are not sentries): + * + * X --> X --> 2 --> X --> X <==> X --> X + * ^ + * || + * ##==> X <-- X <-- X <========## + * | || + * `> X <==> X <===## + */ + // => It can be seen 2 = 1 sentries + // => MINUS the sentry #2 (which is tested and is not to be included) + // => 0 sentries + // => ALWAYS OK, no sentries to constraint + + it('distance k = 1', function() { + assert.equal(wot.isOutdistanced(2, FROM_3_LINKS_SENTRIES, MAX_DISTANCE_1, _100_PERCENT), __OK__); + assert.equal(wot.isOutdistanced(2, FROM_3_LINKS_SENTRIES, MAX_DISTANCE_1, 0.01), __OK__); + }); + + it('distance k = 2', function() { + assert.equal(wot.isOutdistanced(2, FROM_3_LINKS_SENTRIES, MAX_DISTANCE_2, _100_PERCENT), __OK__); + assert.equal(wot.isOutdistanced(2, FROM_3_LINKS_SENTRIES, MAX_DISTANCE_2, 0.01), __OK__); + }); + + it('distance k = 5', function() { + assert.equal(wot.isOutdistanced(2, FROM_3_LINKS_SENTRIES, MAX_DISTANCE_5, _100_PERCENT), __OK__); + assert.equal(wot.isOutdistanced(2, FROM_3_LINKS_SENTRIES, MAX_DISTANCE_5, 0.01), __OK__); + }); + }); + })); + + describe('tests open written wot file', newInstance((wot) => { + + it('should can add 12 nodes', function() { + for (let i = 0; i < 12; i++) { + assert.equal(wot.addNode(), i); + } + assert.equal(wot.getWoTSize(), 12); + }); + + it('should add node, write and read new wot with 13 nodes', function() { + wot.addNode(); + assert.equal(wot.getWoTSize(), 13); + assert.equal(wot.writeInFile("test2.bin.gz"), true) + wot.clear(); + assert.equal(wot.getWoTSize(), 0); + let wot2 = WotBuilder.fromFile("test2.bin.gz"); + assert.equal(wot2.getWoTSize(), 13); + }); + })); + + describe('tests g1 genesis wot', newInstance((wot) => { + + before(() => { + wot = WotBuilder.fromFile(G1_GENESIS); + }); + + it('should have a wot size of 59', function() { + assert.equal(wot.getWoTSize(), 59); + }); + + it('should have only enabled members', function() { + assert.equal(wot.getEnabled().length, 59); + assert.equal(wot.getDisabled().length, 0); + }); + + it('should have 48 sentries', function() { + assert.equal(wot.getSentries(FROM_3_LINKS_SENTRIES).length, 48); + }); + })); + + describe('tests cpp wot', newInstance((wot) => { + + before(() => { + wot = WotBuilder.fromFile(CPP_FILE); + }); + + it('should have 100 max links', function() { + assert.equal(wot.getMaxCert(), 100) + }); + + it('should have 3394 nodes', function() { + assert.equal(wot.getWoTSize(), 3394) + }); + })); + }); diff --git a/test/neon/wotb.bin b/test/neon/wotb.bin new file mode 100644 index 0000000000000000000000000000000000000000..6f9678b1dd4ec45e70b649891e212ec6f30de6ca Binary files /dev/null and b/test/neon/wotb.bin differ diff --git a/yarn.lock b/yarn.lock index caa21959b17e9ef1c68725f1cd9ce441c33408bf..a5c421932dc393c9fa218d771537ed6bc6e0f37a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1367,13 +1367,6 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -duniteroxyde@0.2.9: - version "0.2.9" - resolved "https://registry.yarnpkg.com/duniteroxyde/-/duniteroxyde-0.2.9.tgz#ba6923977854357bb98dac9f4308f2243031eec8" - integrity sha512-0nZkqIPdYdMg8+JvvwhBgO/XU48SX7mgH/pzp84qfmF7qTMoLg3i9Y6L60N0vDRwyLLSHGMdlVxcKOB3FiL43A== - dependencies: - neon-cli "^0.4.0" - duplexer@^0.1.1, duplexer@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"