From 87ba1c23e5b2a34e57bb9510eb10de76dca3f78d Mon Sep 17 00:00:00 2001 From: otsmr Date: Mon, 13 Apr 2026 02:28:41 +0200 Subject: [PATCH] started with ud --- rust/.gitignore | 2 + rust/Cargo.lock | 560 +++++++++++++++++- rust/Cargo.toml | 11 +- rust/build.rs | 5 + .../{user_discovery/memory.rs => sss/sss.rs} | 0 rust/src/twonly/database/contact.rs | 30 +- rust/src/twonly/mod.rs | 36 ++ rust/src/user_discovery/README.md | 10 + rust/src/user_discovery/error.rs | 28 + rust/src/user_discovery/mod.rs | 471 +++++++++++++++ .../user_discovery/stores/in_memory_store.rs | 102 ++++ rust/src/user_discovery/stores/mod.rs | 2 + rust/src/user_discovery/traits.rs | 50 ++ rust/src/user_discovery/types.proto | 52 ++ rust/tests/testing.db | Bin 0 -> 131072 bytes test/rust/create_db_for_rust_testing.dart | 56 ++ 16 files changed, 1394 insertions(+), 21 deletions(-) create mode 100644 rust/build.rs rename rust/src/{user_discovery/memory.rs => sss/sss.rs} (100%) create mode 100644 rust/src/user_discovery/README.md create mode 100644 rust/src/user_discovery/error.rs create mode 100644 rust/src/user_discovery/stores/in_memory_store.rs create mode 100644 rust/src/user_discovery/stores/mod.rs create mode 100644 rust/src/user_discovery/traits.rs create mode 100644 rust/src/user_discovery/types.proto create mode 100644 rust/tests/testing.db create mode 100644 test/rust/create_db_for_rust_testing.dart diff --git a/rust/.gitignore b/rust/.gitignore index ea8c4bf..dfb400c 100644 --- a/rust/.gitignore +++ b/rust/.gitignore @@ -1 +1,3 @@ /target + +tests/tmp_testing.db* \ No newline at end of file diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 5276fa8..12b0104 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -71,9 +71,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "atoi" @@ -90,6 +90,15 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -132,6 +141,17 @@ dependencies = [ "serde_core", ] +[[package]] +name = "blahaj" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5106bf2680d585dc5f29711b8aa5dde353180b8e14af89b7f0424f760c84e7ce" +dependencies = [ + "hashbrown 0.15.5", + "rand 0.8.5", + "zeroize", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -187,6 +207,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chacha20" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.0", +] + [[package]] name = "chrono" version = "0.4.44" @@ -198,6 +229,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "cobs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -238,6 +278,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc" version = "3.4.0" @@ -253,6 +302,12 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + [[package]] name = "crossbeam-queue" version = "0.3.12" @@ -360,6 +415,18 @@ dependencies = [ "serde", ] +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "env_filter" version = "0.1.4" @@ -408,12 +475,24 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + [[package]] name = "find-msvc-tools" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "flume" version = "0.11.1" @@ -609,12 +688,35 @@ dependencies = [ "wasi", ] +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "rand_core 0.10.0", + "wasip2", + "wasip3", +] + [[package]] name = "gimli" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -627,6 +729,8 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ + "allocator-api2", + "equivalent", "foldhash 0.1.5", ] @@ -656,6 +760,20 @@ dependencies = [ "hashbrown 0.15.5", ] +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "serde", + "spin 0.9.8", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.5.0" @@ -807,6 +925,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "idna" version = "1.1.0" @@ -836,6 +960,17 @@ checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", "hashbrown 0.17.0", + "serde", + "serde_core", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", ] [[package]] @@ -862,6 +997,12 @@ dependencies = [ "spin 0.5.2", ] +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libc" version = "0.2.184" @@ -897,6 +1038,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + [[package]] name = "litemap" version = "0.8.2" @@ -954,6 +1101,21 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "num-bigint-dig" version = "0.8.6" @@ -965,7 +1127,7 @@ dependencies = [ "num-integer", "num-iter", "num-traits", - "rand", + "rand 0.8.5", "smallvec", "zeroize", ] @@ -1080,6 +1242,17 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "petgraph" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" +dependencies = [ + "fixedbitset", + "hashbrown 0.15.5", + "indexmap", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -1131,6 +1304,19 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +[[package]] +name = "postcard" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "heapless", + "serde", +] + [[package]] name = "potential_utf" version = "0.1.5" @@ -1149,6 +1335,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -1158,6 +1354,57 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prost" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343d3bd7056eda839b03204e68deff7d1b13aba7af2b2fd16890697274262ee7" +dependencies = [ + "heck", + "itertools", + "log", + "multimap", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8991c4cbdb8bc5b11f0b074ffe286c30e523de90fee5ba8132f1399f23cb3dd7" +dependencies = [ + "prost", +] + [[package]] name = "quote" version = "1.0.45" @@ -1167,6 +1414,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "rand" version = "0.8.5" @@ -1175,7 +1428,18 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +dependencies = [ + "chacha20", + "getrandom 0.4.2", + "rand_core 0.10.0", ] [[package]] @@ -1185,7 +1449,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -1194,9 +1458,15 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.17", ] +[[package]] +name = "rand_core" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" + [[package]] name = "redox_syscall" version = "0.5.18" @@ -1257,7 +1527,7 @@ dependencies = [ "num-traits", "pkcs1", "pkcs8", - "rand_core", + "rand_core 0.6.4", "signature", "spki", "subtle", @@ -1268,11 +1538,18 @@ dependencies = [ name = "rust_lib_twonly" version = "0.1.0" dependencies = [ + "blahaj", "flutter_rust_bridge", + "postcard", + "prost", + "prost-build", + "rand 0.10.1", + "serde", "sqlx", "thiserror", "tokio", "tracing", + "tracing-subscriber", ] [[package]] @@ -1281,6 +1558,28 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + [[package]] name = "ryu" version = "1.0.23" @@ -1293,6 +1592,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + [[package]] name = "serde" version = "1.0.228" @@ -1355,7 +1660,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest", ] @@ -1366,10 +1671,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1393,7 +1707,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -1565,7 +1879,7 @@ dependencies = [ "md-5", "memchr", "percent-encoding", - "rand", + "rand 0.8.5", "rsa", "serde", "sha1", @@ -1603,7 +1917,7 @@ dependencies = [ "log", "md-5", "memchr", - "rand", + "rand 0.8.5", "serde", "serde_json", "sha2", @@ -1685,6 +1999,18 @@ dependencies = [ "syn", ] +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "thiserror" version = "2.0.18" @@ -1705,6 +2031,15 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + [[package]] name = "threadpool" version = "1.8.1" @@ -1808,6 +2143,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", ] [[package]] @@ -1843,6 +2204,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "url" version = "2.5.8" @@ -1861,6 +2228,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + [[package]] name = "vcpkg" version = "0.2.15" @@ -1879,6 +2252,24 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "wasite" version = "0.1.0" @@ -1951,6 +2342,40 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + [[package]] name = "web-sys" version = "0.3.66" @@ -2030,6 +2455,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.59.0" @@ -2112,6 +2546,94 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + [[package]] name = "writeable" version = "0.6.3" @@ -2187,6 +2709,20 @@ name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "zerotrie" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 528bd71..1ee6689 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -20,6 +20,13 @@ sqlx = { version = "0.9.0-alpha.1", default-features = false, features = [ ] } tokio = { version = "1.44", features = ["full"] } tracing = "0.1.44" +serde = "1.0.228" +prost = "0.14.1" +rand = "0.10.1" +tracing-subscriber = "0.3.23" +blahaj = "0.6.0" +postcard = { version = "1.1.3", features = ["alloc"] } -[lints.rust] -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(frb_expand)'] } + +[build-dependencies] +prost-build = "0.14.1" diff --git a/rust/build.rs b/rust/build.rs new file mode 100644 index 0000000..135361e --- /dev/null +++ b/rust/build.rs @@ -0,0 +1,5 @@ +use std::io::Result; +fn main() -> Result<()> { + prost_build::compile_protos(&["src/user_discovery/types.proto"], &["src/"])?; + Ok(()) +} diff --git a/rust/src/user_discovery/memory.rs b/rust/src/sss/sss.rs similarity index 100% rename from rust/src/user_discovery/memory.rs rename to rust/src/sss/sss.rs diff --git a/rust/src/twonly/database/contact.rs b/rust/src/twonly/database/contact.rs index 9d48df4..084d3e0 100644 --- a/rust/src/twonly/database/contact.rs +++ b/rust/src/twonly/database/contact.rs @@ -1,9 +1,5 @@ -// Table is defined in contacts.table.dart - -use sqlx::{ - types::chrono::{DateTime, Utc}, - FromRow, -}; +// use sqlx::types::chrono::{DateTime, Utc}; +use sqlx::FromRow; use crate::twonly::{database::Database, error::Result}; @@ -11,7 +7,7 @@ use crate::twonly::{database::Database, error::Result}; struct ContactRow { pub(crate) user_id: i64, pub(crate) username: String, - pub(crate) created_at: DateTime, + // pub(crate) created_at: DateTime, } pub struct Contact { @@ -38,3 +34,23 @@ impl Contact { Ok(rows.into_iter().map(Into::into).collect()) } } + +#[cfg(test)] +mod tests { + use crate::twonly::{database::contact::Contact, tests::initialize_twonly_for_testing}; + + #[tokio::test] + async fn test_get_all_contacts() { + let twonly = initialize_twonly_for_testing().await.unwrap(); + + let contacts = Contact::get_all_contacts(&twonly.database).await.unwrap(); + + let users = vec!["alice", "bob", "charlie", "diana", "eve", "frank", "grace"]; + + assert_eq!(contacts.len(), users.len()); + + for contact in contacts { + assert_eq!(users[contact.user_id as usize], &contact.username); + } + } +} diff --git a/rust/src/twonly/mod.rs b/rust/src/twonly/mod.rs index 9186868..bf4e0f9 100644 --- a/rust/src/twonly/mod.rs +++ b/rust/src/twonly/mod.rs @@ -11,6 +11,7 @@ pub struct TwonlyConfig { } struct Twonly { + #[allow(dead_code)] pub(crate) config: TwonlyConfig, database: Arc, } @@ -39,3 +40,38 @@ pub async fn get_all_contacts() -> Result> { let twonly = get_instance()?; Contact::get_all_contacts(twonly.database.as_ref()).await } + +#[cfg(test)] +pub(crate) mod tests { + use super::error::Result; + use super::Twonly; + use std::path::PathBuf; + + use crate::twonly::{get_instance, initialize_twonly, TwonlyConfig}; + + pub(super) async fn initialize_twonly_for_testing() -> Result<&'static Twonly> { + let default_twonly_database = PathBuf::from("tests/testing.db"); + + if !default_twonly_database.is_file() { + panic!("{} not found!", default_twonly_database.display()) + } + + let copied_twonly_database = default_twonly_database + .parent() + .unwrap() + .join("tmp_testing.db"); + if copied_twonly_database.exists() { + std::fs::remove_file(&copied_twonly_database).unwrap(); + } + + std::fs::copy(default_twonly_database, &copied_twonly_database).unwrap(); + + initialize_twonly(TwonlyConfig { + database_path: copied_twonly_database.display().to_string(), + }) + .await + .unwrap(); + + get_instance() + } +} diff --git a/rust/src/user_discovery/README.md b/rust/src/user_discovery/README.md new file mode 100644 index 0000000..1a5368b --- /dev/null +++ b/rust/src/user_discovery/README.md @@ -0,0 +1,10 @@ + + + + // **Store** promotions: + // version_id, contact_id, Option + // -> In case a contact_id deleted his account deleted or was removed + // - Remove the previous row (version_id must be increased...) + // - Create a new entry with User Discovery Recall + // -> Otherwise this promotion contains the Promotion + // \ No newline at end of file diff --git a/rust/src/user_discovery/error.rs b/rust/src/user_discovery/error.rs new file mode 100644 index 0000000..0c0246e --- /dev/null +++ b/rust/src/user_discovery/error.rs @@ -0,0 +1,28 @@ +use prost::DecodeError; +use thiserror::Error; + +pub type Result = core::result::Result; + +#[derive(Error, Debug)] +pub enum UserDiscoveryError { + #[error("The encrypted announcement data contains malicious data: `{0}`")] + MaliciousAnnouncementData(String), + + #[error("no shares left.")] + NoSharesLeft, + + #[error("User discovery contains no configuration.")] + NotInitialized, + + #[error("`{0}`")] + PostcardError(#[from] postcard::Error), + + #[error("error while calculating shamirs secret shares: `{0}`")] + ShamirsSecret(String), + + #[error("tried to push a invalid version")] + PushedInvalidVersion, + + #[error("`{0}`")] + Prost(#[from] DecodeError), +} diff --git a/rust/src/user_discovery/mod.rs b/rust/src/user_discovery/mod.rs index e69de29..6701a67 100644 --- a/rust/src/user_discovery/mod.rs +++ b/rust/src/user_discovery/mod.rs @@ -0,0 +1,471 @@ +mod error; +pub mod stores; +mod traits; + +use blahaj::{Share, Sharks}; +use postcard::{from_bytes, to_allocvec}; +use prost::Message; +use serde::{Deserialize, Serialize}; +use crate::user_discovery::error::{Result, UserDiscoveryError}; +use crate::user_discovery::traits::UserDiscoveryUtils; +use crate::user_discovery::user_discovery_message::{UserDiscoveryAnnouncement, UserDiscoveryPromotion}; +use crate::user_discovery::user_discovery_message::user_discovery_promotion::AnnouncementShareDecrypted; +use crate::user_discovery::user_discovery_message::user_discovery_promotion::announcement_share_decrypted::SignedData; +pub use traits::UserDiscoveryStore; + +pub type UserID = i64; + +include!(concat!(env!("OUT_DIR"), "/_.rs")); + +#[derive(Serialize, Deserialize, Clone, Debug)] +struct UserDiscoveryConfig { + /// The number of required shares to get the secret + threshold: u8, + /// Currently limited to <= 255 as GF 256 is used + total_number_of_shares: usize, + /// Version of announcements + announcement_version: u32, + /// Version of promotions + promotion_version: u32, + /// This is a random public_id associated with a single announcement. + public_id: u64, + /// Verification shares + verification_shares: Vec>, +} + +impl Default for UserDiscoveryConfig { + fn default() -> Self { + Self { + threshold: 2, + total_number_of_shares: 255, + announcement_version: 0, + promotion_version: 0, + verification_shares: vec![], + public_id: 0, + } + } +} + +pub struct UserDiscovery +where + Store: UserDiscoveryStore, + Utils: UserDiscoveryUtils, +{ + store: Store, + utils: Utils, +} + +impl UserDiscovery { + pub fn new(store: Store, utils: Utils) -> Result { + Ok(Self { store, utils }) + } + + pub fn initialize_or_update( + &self, + threshold: u8, + user_id: UserID, + public_key: Vec, + ) -> Result<()> { + let mut config = match self.store.get_config() { + Ok(config) => { + let mut config: UserDiscoveryConfig = from_bytes(&config)?; + config.threshold = threshold; + config + } + Err(_) => UserDiscoveryConfig { + threshold, + ..Default::default() + }, + }; + + let public_id = rand::random(); + + let signed_data = SignedData { + public_id, + user_id, + public_key, + }; + + let signature = self.utils.sign_data(signed_data.encode_to_vec())?; + + let verification_shares = self.setup_announcements(&config, signed_data, signature)?; + + config.public_id = public_id; + config.announcement_version += 1; + config.verification_shares = verification_shares; + + self.store.update_config(to_allocvec(&config)?)?; + + Ok(()) + } + + fn setup_announcements( + &self, + config: &UserDiscoveryConfig, + signed_data: SignedData, + signature: Vec, + ) -> Result>> { + tracing::debug!( + "Initializing user discovery with {} total shares and with a threshold of {}", + config.total_number_of_shares, + config.threshold + ); + + let encrypted_announcement = AnnouncementShareDecrypted { + signed_data: Some(signed_data), + signature, + } + .encode_to_vec(); + + let sharks = Sharks(config.threshold as u8); + let dealer = sharks.dealer(&encrypted_announcement); + + let mut shares: Vec> = dealer + .take(config.total_number_of_shares) + .map(|x| Vec::from(&x)) + .collect(); + + if shares.len() != config.total_number_of_shares as usize + || shares.is_empty() + || shares.len() <= (config.threshold as usize * 2) + { + return Err(UserDiscoveryError::ShamirsSecret( + "Invalid length of shares where generated".to_string(), + )); + } + + tracing::debug!( + "Generated {} shares each with a size of: {}", + shares.len(), + shares[0].len() + ); + + let mut verification_shares = vec![]; + + let split_index = shares.len() - (config.threshold - 1) as usize; + verification_shares.extend(shares.drain(split_index..)); + + self.store.set_shares(shares)?; + + Ok(verification_shares) + } + + fn get_config(&self) -> Result { + Ok(from_bytes(&self.store.get_config()?)?) + } + + /// Returns the current version of the user discovery. + pub fn get_current_version(&self) -> Result> { + let config = self.get_config()?; + Ok(UserDiscoveryVersion { + announcement: config.announcement_version, + promotion: config.promotion_version, + } + .encode_to_vec()) + } + + /// Returns all new user discovery messages for the provided contact + pub fn get_new_messages( + &self, + contact_id: UserID, + received_version: &[u8], + ) -> Result>> { + let mut messages = vec![]; + let received_version = UserDiscoveryVersion::decode(received_version)?; + + let config = self.get_config()?; + let version = Some(UserDiscoveryVersion { + announcement: config.announcement_version, + promotion: config.promotion_version, + }); + + if received_version.announcement < config.announcement_version { + tracing::info!("New announcement message available for {}", contact_id); + + let announcement_share = self.store.get_share_for_contact(contact_id)?; + + let user_discovery_announcement = Some(UserDiscoveryAnnouncement { + public_id: config.public_id, + threshold: config.threshold as u32, + announcement_share, + verification_shares: config.verification_shares, + }); + + messages.push( + UserDiscoveryMessage { + user_discovery_announcement, + version, + ..Default::default() + } + .encode_to_vec(), + ); + + // messages.push(value); + } + if received_version.promotion < config.promotion_version { + tracing::info!("New promotion message available for user {}", contact_id); + let promoting_messages = self + .store + .get_promotions_after_version(received_version.promotion)?; + messages.extend_from_slice(&promoting_messages); + } + Ok(messages) + } + + /// Checks if the provided user has new announcements. + /// In this case the user should be requested to send there updates. + pub fn should_request_new_messages(&self, contact_id: UserID, version: &[u8]) -> Result { + let received_version = UserDiscoveryVersion::decode(version)?; + let stored_version = match self.store.get_contact_version(contact_id)? { + Some(buf) => UserDiscoveryVersion::decode(buf.as_slice())?, + None => UserDiscoveryVersion { + announcement: 0, + promotion: 0, + }, + }; + Ok(received_version.announcement > stored_version.announcement + || received_version.promotion < received_version.promotion) + } + + pub(crate) fn get_contact_version(&self, contact_id: UserID) -> Result>> { + self.store.get_contact_version(contact_id) + } + + /// Returns the latest version for this discovery. + /// Before calling this function the application must sure that contact_id is qualified to be announced. + pub fn handle_user_discovery_messages( + &self, + contact_id: UserID, + messages: Vec>, + ) -> Result<()> { + for message in messages { + let message = UserDiscoveryMessage::decode(message.as_slice())?; + let Some(version) = message.version else { + continue; + }; + + if let Some(uda) = message.user_discovery_announcement { + self.handle_user_discovery_announcement(contact_id, uda)?; + } + if let Some(udp) = message.user_discovery_promotion { + self.handle + tracing::info!("Got UDP from {contact_id}"); + } + + self.store + .set_contact_version(contact_id, version.encode_to_vec())?; + } + + Ok(()) + } + + fn handle_user_discovery_announcement( + &self, + contact_id: UserID, + uda: UserDiscoveryAnnouncement, + ) -> Result<()> { + tracing::info!("Got a user discovery announcement from {contact_id}."); + + if uda.threshold as usize != uda.verification_shares.len() + 1 { + tracing::error!( + "UDA contains to few shares to verify: {} != {} + 1.", + uda.threshold, + uda.verification_shares.len(), + ); + return Ok(()); + } + + let sharks = Sharks(uda.threshold as u8); + + let mut all_shares = uda.verification_shares.clone(); + all_shares.push(uda.announcement_share.clone()); + let shares: Vec<_> = all_shares + .iter() + .filter_map(|x| Share::try_from(x.as_slice()).ok()) + .collect(); + + match sharks.recover(&shares) { + Ok(secret) => { + let asd = AnnouncementShareDecrypted::decode(secret.as_slice())?; + + let Some(signed_data) = asd.signed_data else { + return Err(UserDiscoveryError::MaliciousAnnouncementData( + "missing signed data".into(), + )); + }; + + if contact_id != signed_data.user_id { + return Err(UserDiscoveryError::MaliciousAnnouncementData(format!( + "contact_id ({contact_id}) != signed_data.user_id ({})", + signed_data.user_id + ))); + } + + if !self.utils.verify_pubkey_and_signature_from( + contact_id, + signed_data.encode_to_vec(), + signed_data.public_key, + asd.signature, + )? { + return Err(UserDiscoveryError::MaliciousAnnouncementData(format!( + "signature invalid or public key does not match with stored one", + ))); + } + + let mut config = self.get_config()?; + config.promotion_version += 1; + self.store.update_config(to_allocvec(&config)?)?; + + let message = UserDiscoveryMessage { + version: Some(UserDiscoveryVersion { + announcement: config.announcement_version, + promotion: config.promotion_version, + }), + user_discovery_promotion: Some(UserDiscoveryPromotion { + promotion_id: rand::random(), + public_id: signed_data.public_id, + threshold: uda.threshold, + announcement_share: uda.announcement_share, + verification_state: None, + }), + ..Default::default() + }; + + self.store + .push_promotion(config.promotion_version, message.encode_to_vec())?; + + Ok(()) + } + Err(err) => { + return Err(UserDiscoveryError::ShamirsSecret(err.to_string())); + } + } + } + + fn handle_user_discovery_promotion( + &self, + contact_id: UserID, + uda: UserDiscoveryPromotion, + ) { + + // contact_id + // uda.promotion_id + // uda.public_id + // uda.threshold + // uda.announcement_share + // uda.verification_state + + // store this into the received_promotion_table + // check if the threshold is reached + // in case thr threshold is reached -> CALL STORE -> NEW USER + // otherwise do nothing + + } + +} + +#[cfg(test)] +mod tests { + + use crate::user_discovery::stores::InMemoryStore; + use crate::user_discovery::traits::tests::TestingUtils; + use crate::user_discovery::{UserDiscovery, UserDiscoveryVersion, UserID}; + use prost::Message; + + fn get_version_bytes(announcement: u32, promotion: u32) -> Vec { + UserDiscoveryVersion { + announcement, + promotion, + } + .encode_to_vec() + } + + fn zero() -> Vec { + get_version_bytes(0, 0) + } + + fn get_ud(user_id: UserID, friends: &[UserID]) -> UserDiscovery { + let store = InMemoryStore::default(); + store.set_friends(friends.iter().copied().collect()); + let ud = UserDiscovery::new(store.to_owned(), TestingUtils::default()).unwrap(); + + ud.initialize_or_update(3, user_id, vec![user_id as u8; 32]) + .unwrap(); + + let version = ud.get_current_version().unwrap(); + + assert_eq!(version, get_version_bytes(1, 0)); + ud + } + + fn init() { + tracing_subscriber::fmt() + .with_max_level(tracing::Level::DEBUG) + .init(); + } + + fn request_and_handle_messages( + from: (UserID, &UserDiscovery), + to: (UserID, &UserDiscovery), + messages_count: usize, + ) { + // From sends a message with his current version to To + let to_received_version = &from.1.get_current_version().unwrap(); + assert_eq!( + to.1.should_request_new_messages(from.0, to_received_version) + .unwrap(), + true + ); + + // As To has a older version stored he sends a request to From: Give me all messages since version. + let from_request_version_from_to = + to.1.get_contact_version(from.0).unwrap().unwrap_or(zero()); + + let new_messages = from + .1 + .get_new_messages(to.0, &from_request_version_from_to) + .unwrap(); + + assert_eq!(new_messages.len(), messages_count); + + to.1.handle_user_discovery_messages(from.0, new_messages) + .unwrap(); + + assert_eq!( + to.1.should_request_new_messages(from.0, &from.1.get_current_version().unwrap()) + .unwrap(), + false + ); + } + + #[tokio::test] + async fn test_initialize_user_discovery() { + init(); + + const ALICE: UserID = 0; + const BOB: UserID = 1; + const CHARLIE: UserID = 2; + const DAVID: UserID = 3; + const FRANK: UserID = 4; + + let alice_ud = get_ud(ALICE, &[BOB, CHARLIE]); + let bob_ud = get_ud(BOB, &[ALICE, CHARLIE, DAVID]); + let charlie_ud = get_ud(CHARLIE, &[ALICE, BOB, DAVID, FRANK]); + let david_ud = get_ud(DAVID, &[BOB, CHARLIE]); + let frank_ud = get_ud(FRANK, &[CHARLIE]); + + { + // Alice send UDA to Bob and Charlie + request_and_handle_messages((ALICE, &alice_ud), (BOB, &bob_ud), 1); + request_and_handle_messages((ALICE, &alice_ud), (CHARLIE, &charlie_ud), 1); + } + + { + // This now contains Bob's own announcement in addition this now also contains the promotion from Alice + request_and_handle_messages((BOB, &bob_ud), (DAVID, &david_ud), 2); + request_and_handle_messages((BOB, &bob_ud), (CHARLIE, &charlie_ud), 2); + } + + todo!(); + } +} diff --git a/rust/src/user_discovery/stores/in_memory_store.rs b/rust/src/user_discovery/stores/in_memory_store.rs new file mode 100644 index 0000000..2b7ec34 --- /dev/null +++ b/rust/src/user_discovery/stores/in_memory_store.rs @@ -0,0 +1,102 @@ +use crate::user_discovery::error::UserDiscoveryError; +use crate::user_discovery::traits::UserDiscoveryStore; +use crate::user_discovery::UserID; +use std::collections::{HashMap, HashSet}; +use std::sync::{Arc, Mutex}; + +#[derive(Default)] +pub(crate) struct Storage { + config: Option>, + unused_shares: Vec>, + used_shares: HashMap>, + contact_versions: HashMap>, + friends: HashSet, + promotions: Vec>, +} + +#[derive(Default, Clone)] +pub struct InMemoryStore { + pub(crate) storage: Arc>, +} + +impl InMemoryStore { + fn storage(&self) -> std::sync::MutexGuard<'_, Storage> { + self.storage.lock().unwrap() + } + pub fn set_friends(&self, friends: HashSet) { + self.storage().friends = friends; + } +} + +impl UserDiscoveryStore for InMemoryStore { + fn get_config(&self) -> crate::user_discovery::error::Result> { + if let Some(storage) = self.storage().config.clone() { + return Ok(storage); + } + Err(UserDiscoveryError::NotInitialized) + } + + fn update_config(&self, update: Vec) -> crate::user_discovery::error::Result<()> { + self.storage().config = Some(update); + Ok(()) + } + + fn set_shares(&self, shares: Vec>) -> crate::user_discovery::error::Result<()> { + self.storage().unused_shares = shares; + Ok(()) + } + + fn get_share_for_contact( + &self, + contact_id: UserID, + ) -> crate::user_discovery::error::Result> { + let mut storage = self.storage(); + if let Some(share) = storage.used_shares.get(&contact_id) { + return Ok(share.to_vec()); + } + if let Some(new_share) = storage.unused_shares.pop() { + storage.used_shares.insert(contact_id, new_share.clone()); + return Ok(new_share); + } + Err(UserDiscoveryError::NoSharesLeft) + } + + fn get_contact_version( + &self, + contact_id: UserID, + ) -> crate::user_discovery::error::Result>> { + Ok(self.storage().contact_versions.get(&contact_id).cloned()) + } + + fn set_contact_version( + &self, + contact_id: UserID, + update: Vec, + ) -> crate::user_discovery::error::Result<()> { + self.storage().contact_versions.insert(contact_id, update); + Ok(()) + } + + fn push_promotion( + &self, + version: u32, + promotion: Vec, + ) -> crate::user_discovery::error::Result<()> { + let mut storage = self.storage(); + // println!("{} != {}", version, storage.promotions.len()); + if version as usize != storage.promotions.len() + 1 { + return Err(UserDiscoveryError::PushedInvalidVersion); + } + storage.promotions.push(promotion); + Ok(()) + } + + fn get_promotions_after_version( + &self, + version: u32, + ) -> crate::user_discovery::error::Result>> { + let storage = self.storage(); + let elements = storage.promotions[(version as usize)..].to_vec(); + Ok(elements) + } +} diff --git a/rust/src/user_discovery/stores/mod.rs b/rust/src/user_discovery/stores/mod.rs new file mode 100644 index 0000000..c22f0a9 --- /dev/null +++ b/rust/src/user_discovery/stores/mod.rs @@ -0,0 +1,2 @@ +mod in_memory_store; +pub use in_memory_store::InMemoryStore; diff --git a/rust/src/user_discovery/traits.rs b/rust/src/user_discovery/traits.rs new file mode 100644 index 0000000..67dca8c --- /dev/null +++ b/rust/src/user_discovery/traits.rs @@ -0,0 +1,50 @@ +use crate::user_discovery::error::Result; +use crate::user_discovery::UserID; + +pub trait UserDiscoveryStore { + fn get_config(&self) -> Result>; + fn update_config(&self, update: Vec) -> Result<()>; + fn set_shares(&self, shares: Vec>) -> Result<()>; + + fn get_share_for_contact(&self, contact_id: UserID) -> Result>; + + fn push_promotion(&self, version: u32, promotion: Vec) -> Result<()>; + fn get_promotions_after_version(&self, version: u32) -> Result>>; + + fn get_contact_version(&self, contact_id: UserID) -> Result>>; + fn set_contact_version(&self, contact_id: UserID, update: Vec) -> Result<()>; +} + +pub trait UserDiscoveryUtils { + fn sign_data(&self, input_data: Vec) -> Result>; + fn verify_pubkey_and_signature_from( + &self, + from_contact_id: UserID, + data: Vec, + pubkey: Vec, + signature: Vec, + ) -> Result; +} + +#[cfg(test)] +pub(crate) mod tests { + use crate::user_discovery::traits::UserDiscoveryUtils; + + #[derive(Default)] + pub(crate) struct TestingUtils {} + impl UserDiscoveryUtils for TestingUtils { + fn sign_data(&self, _input_data: Vec) -> crate::user_discovery::error::Result> { + Ok(vec![0; 64]) + } + + fn verify_pubkey_and_signature_from( + &self, + from_contact_id: crate::user_discovery::UserID, + data: Vec, + pubkey: Vec, + signature: Vec, + ) -> crate::user_discovery::error::Result { + Ok(true) + } + } +} diff --git a/rust/src/user_discovery/types.proto b/rust/src/user_discovery/types.proto new file mode 100644 index 0000000..f719ece --- /dev/null +++ b/rust/src/user_discovery/types.proto @@ -0,0 +1,52 @@ +syntax = "proto3"; + +message UserDiscoveryVersion { + uint32 announcement = 1; + uint32 promotion = 2; +} + +message UserDiscoveryMessage { + + UserDiscoveryVersion version = 1; + + optional UserDiscoveryAnnouncement user_discovery_announcement = 2; + optional UserDiscoveryPromotion user_discovery_promotion = 3; + optional UserDiscoveryRecall user_discovery_recall = 4; + + message UserDiscoveryAnnouncement { + uint64 public_id = 1; + uint32 threshold = 2; + bytes announcement_share = 4; + repeated bytes verification_shares = 5; + } + + message UserDiscoveryPromotion { + uint32 promotion_id = 1; + uint64 public_id = 2; + uint32 threshold = 3; + bytes announcement_share = 5; + + optional VerificationState verification_state = 6; + + message AnnouncementShareDecrypted { + message SignedData { + uint64 public_id = 1; + int64 user_id = 2; + bytes public_key = 3; + } + SignedData signed_data = 1; + bytes signature = 2; + } + + message VerificationState { + int64 timestamp = 1; + bytes signature = 2; + } + + } + + message UserDiscoveryRecall { + int64 promotion_id = 1; + } + +} diff --git a/rust/tests/testing.db b/rust/tests/testing.db new file mode 100644 index 0000000000000000000000000000000000000000..df0d8a5baf3e0f4458a74ca7baa6b4a04e138e4c GIT binary patch literal 131072 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 BjdB10 literal 0 HcmV?d00001 diff --git a/test/rust/create_db_for_rust_testing.dart b/test/rust/create_db_for_rust_testing.dart new file mode 100644 index 0000000..a9c308f --- /dev/null +++ b/test/rust/create_db_for_rust_testing.dart @@ -0,0 +1,56 @@ +import 'dart:io'; +import 'package:drift/drift.dart' show Value; +import 'package:drift/native.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:twonly/src/database/twonly.db.dart'; + +void main() { + if (!Platform.isMacOS) return; + late TwonlyDB database; + late File dbFile; + + setUp(() { + dbFile = File('rust/tests/testing.db'); + if (dbFile.existsSync()) { + dbFile.deleteSync(); + } + database = TwonlyDB(NativeDatabase(dbFile)); + }); + + tearDown(() async { + await database.close(); + }); + + test('Database successfully writes to the physical file system', () async { + final users = [ + 'alice', + 'bob', + 'charlie', + 'david', + 'frank', + ]; + + for (var i = 0; i < users.length; i++) { + await database.contactsDao.insertContact( + ContactsCompanion(userId: Value(i), username: Value(users[i])), + ); + } + + expect( + dbFile.existsSync(), + isTrue, + reason: 'The SQLite file was not created on disk.', + ); + + expect( + dbFile.lengthSync(), + greaterThan(0), + reason: 'The SQLite file is completely empty.', + ); + + if (kDebugMode) { + print('Test passed! Database written to: ${dbFile.absolute.path}'); + } + }); +}