started with ud

This commit is contained in:
otsmr 2026-04-13 02:28:41 +02:00
parent 8aacdc5235
commit 87ba1c23e5
16 changed files with 1394 additions and 21 deletions

2
rust/.gitignore vendored
View file

@ -1 +1,3 @@
/target
tests/tmp_testing.db*

560
rust/Cargo.lock generated
View file

@ -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"

View file

@ -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"

5
rust/build.rs Normal file
View file

@ -0,0 +1,5 @@
use std::io::Result;
fn main() -> Result<()> {
prost_build::compile_protos(&["src/user_discovery/types.proto"], &["src/"])?;
Ok(())
}

View file

@ -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<Utc>,
// pub(crate) created_at: DateTime<Utc>,
}
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);
}
}
}

View file

@ -11,6 +11,7 @@ pub struct TwonlyConfig {
}
struct Twonly {
#[allow(dead_code)]
pub(crate) config: TwonlyConfig,
database: Arc<Database>,
}
@ -39,3 +40,38 @@ pub async fn get_all_contacts() -> Result<Vec<Contact>> {
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()
}
}

View file

@ -0,0 +1,10 @@
// **Store** promotions:
// version_id, contact_id, Option<UserDiscoveryMessage>
// -> 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
//

View file

@ -0,0 +1,28 @@
use prost::DecodeError;
use thiserror::Error;
pub type Result<T> = core::result::Result<T, UserDiscoveryError>;
#[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),
}

View file

@ -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<Vec<u8>>,
}
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<Store, Utils>
where
Store: UserDiscoveryStore,
Utils: UserDiscoveryUtils,
{
store: Store,
utils: Utils,
}
impl<Store: UserDiscoveryStore, Utils: UserDiscoveryUtils> UserDiscovery<Store, Utils> {
pub fn new(store: Store, utils: Utils) -> Result<Self> {
Ok(Self { store, utils })
}
pub fn initialize_or_update(
&self,
threshold: u8,
user_id: UserID,
public_key: Vec<u8>,
) -> 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<u8>,
) -> Result<Vec<Vec<u8>>> {
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<Vec<u8>> = 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<UserDiscoveryConfig> {
Ok(from_bytes(&self.store.get_config()?)?)
}
/// Returns the current version of the user discovery.
pub fn get_current_version(&self) -> Result<Vec<u8>> {
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<Vec<Vec<u8>>> {
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<bool> {
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<Option<Vec<u8>>> {
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<Vec<u8>>,
) -> 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<u8> {
UserDiscoveryVersion {
announcement,
promotion,
}
.encode_to_vec()
}
fn zero() -> Vec<u8> {
get_version_bytes(0, 0)
}
fn get_ud(user_id: UserID, friends: &[UserID]) -> UserDiscovery<InMemoryStore, TestingUtils> {
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<InMemoryStore, TestingUtils>),
to: (UserID, &UserDiscovery<InMemoryStore, TestingUtils>),
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!();
}
}

View file

@ -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<Vec<u8>>,
unused_shares: Vec<Vec<u8>>,
used_shares: HashMap<UserID, Vec<u8>>,
contact_versions: HashMap<UserID, Vec<u8>>,
friends: HashSet<UserID>,
promotions: Vec<Vec<u8>>,
}
#[derive(Default, Clone)]
pub struct InMemoryStore {
pub(crate) storage: Arc<Mutex<Storage>>,
}
impl InMemoryStore {
fn storage(&self) -> std::sync::MutexGuard<'_, Storage> {
self.storage.lock().unwrap()
}
pub fn set_friends(&self, friends: HashSet<UserID>) {
self.storage().friends = friends;
}
}
impl UserDiscoveryStore for InMemoryStore {
fn get_config(&self) -> crate::user_discovery::error::Result<Vec<u8>> {
if let Some(storage) = self.storage().config.clone() {
return Ok(storage);
}
Err(UserDiscoveryError::NotInitialized)
}
fn update_config(&self, update: Vec<u8>) -> crate::user_discovery::error::Result<()> {
self.storage().config = Some(update);
Ok(())
}
fn set_shares(&self, shares: Vec<Vec<u8>>) -> 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<Vec<u8>> {
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<Option<Vec<u8>>> {
Ok(self.storage().contact_versions.get(&contact_id).cloned())
}
fn set_contact_version(
&self,
contact_id: UserID,
update: Vec<u8>,
) -> crate::user_discovery::error::Result<()> {
self.storage().contact_versions.insert(contact_id, update);
Ok(())
}
fn push_promotion(
&self,
version: u32,
promotion: Vec<u8>,
) -> 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<Vec<Vec<u8>>> {
let storage = self.storage();
let elements = storage.promotions[(version as usize)..].to_vec();
Ok(elements)
}
}

View file

@ -0,0 +1,2 @@
mod in_memory_store;
pub use in_memory_store::InMemoryStore;

View file

@ -0,0 +1,50 @@
use crate::user_discovery::error::Result;
use crate::user_discovery::UserID;
pub trait UserDiscoveryStore {
fn get_config(&self) -> Result<Vec<u8>>;
fn update_config(&self, update: Vec<u8>) -> Result<()>;
fn set_shares(&self, shares: Vec<Vec<u8>>) -> Result<()>;
fn get_share_for_contact(&self, contact_id: UserID) -> Result<Vec<u8>>;
fn push_promotion(&self, version: u32, promotion: Vec<u8>) -> Result<()>;
fn get_promotions_after_version(&self, version: u32) -> Result<Vec<Vec<u8>>>;
fn get_contact_version(&self, contact_id: UserID) -> Result<Option<Vec<u8>>>;
fn set_contact_version(&self, contact_id: UserID, update: Vec<u8>) -> Result<()>;
}
pub trait UserDiscoveryUtils {
fn sign_data(&self, input_data: Vec<u8>) -> Result<Vec<u8>>;
fn verify_pubkey_and_signature_from(
&self,
from_contact_id: UserID,
data: Vec<u8>,
pubkey: Vec<u8>,
signature: Vec<u8>,
) -> Result<bool>;
}
#[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<u8>) -> crate::user_discovery::error::Result<Vec<u8>> {
Ok(vec![0; 64])
}
fn verify_pubkey_and_signature_from(
&self,
from_contact_id: crate::user_discovery::UserID,
data: Vec<u8>,
pubkey: Vec<u8>,
signature: Vec<u8>,
) -> crate::user_discovery::error::Result<bool> {
Ok(true)
}
}
}

View file

@ -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;
}
}

BIN
rust/tests/testing.db Normal file

Binary file not shown.

View file

@ -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}');
}
});
}