diff --git a/Cargo.lock b/Cargo.lock index e57bc0a..a5982b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,16 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + [[package]] name = "ahash" version = "0.8.12" @@ -16,9 +26,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.19" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" dependencies = [ "anstyle", "anstyle-parse", @@ -46,22 +56,22 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.9" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys", + "windows-sys 0.60.2", ] [[package]] @@ -104,10 +114,22 @@ dependencies = [ ] [[package]] -name = "async-executor" -version = "1.13.2" +name = "async-channel" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" dependencies = [ "async-task", "concurrent-queue", @@ -117,6 +139,93 @@ dependencies = [ "slab", ] +[[package]] +name = "async-fs" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f7e37c0ed80b2a977691c47dae8625cfb21e205827106c64f7c588766b2e50" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19634d6336019ef220f09fd31168ce5c184b295cbf80345437cc36094ef223ca" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "windows-sys 0.60.2", +] + +[[package]] +name = "async-lock" +version = "3.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-net" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" +dependencies = [ + "async-io", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-process" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65daa13722ad51e6ab1a1b9c01299142bc75135b337923cfa10e79bbbd669f00" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", +] + +[[package]] +name = "async-signal" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f567af260ef69e1d52c2b560ce0ea230763e6fbb9214a85d768760a920e3e3c1" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.60.2", +] + [[package]] name = "async-task" version = "4.7.1" @@ -126,6 +235,17 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -141,6 +261,37 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "bevy_app" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4491cc4c718ae76b4c6883df58b94cc88b32dcd894ea8d5b603c7c7da72ca967" +dependencies = [ + "bevy_derive", + "bevy_ecs", + "bevy_platform", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", + "cfg-if", + "ctrlc", + "downcast-rs", + "log", + "thiserror", + "variadics_please", +] + +[[package]] +name = "bevy_derive" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b837bf6c51806b10ebfa9edf1844ad80a3a0760d6c5fac4e90761df91a8901a" +dependencies = [ + "bevy_macro_utils", + "quote", + "syn", +] + [[package]] name = "bevy_ecs" version = "0.16.1" @@ -203,7 +354,7 @@ dependencies = [ "cfg-if", "critical-section", "foldhash", - "hashbrown 0.15.1", + "hashbrown 0.15.5", "portable-atomic", "portable-atomic-util", "serde", @@ -282,11 +433,21 @@ dependencies = [ "thread_local", ] +[[package]] +name = "bevy_vanth" +version = "0.1.0" +dependencies = [ + "bevy_app", + "bevy_ecs", + "serde", + "vanth", +] + [[package]] name = "bitflags" -version = "2.9.1" +version = "2.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" dependencies = [ "serde", ] @@ -314,6 +475,19 @@ dependencies = [ "generic-array", ] +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + [[package]] name = "bstr" version = "1.12.0" @@ -339,24 +513,65 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.2.30" +version = "1.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7" +checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc" dependencies = [ "shlex", ] [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] [[package]] name = "clap" -version = "4.5.42" +version = "4.5.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" +checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57" dependencies = [ "clap_builder", "clap_derive", @@ -364,9 +579,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.42" +version = "4.5.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" +checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41" dependencies = [ "anstream", "anstyle", @@ -376,9 +591,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.41" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" +checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6" dependencies = [ "heck", "proc-macro2", @@ -414,6 +629,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + [[package]] name = "critical-section" version = "1.2.0" @@ -431,9 +655,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crypto-common" @@ -442,9 +666,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core 0.6.4", "typenum", ] +[[package]] +name = "ctrlc" +version = "3.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46f93780a459b7d656ef7f071fe699c4d3d2cb201c4b24d085b6ddc505276e73" +dependencies = [ + "nix", + "windows-sys 0.59.0", +] + [[package]] name = "derive_more" version = "1.0.0" @@ -503,9 +738,9 @@ checksum = "ea8a8b81cacc08888170eef4d13b775126db426d0b348bee9d18c2c1eaf123cf" [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "erased-serde" @@ -524,7 +759,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.60.2", +] + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", ] [[package]] @@ -553,9 +809,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "foldhash" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "futures-core" @@ -571,9 +827,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ "fastrand", "futures-core", @@ -592,6 +848,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", +] + [[package]] name = "getrandom" version = "0.3.3" @@ -601,7 +868,7 @@ dependencies = [ "cfg-if", "libc", "r-efi", - "wasi", + "wasi 0.14.3+wasi-0.2.4", ] [[package]] @@ -633,9 +900,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.1" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "equivalent", "serde", @@ -668,13 +935,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] -name = "indexmap" -version = "2.10.0" +name = "hermit-abi" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "indexmap" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" dependencies = [ "equivalent", - "hashbrown 0.15.1", + "hashbrown 0.15.5", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", ] [[package]] @@ -707,9 +989,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.174" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "libsqlite3-sys" @@ -750,6 +1032,18 @@ version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nonmax" version = "0.5.5" @@ -757,13 +1051,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "610a5acd306ec67f907abe5567859a3c693fb9886eb1f012ab8f2a47bef3db51" [[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +name = "ntrulp" +version = "0.2.3" dependencies = [ - "overload", - "winapi", + "rand", + "rand_chacha", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +dependencies = [ + "windows-sys 0.52.0", ] [[package]] @@ -779,10 +1080,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] -name = "overload" -version = "0.1.1" +name = "opaque-debug" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "parking" @@ -810,7 +1111,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -819,12 +1120,48 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "pkg-config" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "polling" +version = "3.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5bd19146350fe804f7cb2669c851c03d69da628803dab0d98018142aaa5d829" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "windows-sys 0.60.2", +] + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "portable-atomic" version = "1.11.1" @@ -877,10 +1214,19 @@ dependencies = [ ] [[package]] -name = "proc-macro2" -version = "1.0.95" +name = "proc-macro-crate" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -901,13 +1247,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] -name = "rand_chacha" -version = "0.3.1" +name = "rand" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.9.3", "serde", ] @@ -916,21 +1272,33 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] [[package]] name = "redox_syscall" -version = "0.5.13" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ "bitflags", ] [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" [[package]] name = "rusqlite" @@ -956,14 +1324,14 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.60.2", ] [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" @@ -986,6 +1354,24 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_arrays" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94a16b99c5ea4fe3daccd14853ad260ec00ea043b2708d1fd1da3106dcd8d9df" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_bytes" +version = "0.11.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8437fd221bde2d4ca316d61b90e337e9e702b3820b87d63caa9ba6c02bd06d96" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.219" @@ -999,9 +1385,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.141" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" dependencies = [ "itoa", "memchr", @@ -1025,10 +1411,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] -name = "slab" -version = "0.4.10" +name = "signal-hook-registry" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" @@ -1036,6 +1431,23 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "smol" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33bd3e260892199c3ccfc487c88b2da2265080acb316cd920da72fdfd7c599f" +dependencies = [ + "async-channel", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-net", + "async-process", + "blocking", + "futures-lite", +] + [[package]] name = "smol_str" version = "0.2.2" @@ -1074,9 +1486,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.104" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -1085,15 +1497,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.20.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" dependencies = [ "fastrand", - "getrandom", + "getrandom 0.3.3", "once_cell", "rustix", - "windows-sys", + "windows-sys 0.60.2", ] [[package]] @@ -1104,18 +1516,18 @@ checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" dependencies = [ "proc-macro2", "quote", @@ -1193,9 +1605,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "nu-ansi-term", "sharded-slab", @@ -1229,6 +1641,16 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "utf8parse" version = "0.2.2" @@ -1237,11 +1659,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" dependencies = [ - "getrandom", + "getrandom 0.3.3", "js-sys", "serde", "wasm-bindgen", @@ -1257,12 +1679,16 @@ checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" name = "vanth" version = "0.1.0" dependencies = [ + "async-process", + "async-trait", "bevy_ecs", "blake3", "digest", + "rand", "rusqlite", "serde", "serde_json", + "smol", "tempfile", "tracing", "vanth_derive", @@ -1286,6 +1712,7 @@ dependencies = [ name = "vanth_derive" version = "0.1.0" dependencies = [ + "proc-macro-crate", "proc-macro2", "quote", "syn", @@ -1295,6 +1722,27 @@ dependencies = [ name = "vanth_meta" version = "0.1.0" +[[package]] +name = "vanth_transport" +version = "0.1.0" +dependencies = [ + "async-process", + "async-trait", + "blake3", + "chacha20poly1305", + "digest", + "ntrulp", + "rand", + "serde", + "serde_arrays", + "serde_bytes", + "serde_json", + "smol", + "tracing", + "vanth", + "vanth_derive", +] + [[package]] name = "variadics_please" version = "1.1.0" @@ -1313,7 +1761,7 @@ dependencies = [ "blake3", "digest", "rand_chacha", - "rand_core", + "rand_core 0.9.3", "serde", "serde_json", "tempfile", @@ -1344,11 +1792,17 @@ dependencies = [ [[package]] name = "wasi" -version = "0.14.2+wasi-0.2.4" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasi" +version = "0.14.3+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] @@ -1433,34 +1887,36 @@ dependencies = [ ] [[package]] -name = "winapi" -version = "0.3.9" +name = "windows-link" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "windows-targets 0.52.6", ] -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", ] [[package]] @@ -1469,14 +1925,31 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", ] [[package]] @@ -1485,42 +1958,84 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -1528,22 +2043,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "winnow" -version = "0.7.12" +name = "windows_x86_64_msvc" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winnow" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "wit-bindgen" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags", -] +checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" [[package]] name = "zerocopy" @@ -1564,3 +2082,9 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index 2506e61..93ffa6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ name = "vanth" [workspace.dependencies] clap = { version = "4.5.42", features = ["derive"] } bevy_ecs = "0.16.1" +bevy_app = "0.16.1" bincode = "2.0.1" serde = { version = "1.0.219", features = ["derive"] } serde_json = "1.0.140" @@ -22,8 +23,16 @@ syn = { version = "2.0", features = ["full"] } proc-macro2 = "1.0" rusqlite = { version = "0.32.1", features = ["bundled"] } tempfile = "3.12.0" -rand_core = "0.6.4" -rand_chacha = { version = "0.3.1", features = ["serde1"] } +rand = "0.9.2" +rand_chacha = { version = "0.9.0", features = ["serde"] } +rand_core = "0.9.0" tracing = "0.1.41" tracing-subscriber = "0.3.19" assert_cmd = "2.0.17" +proc-macro-crate = "3.3.0" +smol = "2.0.2" +async-process = "2.4.0" +async-trait = "0.1.89" +chacha20poly1305 = "0.10.1" +serde_bytes = "0.11.17" +serde_arrays = "0.2.0" diff --git a/README.md b/README.md index 24e878c..08e99d6 100644 --- a/README.md +++ b/README.md @@ -1,89 +1,9 @@ # Vanth -Vanth is a content-addressed database as a library designed for entity-component-system (ECS) applications. +Vanth is a content-addressed data storage and task memoization framework designed for entity-component-system (ECS) applications. + +Inspired by [Salsa](https://github.com/salsa-rs/salsa) and [Bevy](https://bevy.org/). It is currently experimental and should not be used for anything. -## Library usage - -Any type that implements `serde::Serialize` can be hashed using `vanth::hash` to produce a `vanth::ContentHash`. - -```rust -let x = "hello"; -assert_eq!(vanth::hash(&x).hex(), "ea8f163db38682925e4491c5e58d4bb3506ef8c14eb78a86e908c5624a67200f"); -``` - -Derive or implement the `vanth::Vanth` trait for types you want to store in the database. - -```rust -use serde::{Deserialize, Serialize}; -use vanth::Vanth; - -#[derive(Deserialize, Serialize, Vanth)] -struct Data { - value: u32, -} -``` - -This generates a method `Vanth::ty()` which returns a `vanth::Ty`. This should represent the type's fully qualified name - its module path followed by the type itself and any generics it has. E.g. `Data::ty().to_string()` could return `"my::crate::module::Data"`. - -The derive macro only works for basic types right now and is not implemented for `std` types. Moving or renaming types or modules will change the type name, necessitating a database migration. This is not supported yet. - -This should be used with caution. There are good reasons why `std::any::TypeId` is opaque. - -### Database storage - -```rust -use vanth::store::{Store, StoreParams}; - -// Or use `Store::in_memory`. -let mut store = Store::sqlite_from_path( - "path/to/my_database.sqlite".into(), - StoreParams { create_if_not_exists: true, ..Default::default() }, -); -let hash = store.write(Data { value: 5 }).unwrap(); -let my_data: Data = store.get_from_hash(hash).unwrap(); -``` - -## CLI usage - -You can run the Vanth CLI with Nix using `nix run git+https://git.mascully.com/mascully/vanth.git`. - -Use `--` to pass arguments, e.g. `nix run git+https://git.mascully.com/mascully/vanth.git -- --help`. - -### Syntax - -Write a component to the database: - -```bash -$ vanth write --db /path/to/db.sqlite --ty my::type::Name --value '{ "field": "value" }' -a1cf81d8afe4e72604ea5c13bbd9b6cce14bd98c3a2f036f7156c4464a88ec09 -``` - -Get a component from the database: - -```bash -$ vanth get --db /path/to/db.sqlite --ty my::type::Name a1cf81d8afe4e72604ea5c13bbd9b6cce14bd98c3a2f036f7156c4464a88ec09 -{"field":"value"} -``` - -Get all components of a type from the database: - -```bash -$ vanth get-all --db /path/to/db.sqlite --ty my::type::Name -{"field":"value1"} -{"field":"value2"} -{"field":"value3"} -``` - -Delete a component by its content hash: - -```bash -$ vanth delete --db /path/to/db.sqlite ea8f163db38682925e4491c5e58d4bb3506ef8c14eb78a86e908c5624a67200f -``` - -Delete all components of a type: - -```bash -$ vanth delete-all --db /path/to/db.sqlite --ty my::type::Name -``` +See the [crate-level documentation](crates/vanth/README.md) for more details. diff --git a/crates/bevy_vanth/Cargo.toml b/crates/bevy_vanth/Cargo.toml new file mode 100644 index 0000000..6f73459 --- /dev/null +++ b/crates/bevy_vanth/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "bevy_vanth" +version.workspace = true +edition.workspace = true + +[dependencies] +bevy_ecs.workspace = true +bevy_app.workspace = true +serde.workspace = true +vanth = { path = "../vanth" } diff --git a/crates/bevy_vanth/src/lib.rs b/crates/bevy_vanth/src/lib.rs new file mode 100644 index 0000000..4549852 --- /dev/null +++ b/crates/bevy_vanth/src/lib.rs @@ -0,0 +1,56 @@ +use bevy_ecs::{component::Component, system::Query, world::World as BevyWorld}; +use serde::{Deserialize, Serialize}; +use vanth::{ActiveTask, Module, Root, Task, Vanth, entity::TaskId}; + +// TODO: Should there be one plugin per root, or one global plugin with many root components? +// #[derive(Component)] +// pub struct VanthRoot {} + +pub struct BevyVanthPlugin { + root: Root, +} + +impl BevyVanthPlugin { + pub fn from_root(root: Root) -> Self { + Self { root } + } +} + +// impl bevy_app::Plugin for BevyVanthPlugin { +// fn build(&self, app: &mut bevy_app::App) { +// app.add_systems(bevy_app::Update, process_active_tasks); +// } +// } + +pub struct BevyPluginModule(M); + +impl From for BevyPluginModule +where + M: Module + Send + Sync, +{ + fn from(value: M) -> Self { + Self(value) + } +} + +impl bevy_app::Plugin for BevyPluginModule +where + M: Module + Send + Sync + 'static, +{ + fn build(&self, app: &mut bevy_app::App) { + // app.add_systems(bevy_app::FixedPreUpdate, run_vanth).finish(); + } +} + +fn process_active_tasks(tasks: Query<()>) {} + +pub fn run_task>(world: &mut BevyWorld, task: T) -> TaskId { + todo!() +} + +pub fn creation_bevy_plugin(module: M) -> impl bevy_app::Plugin +where + M: Module + Send + Sync + 'static, +{ + BevyPluginModule(module) +} diff --git a/crates/bevy_vanth/tests/test.rs b/crates/bevy_vanth/tests/test.rs new file mode 100644 index 0000000..5958683 --- /dev/null +++ b/crates/bevy_vanth/tests/test.rs @@ -0,0 +1,42 @@ +use bevy_app::App; +use bevy_vanth::BevyPluginModule; +use serde::{Deserialize, Serialize}; +use vanth::{Module, Vanth}; + +mod bevy_tests { + use bevy_app::Plugin; + use vanth::Root; + + use super::*; + + #[derive(Deserialize, Serialize, Vanth)] + struct TestData { + number: u32, + } + + #[derive(Deserialize, Serialize, Vanth)] + struct TestTask { + a: TestData, + b: TestData, + } + + #[derive(Deserialize, Serialize, Vanth)] + struct TestModule {} + + impl Module for TestModule { + + } + + #[test] + fn test_bevy() { + let root = Root::local(); + let module = TestModule {}; + let module_bevy_plugin = bevy_vanth::creation_bevy_plugin(module); + let bevy_vanth_plugin = bevy_vanth::BevyVanthPlugin::from_root(root); + + let mut app = App::new(); + + app.add_plugins(module_bevy_plugin); + // app.add_plugins(bevy_vanth_plugin); + } +} diff --git a/crates/cli/tests/nu/mod.rs b/crates/cli/tests/nu/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/crates/cli/tests/integration/main.rs b/crates/cli/tests/test.rs similarity index 99% rename from crates/cli/tests/integration/main.rs rename to crates/cli/tests/test.rs index bdeb243..d7e40c6 100644 --- a/crates/cli/tests/integration/main.rs +++ b/crates/cli/tests/test.rs @@ -7,6 +7,8 @@ use serde_json::{Value, json}; use tempfile::tempdir; use vanth::{ContentHash, Vanth, hash as vanth_hash}; +pub mod nu; + fn run_vanth(args: &[&str], input: Option<&str>) -> (String, String, i32) { let mut cmd = Command::cargo_bin("vanth").unwrap(); let output = cmd.args(args).write_stdin(input.unwrap_or("")).output().unwrap(); diff --git a/crates/vanth/Cargo.toml b/crates/vanth/Cargo.toml index 59003fb..3629b46 100644 --- a/crates/vanth/Cargo.toml +++ b/crates/vanth/Cargo.toml @@ -16,6 +16,10 @@ blake3.workspace = true vanth_derive = { path = "../vanth_derive" } rusqlite.workspace = true tracing.workspace = true +rand = { workspace = true } +smol.workspace = true +async-process.workspace = true +async-trait.workspace = true [dev-dependencies] tempfile = { workspace = true } diff --git a/crates/vanth/README.md b/crates/vanth/README.md new file mode 100644 index 0000000..7ff6f75 --- /dev/null +++ b/crates/vanth/README.md @@ -0,0 +1,85 @@ +# Vanth + +## Library usage + +Any type that implements `serde::Serialize` can be hashed using `vanth::hash` to produce a `vanth::ContentHash`. + +```rust +let x = "hello"; +assert_eq!(vanth::hash(&x).hex(), "ea8f163db38682925e4491c5e58d4bb3506ef8c14eb78a86e908c5624a67200f"); +``` + +Derive or implement the `vanth::Vanth` trait for types you want to store in the database. + +```rust +use serde::{Deserialize, Serialize}; +use vanth::Vanth; + +#[derive(Deserialize, Serialize, Vanth)] +struct Data { + value: u32, +} +``` + +This generates a method `Vanth::ty()` which returns a `vanth::Ty`. This should represent the type's fully qualified name - its module path followed by the type itself and any generics it has. E.g. `Data::ty().to_string()` could return `"my::crate::module::Data"`. + +The derive macro only works for basic types right now and is not implemented for `std` types. Moving or renaming types or modules will change the type name, necessitating a database migration. This is not supported yet. + +This should be used with caution. There are good reasons why `std::any::TypeId` is opaque. + +### Database storage + +```rust +use vanth::store::{Store, StoreParams}; + +// Or use `Store::in_memory`. +let mut store = Store::sqlite_from_path( + "path/to/my_database.sqlite", + StoreParams { create_if_not_exists: true, ..Default::default() }, +); +let hash = store.write(Data { value: 5 }).unwrap(); +let my_data: Data = store.get_from_hash(hash).unwrap(); +``` + +## CLI usage + +You can run the Vanth CLI with Nix using `nix run git+https://git.mascully.com/mascully/vanth.git`. + +Use `--` to pass arguments, e.g. `nix run git+https://git.mascully.com/mascully/vanth.git -- --help`. + +### Syntax + +Write a component to the database: + +```bash +$ vanth write --db /path/to/db.sqlite --ty my::type::Name --value '{ "field": "value" }' +a1cf81d8afe4e72604ea5c13bbd9b6cce14bd98c3a2f036f7156c4464a88ec09 +``` + +Get a component from the database: + +```bash +$ vanth get --db /path/to/db.sqlite --ty my::type::Name a1cf81d8afe4e72604ea5c13bbd9b6cce14bd98c3a2f036f7156c4464a88ec09 +{"field":"value"} +``` + +Get all components of a type from the database: + +```bash +$ vanth get-all --db /path/to/db.sqlite --ty my::type::Name +{"field":"value1"} +{"field":"value2"} +{"field":"value3"} +``` + +Delete a component by its content hash: + +```bash +$ vanth delete --db /path/to/db.sqlite ea8f163db38682925e4491c5e58d4bb3506ef8c14eb78a86e908c5624a67200f +``` + +Delete all components of a type: + +```bash +$ vanth delete-all --db /path/to/db.sqlite --ty my::type::Name +``` diff --git a/crates/vanth/examples/simple.rs b/crates/vanth/examples/simple.rs index 44a0d9c..7b7be60 100644 --- a/crates/vanth/examples/simple.rs +++ b/crates/vanth/examples/simple.rs @@ -1,12 +1,12 @@ -use serde::{Deserialize, Serialize}; -use vanth::{hash, Vanth}; +// use serde::{Deserialize, Serialize}; +// use vanth::{hash, Vanth}; -#[derive(Clone, Debug, Deserialize, Serialize, Vanth)] -struct Foo { - value: i32, -} +// #[derive(Clone, Debug, Deserialize, Serialize, Vanth)] +// struct Foo { +// value: i32, +// } fn main() { - let x = "hello"; - println!("Hash: {:?}", hash(&x).hex()); +// let x = "hello"; +// println!("Hash: {:?}", hash(&x).hex()); } diff --git a/crates/vanth/src/compress.rs b/crates/vanth/src/compress.rs new file mode 100644 index 0000000..0d0fadb --- /dev/null +++ b/crates/vanth/src/compress.rs @@ -0,0 +1,11 @@ +pub trait Compress { + type Output; + + fn compress(&self) -> Self::Output; +} + +pub trait Decompress { + type Output; + + fn decompress(&self) -> Self::Output; +} diff --git a/crates/vanth/src/ecc.rs b/crates/vanth/src/ecc.rs new file mode 100644 index 0000000..e72a787 --- /dev/null +++ b/crates/vanth/src/ecc.rs @@ -0,0 +1 @@ +//! Error-correcting codes or something. diff --git a/crates/vanth/src/entity.rs b/crates/vanth/src/entity.rs index e42b362..475d0ef 100644 --- a/crates/vanth/src/entity.rs +++ b/crates/vanth/src/entity.rs @@ -1,11 +1,13 @@ +use crate::hashing_serializer::{self, HashingSerializer}; +use crate::{hash, ComponentMarker, ContentHash, ModuleMarker, Task, TaskMarker, Ty, Vanth}; +use rand::TryRngCore; +use rand::rngs::OsRng; use serde::{Deserialize, Serialize}; +use std::collections::HashMap; use std::fmt::{self, Debug, Display}; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; -use crate::hash; -use crate::hashing_serializer::{self, HashingSerializer}; - #[derive(Clone, Copy, Deserialize)] pub struct EntityId([u8; 32]); @@ -15,8 +17,14 @@ impl From for EntityId { } } +pub type TaskId = Id; + +pub type ModuleId = Id; + +pub type ComponentId = Id; + /// A generic identifier type that can be used for different entity types -#[derive(Clone, Copy, Serialize, Deserialize)] +#[derive(Serialize, Deserialize)] pub struct Id { /// The raw identifier value pub value: [u8; 32], @@ -25,6 +33,20 @@ pub struct Id { _marker: PhantomData, } +impl Copy for Id {} + +impl Clone for Id { + fn clone(&self) -> Self { + Self::new(self.value) + } +} + +impl std::fmt::Debug for Id { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Id<{}>", std::any::type_name::()) + } +} + impl Id { /// Create a new ID with the given value pub fn new(value: [u8; 32]) -> Self { @@ -34,17 +56,15 @@ impl Id { } } + /// Create an ID from a u64 (fills only first 8 bytes) + pub fn zero() -> Self { + Self::new([0; 32]) + } + /// Generate a random ID pub fn random() -> Self { let mut value = [0u8; 32]; - // Simple random generation for demonstration - for i in 0..32 { - value[i] = (std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_nanos() as u8) - .wrapping_add(i as u8); - } + OsRng.try_fill_bytes(&mut value).unwrap(); Self::new(value) } @@ -67,42 +87,6 @@ impl Id { } Self::new(bytes) } - - /// Convert the ID to a pair of u128 values for handling larger values - /// Returns (high_bits, low_bits) - pub fn to_u128_pair(&self) -> (u128, u128) { - let mut high = 0u128; - let mut low = 0u128; - - // First 16 bytes go to low - for i in 0..16 { - low |= (self.value[i] as u128) << (i * 8); - } - - // Next 16 bytes go to high - for i in 0..16 { - high |= (self.value[i + 16] as u128) << (i * 8); - } - - (high, low) - } - - /// Create an ID from a pair of u128 values - pub fn from_u128_pair(high: u128, low: u128) -> Self { - let mut bytes = [0u8; 32]; - - // Low bits fill first 16 bytes - for i in 0..16 { - bytes[i] = ((low >> (i * 8)) & 0xFF) as u8; - } - - // High bits fill next 16 bytes - for i in 0..16 { - bytes[i + 16] = ((high >> (i * 8)) & 0xFF) as u8; - } - - Self::new(bytes) - } } impl PartialEq for Id { @@ -119,34 +103,28 @@ impl Hash for Id { } } -impl Debug for Id { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let (high, low) = self.to_u128_pair(); - write!(f, "Id<{}>({:016x}{:016x})", std::any::type_name::(), high, low) - } +#[derive(Debug)] +pub struct Entity { + pub id: Id, + pub components: HashMap>, } -impl Display for Id { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let (high, low) = self.to_u128_pair(); - write!(f, "{:016x}{:016x}", high, low) - } +pub trait EntityComponent: Debug {} + +impl EntityComponent for T where T: Component + Debug {} + +pub trait Component: Clone + Vanth { + fn component_id(&self) -> &'static str; } -pub struct ContentHash { - value: [u8; 32], +#[derive(Clone, Debug, Deserialize, Serialize, Vanth)] +pub struct EntityContents { + components: Vec, } -impl ContentHash {} - -pub trait Entity { - fn entity_id() -> Id - where - Self: Sized; +#[derive(Clone, Debug, Deserialize, Serialize, Vanth)] +pub struct ComponentContents { + pub content_hash: ContentHash, + pub ty: Ty, + pub data: Vec, } - -pub trait Component: Send + Sync + 'static { - fn component_id() -> &'static str; -} - -// impl<'de, T> Component for T where T: Serialize + Deserialize<'de> {} diff --git a/crates/vanth/src/lib.rs b/crates/vanth/src/lib.rs index ab37350..c49fcc3 100644 --- a/crates/vanth/src/lib.rs +++ b/crates/vanth/src/lib.rs @@ -1,57 +1,44 @@ -use std::marker::PhantomData; +#![doc = include_str!("../README.md")] -use bevy_ecs::{prelude::*, query::QueryData}; +use std::{ + collections::{HashMap, HashSet}, + marker::PhantomData, +}; + +use async_trait::async_trait; +use bevy_ecs::{ + prelude::{Component as BevyComponent, Entity as BevyEntity}, + query::QueryData, +}; use serde::{Deserialize, Serialize, de::DeserializeOwned}; -use crate::entity::EntityId; +use crate::{ + entity::{Entity, EntityId, Id, TaskId}, + store::Store, +}; +pub mod compress; +pub mod ecc; pub mod entity; pub mod hashing_serializer; +pub mod network; pub mod nix; +pub mod std_impls; pub mod store; pub use hashing_serializer::hash; +pub use network::Node; pub use vanth_derive::Vanth; -pub type Result = std::result::Result; - +#[derive(Clone, Debug, Deserialize, Serialize)] pub enum Error { Other(String), } - -/// A view of all of the [`Node`]s in a cluster. -pub struct Network { - // TODO -} - -/// A Vanth server. -pub struct Node { - // TODO -} - -impl Node { - pub fn new() -> Self { - Self {} +impl From for Error { + fn from(value: smol::io::Error) -> Self { + Self::Other(value.to_string()) } - - pub fn entity_count(&self) -> usize { - todo!() - } - - pub fn run() { - todo!() - } - - pub fn save(entity_id: impl Into) -> Result<()> { - // TODO - Ok(()) - } - - // pub fn load(entity_id: impl Into) -> Result> { - // // TODO - // Ok(None) - // } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -75,65 +62,57 @@ pub struct Value { data: Vec, } +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash)] +pub struct EventTy(Ty); + +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash)] +pub struct TaskTy(Ty); + /// A wrapper for the fully-qualified name of a Rust type. This should be univerisally unique for a given type within a /// given project. -#[derive(Clone, Debug, Deserialize, Serialize, Eq, Hash)] +#[derive(Clone, Deserialize, Serialize, PartialEq, Eq, Hash)] pub struct Ty { pub path: Vec, } +impl std::fmt::Debug for Ty { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.to_string()) + } +} + impl ToString for Ty { fn to_string(&self) -> String { self.path.join("::") } } -impl PartialEq for Ty { - fn eq(&self, other: &Self) -> bool { - self.path == other.path - } -} - impl> PartialEq for Ty { fn eq(&self, other: &T) -> bool { self.to_string() == *other.as_ref() } } +#[derive(Copy, Clone, Debug, Deserialize, BevyComponent, Serialize, PartialEq, Eq, Hash, Vanth)] +pub struct VanthVersion { + pub version_number: u64, + pub extra_data: u64, +} + /// All types stored in the Vanth database should implement this trait. pub trait Vanth { /// Get the [`Ty`] representing this type. fn ty() -> Ty; } -macro_rules! impl_vanth { - // TODO - () => {}; -} - // impl_vanth!(std::string::String) -// TODO: Impl for different tuple sizes -pub trait VanthTuple {} - -// #[derive(Clone, Debug, Deserialize, Serialize)] -// pub struct EntityContents { -// components: Vec -// } - -#[derive(Clone, Debug)] -pub struct ComponentContents { - content_hash: ContentHash, - data: Vec, - _marker: PhantomData, -} - // use a macro to implement VanthTuiple here. /// A 32 byte BLAKE3 hash representing the contents of some value. /// /// This can be generated with the [`hash`] function. -#[derive(Copy, Clone, Debug, Deserialize, Component, Serialize, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, Deserialize, BevyComponent, Serialize, PartialEq, Eq, Hash, Vanth)] pub struct ContentHash { pub hash: [u8; 32], } @@ -144,13 +123,13 @@ impl ContentHash { } } -#[derive(Clone, Debug, Deserialize, Component, Serialize)] +#[derive(Clone, Debug, Deserialize, BevyComponent, Serialize)] pub struct Reference { value: ReferenceValue, _marker: PhantomData, } -#[derive(Clone, Debug, Deserialize, Component, Serialize)] +#[derive(Clone, Debug, Deserialize, BevyComponent, Serialize)] pub enum ReferenceValue { Absent, Retrieving(ReferenceRetrievalTask), @@ -167,7 +146,7 @@ impl Reference { } } -#[derive(Component, Clone, Debug, Deserialize, Serialize)] +#[derive(BevyComponent, Clone, Debug, Deserialize, Serialize)] pub struct ReferenceRetrievalTask {} impl Future for ReferenceRetrievalTask { @@ -182,5 +161,157 @@ pub struct Handle { _marker: PhantomData, } +#[async_trait] +pub trait Task: Vanth + Serialize + DeserializeOwned { + type Input: Vanth + Serialize; + type Output: Vanth + Serialize + DeserializeOwned; + type Resource: Vanth; +} + +pub struct TaskMarker; +pub struct ModuleMarker; +pub struct ComponentMarker; + +#[derive(BevyComponent)] +pub enum ActiveTask { + Requested(T::Input), + // Running(Box + Send + Sync>), + // TODO + Running(TaskId), + Completed(T::Output), +} + +pub struct TaskCache { + pub values: HashMap>, +} + +/// A representation of the approximate computational cost of completing a task. +pub struct Cost { + value: f64, +} + /// A world which Vanth entities live in. Lifetimes `'v` of [`Vanth<'v>`] types are tied to the lifetime of the `Root`. -pub struct Root {} +#[derive(Debug)] +pub struct Root { + authority: Authority, + current_epoch: u64, + store: Store, + registered_components: HashSet, + registered_modules: HashMap, +} + +#[derive(Debug)] +pub struct BoxedModule {} + +impl Root { + pub fn local() -> Self { + Self::new(Authority::Local, Store::in_memory().unwrap()) + } + + pub fn new(authority: Authority, store: Store) -> Self { + Self { + authority, + current_epoch: 0, + store, + registered_components: HashSet::new(), + registered_modules: HashMap::new(), + } + } + + pub fn register_module(&mut self) { + let boxed_module = BoxedModule {}; + self.registered_modules.insert(M::ty(), boxed_module); + } + + pub async fn run_module_task(&mut self, module: &mut M, input: T::Input) -> T::Output + where + M: Module + ProvideResource + ExecuteTask, + T: Task, + { + let mut resources = module.get_resource(); + module.execute_task(input, &mut resources).await + } +} + +pub trait Module: Vanth + Serialize + DeserializeOwned {} + +#[async_trait] +pub trait ExecuteTask +where + T: Task, +{ + async fn execute_task(&self, input: T::Input, resource: &mut T::Resource) -> T::Output; +} + +pub trait ProvideResource: Module { + fn get_resource(&self) -> R; +} + +// TODO: Implement for other tuple sizes. +impl ProvideResource<(R1, R2)> for M +where + M: Module + ProvideResource + ProvideResource, +{ + fn get_resource(&self) -> (R1, R2) { + (self.get_resource(), self.get_resource()) + } +} + +impl ProvideResource<(R1, R2, R3)> for M +where + M: Module + ProvideResource + ProvideResource + ProvideResource, +{ + fn get_resource(&self) -> (R1, R2, R3) { + (self.get_resource(), self.get_resource(), self.get_resource()) + } +} + +pub trait Shell: Module { + // ::Monads +} + +pub struct ModuleRegistration { + // types: Vec, + // events: Vec, + handler: Box, +} + +impl ModuleRegistration { + fn add_type(&mut self) { + self.handler.add_type(T::ty()); + } + + fn add_task(&mut self) -> TaskId { + todo!() + // self.handler.add_task(T::Input::ty(), T::Output::ty()) + } + + fn add_resource(&mut self) { + self.handler.add_resource(R::ty()) + } +} + +pub trait ModuleRegistrationHandler { + fn add_type(&self, ty: Ty); + fn add_task(&self, input_ty: Ty, output_ty: Ty) -> TaskId; + fn add_resource(&self, resource_ty: Ty); +} + +pub trait TaskSystem {} + +// impl TaskSystem for F where F: Fn() + +impl ModuleRegistration {} + +#[derive(Clone, Debug, Deserialize, Serialize, Vanth)] +pub enum Authority { + /// No networking or synchronization. + Local, + Master(MasterAuthority), +} + +#[derive(Clone, Debug, Deserialize, Serialize, Vanth)] +pub struct MasterAuthority { + // TODO + public_key: Vec, +} diff --git a/crates/vanth/src/network.rs b/crates/vanth/src/network.rs new file mode 100644 index 0000000..6f39e1a --- /dev/null +++ b/crates/vanth/src/network.rs @@ -0,0 +1,145 @@ +use std::collections::HashMap; +use std::net::Ipv6Addr; + +use async_trait::async_trait; +use serde::{Deserialize, Serialize}; +use smol::net::UdpSocket; + +use crate::entity::{Entity, EntityContents}; +use crate::{ContentHash, Error}; +use crate::{ + Vanth, + entity::{EntityId, Id}, +}; + +#[derive(Debug)] +pub enum NetworkError { + Timeout { + duration_waited_secs: f64, + }, + Other(String) +} + +pub struct Packet { + pub source_node: Id, + pub signature: Vec, + pub message: Message, +} + +#[derive(Clone, Debug, Deserialize, Serialize, Vanth)] +pub struct Message { + pub events: Vec, +} + +#[derive(Clone, Debug, Deserialize, Serialize, Vanth)] +pub enum Event { + RequestValue(ContentHash), + UpsertEntity(EntityContents), + DeleteEntity(EntityContents), +} + +#[derive(Clone, Debug, Deserialize, Serialize, Vanth)] +pub enum Address { + Uri(String), + Id(Id
), +} + +#[derive(Clone, Debug, Deserialize, Serialize, Vanth)] +pub struct NetworkParams { + id: Id, + initial_peers: Vec
, +} + +/// A view of all of the [`Node`]s in a cluster. +#[derive(Debug)] +pub struct Network { + // TODO + id: Id, + peers: HashMap, Peer>, +} + +#[derive(Clone, Debug, Deserialize, Serialize, Vanth)] +pub struct Peer { + id: Id, + last_confirmed_epoch: u64, +} + +/// A Vanth server. +#[derive(Clone, Debug, Deserialize, Serialize, Vanth)] +pub struct Node { + id: Id, + config: NodeConfig, + ip_address: Option, +} + +#[derive(Clone, Debug, Deserialize, Serialize, Vanth)] +pub struct NodeConfig { + pub port: u16, + networks: Vec, +} + +impl Default for NodeConfig { + fn default() -> Self { + Self { + // Sidereal orbital period of Vanth in 100's of seconds. + port: 8241, + networks: Vec::new(), + } + } +} + +impl Node { + pub fn from_config(config: impl Into) -> Self { + Self { + id: Id::random(), + config: config.into(), + ip_address: None, + } + } + + pub fn with_id(id: Id, config: impl Into) -> Self { + Self { + id, + config: config.into(), + ip_address: None, + } + } + + pub fn entity_count(&self) -> usize { + todo!() + } + + pub async fn run(self) -> Result<(), Error> { + let socket = UdpSocket::bind(("127.0.0.1", self.config.port)).await?; + + Ok(()) + } + + pub fn save(entity_id: impl Into) -> Result<(), Error> { + // TODO + Ok(()) + } + + /// Attempt to open a UDP connection to the provided [`Node`]. + pub async fn connect_to(network: Id, node: &Node) -> Result<(), Error> { + // TODO + Ok(()) + } + + // pub fn load(entity_id: impl Into) -> Result> { + // // TODO + // Ok(None) + // } +} + +#[async_trait] +pub trait Backend { + type ConnectionHandle; + type Error; + + // TODO: Improve efficiency or something. + async fn connect<'a>(&mut self, address: &Address) -> Result; + async fn close(&mut self, connection: Self::ConnectionHandle) -> Result<(), Self::Error>; + async fn poll(&mut self) -> impl Iterator), Self::Error>>; + async fn send(&mut self, data: &[u8]) -> Result<(), Self::Error>; +} diff --git a/crates/vanth/src/std_impls.rs b/crates/vanth/src/std_impls.rs new file mode 100644 index 0000000..4f3ce1a --- /dev/null +++ b/crates/vanth/src/std_impls.rs @@ -0,0 +1,68 @@ +use super::{Ty, Vanth}; + +impl Vanth for () { + fn ty() -> Ty { + Ty { + path: vec!["()".into()], + } + } +} + +macro_rules! impl_tuple_vanth { + ($($T:ident),+ $(,)?) => { + impl<$($T),+> Vanth for ($($T,)+) + where + $($T: Vanth),+ + { + fn ty() -> Ty { + // Join the inner type strings with ", " and wrap in parentheses. + Ty { + path: vec![format!( + "({})", + vec![$($T::ty().to_string()),+].join(", ") + )], + } + } + } + }; +} + +impl_tuple_vanth!(T1); +impl_tuple_vanth!(T1, T2); +impl_tuple_vanth!(T1, T2, T3); +impl_tuple_vanth!(T1, T2, T3, T4); +impl_tuple_vanth!(T1, T2, T3, T4, T5); +impl_tuple_vanth!(T1, T2, T3, T4, T5, T6); +impl_tuple_vanth!(T1, T2, T3, T4, T5, T6, T7); +impl_tuple_vanth!(T1, T2, T3, T4, T5, T6, T7, T8); +impl_tuple_vanth!(T1, T2, T3, T4, T5, T6, T7, T8, T9); +impl_tuple_vanth!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10); +impl_tuple_vanth!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11); +impl_tuple_vanth!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12); + +impl Vanth for Option +where + T: Vanth, +{ + fn ty() -> Ty { + Ty { + path: vec![format!("Option<{}>", T::ty().to_string())], + } + } +} + +impl Vanth for Result +where + T: Vanth, + E: Vanth, +{ + fn ty() -> Ty { + Ty { + path: vec![format!( + "Result<{}, {}>", + T::ty().to_string(), + E::ty().to_string() + )], + } + } +} diff --git a/crates/vanth/src/store.rs b/crates/vanth/src/store.rs index 8a96082..49d4e56 100644 --- a/crates/vanth/src/store.rs +++ b/crates/vanth/src/store.rs @@ -1,4 +1,8 @@ -use std::{collections::HashMap, marker::PhantomData, path::PathBuf}; +use std::{ + collections::HashMap, + marker::PhantomData, + path::{Path, PathBuf}, +}; use rusqlite::{Connection, named_params, params}; @@ -6,7 +10,7 @@ use bevy_ecs::prelude::*; use serde::{Deserialize, Serialize, de::DeserializeOwned}; use tracing::trace; -use crate::{ComponentContents, ContentHash, Ty, Vanth, hash}; +use crate::{ContentHash, Ty, Vanth, entity::ComponentContents, hash}; #[derive(Debug)] pub struct Store { @@ -58,7 +62,7 @@ impl Default for StoreParams { impl Store { /// Use an SQLite backend with a database file at the provided path. - pub fn sqlite_from_path(path: PathBuf, params: StoreParams) -> Result { + pub fn sqlite_from_path(path: impl AsRef, params: StoreParams) -> Result { use rusqlite::OpenFlags; // Base flags for URI handling and disabling mutexes. let mut flags = OpenFlags::SQLITE_OPEN_URI | OpenFlags::SQLITE_OPEN_NO_MUTEX; @@ -76,7 +80,7 @@ impl Store { } // Open the SQLite connection with the computed flags. - let connection = rusqlite::Connection::open_with_flags(path, flags)?; + let connection = rusqlite::Connection::open_with_flags(path.as_ref(), flags)?; Ok(Self { backend: Box::new(Sqlite { connection }), }) @@ -85,7 +89,7 @@ impl Store { /// Use an in-memory backend. pub fn in_memory() -> Result { Ok(Self { - backend: Box::new(Memory::new()), + backend: Box::new(InMemoryStore::new()), }) } @@ -102,14 +106,14 @@ impl Store { self.backend.get_from_hash(ty, content_hash) } - pub fn get_all_of_type(&mut self) -> Result>> { + pub fn get_all_of_type(&mut self) -> Result> { let raw_items = self.backend.get_all_of_ty(T::ty())?; let mut results = Vec::new(); for (content_hash, data) in raw_items { results.push(ComponentContents { - _marker: PhantomData, content_hash, data, + ty: T::ty(), }); } Ok(results) @@ -153,6 +157,7 @@ pub struct Cache { // backend: Backend, } +// TODO: Change to storing entities (groups of components) instead of components. pub trait Backend: std::fmt::Debug + Send { fn get_from_hash(&mut self, ty: Ty, content_hash: ContentHash) -> Result>>; @@ -229,7 +234,7 @@ impl Backend for Sqlite { drop(statement); transaction.commit()?; - + Ok(results) } @@ -272,17 +277,17 @@ impl Backend for Sqlite { /// In-memory storage with one table per type. #[derive(Debug, Deserialize, Serialize)] -pub struct Memory { +pub struct InMemoryStore { tables: HashMap>>, } -impl Memory { +impl InMemoryStore { pub fn new() -> Self { Self { tables: HashMap::new() } } } -impl Backend for Memory { +impl Backend for InMemoryStore { fn get_from_hash(&mut self, ty: Ty, content_hash: ContentHash) -> Result>> { Ok(self.tables.get(&ty).and_then(|table| table.get(&content_hash)).cloned()) } @@ -315,3 +320,13 @@ impl Backend for Memory { Ok(()) } } + +/// One table per type. Keys and values are both blobs. +#[derive(Debug, Deserialize, Serialize)] +pub struct FsStore { + path: PathBuf, +} + +// impl Backend for FsStore { + +// } diff --git a/crates/vanth/src/util.rs b/crates/vanth/src/util.rs deleted file mode 100644 index f94062a..0000000 --- a/crates/vanth/src/util.rs +++ /dev/null @@ -1,60 +0,0 @@ -/// A 192-bit hash value. -pub struct Hash([u8; 24]); - -impl Hash { - pub fn from_bytes(value: [u8; 24]) -> Self { - Hash(value) - } -} - -impl std::fmt::Display for Hash { - /// The Base52 representation of the hash. This will always be 34 ASCII characters long. - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut result = String::with_capacity(34); - for chunk in self.0.chunks(4) { - let mut value = 0u32; - for (i, &byte) in chunk.iter().enumerate() { - value |= (byte as u32) << (8 * i); - } - - for _ in 0..6 { - let digit = value % 52; - value /= 52; - let c = if digit < 26 { - (b'A' + digit as u8) as char - } else { - (b'a' + (digit - 26) as u8) as char - }; - result.push(c); - } - } - write!(f, "{}", result) - } -} - -impl Hash { - pub fn to_string_truncated(&self, length: usize) -> String { - self.to_string()[0..length].to_string() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_hash_to_string() { - let pairs = [(0u8, "AAAAAA"), (255u8, "ZZZZZZ"), (1u8, "AAAAAB")]; - let input = [0u8; 24]; - let hash = Hash::from_bytes(input); - let result = hash.to_string(); - - assert_eq!(result.len(), 34); - - // String should only contain A-Z and a-z - assert!(result.chars().all(|c| c.is_ascii_alphabetic())); - - // With all zero bytes, should start with 'AAAAAA' - assert_eq!(&result[0..6], "AAAAAA"); - } -} diff --git a/crates/vanth/tests/integration/fs.rs b/crates/vanth/tests/fs/mod.rs similarity index 100% rename from crates/vanth/tests/integration/fs.rs rename to crates/vanth/tests/fs/mod.rs diff --git a/crates/vanth/tests/integration/derive.rs b/crates/vanth/tests/integration/derive.rs deleted file mode 100644 index 4a7e56d..0000000 --- a/crates/vanth/tests/integration/derive.rs +++ /dev/null @@ -1,39 +0,0 @@ -use bevy_ecs::component::Component; -use serde::{Deserialize, Serialize, de::DeserializeOwned}; -use vanth::Vanth; - -// TODO: derive `Vanth` -#[derive(Debug, Deserialize, Component, Serialize)] -struct Foo {} - -#[test] -fn test_derive() { - #[derive(Deserialize, Serialize, Vanth)] - struct Foo { - field_a: i32, - field_b: String, - inner: T, - } - - #[derive(Deserialize, Serialize, Vanth)] - struct Bar { - field_a: i32, - } - - #[derive(Deserialize, Serialize, Vanth)] - struct Qux { - field_a: i32, - field_b: String, - inner: T, - inner_2: S, - } - - let base = "integration::derive::"; - - assert_eq!(Bar::ty(), format!("{base}Bar")); - assert_eq!(Foo::::ty(), format!("{base}Foo<{base}Bar>")); - assert_eq!( - Qux::>::ty(), - format!("{base}Qux<{base}Bar,{base}Foo<{base}Bar>>") - ); -} diff --git a/crates/vanth/tests/integration/main.rs b/crates/vanth/tests/integration/main.rs deleted file mode 100644 index 9204b6f..0000000 --- a/crates/vanth/tests/integration/main.rs +++ /dev/null @@ -1,7 +0,0 @@ -use serde::{Deserialize, Serialize}; -use vanth::{Node, Reference}; - -mod derive; -mod fs; -mod reference; -mod store; diff --git a/crates/vanth/tests/net/mod.rs b/crates/vanth/tests/net/mod.rs new file mode 100644 index 0000000..2541f9c --- /dev/null +++ b/crates/vanth/tests/net/mod.rs @@ -0,0 +1,47 @@ +use std::collections::HashMap; + +use async_trait::async_trait; +use smol::channel::{self, Receiver, Sender}; +use vanth::{ + entity::Id, + network::{self, Address, Packet}, +}; + +struct TestNetBackend { + sockets: HashMap, (Sender, Receiver)>, +} + +#[async_trait] +impl network::Backend for TestNetBackend { + type ConnectionHandle = Id
; + type Error = String; + + async fn connect<'a>(&mut self, address: &Address) -> Result { + let id = match address { + Address::Id(id) => id, + _ => unimplemented!(), + }; + let socket = channel::unbounded(); + assert!(self.sockets.insert(*id, socket).is_none()); + Ok(*id) + } + + async fn close(&mut self, connection: Self::ConnectionHandle) -> Result<(), Self::Error> { + todo!() + } + + async fn poll(&mut self) -> impl Iterator), Self::Error>> { + todo!(); + [].into_iter() + } + + async fn send(&mut self, data: &[u8]) -> Result<(), Self::Error> { + todo!() + } +} + +#[test] +fn test_network() { + // let id_1 = Id::random(); + // let address_1 = Address::Id(id); +} diff --git a/crates/vanth/tests/integration/reference.rs b/crates/vanth/tests/reference/mod.rs similarity index 100% rename from crates/vanth/tests/integration/reference.rs rename to crates/vanth/tests/reference/mod.rs diff --git a/crates/vanth/tests/integration/store.rs b/crates/vanth/tests/store/mod.rs similarity index 100% rename from crates/vanth/tests/integration/store.rs rename to crates/vanth/tests/store/mod.rs diff --git a/crates/vanth/tests/test.rs b/crates/vanth/tests/test.rs new file mode 100644 index 0000000..bdf1d78 --- /dev/null +++ b/crates/vanth/tests/test.rs @@ -0,0 +1,138 @@ +use bevy_ecs::component::Component; +use serde::{Deserialize, Serialize, de::DeserializeOwned}; +use vanth::{Node, Reference, Vanth}; + +mod fs; +mod net; +mod reference; +mod store; + +// TODO: derive `Vanth` +#[derive(Debug, Deserialize, Component, Serialize)] +struct Foo {} + +#[test] +fn test_derive() { + mod vanth {} + + use ::vanth::Vanth; + + #[derive(Deserialize, Serialize, Vanth)] + struct Foo { + field_a: i32, + field_b: String, + inner: T, + } + + #[derive(Deserialize, Serialize, Vanth)] + struct Bar { + field_a: i32, + } + + #[derive(Deserialize, Serialize, Vanth)] + struct Qux { + field_a: i32, + field_b: String, + inner: T, + inner_2: S, + } + + let base = "test::"; + + assert_eq!(Bar::ty(), format!("{base}Bar")); + assert_eq!(Foo::::ty(), format!("{base}Foo<{base}Bar>")); + assert_eq!( + Qux::>::ty(), + format!("{base}Qux<{base}Bar,{base}Foo<{base}Bar>>") + ); +} + +mod task_tests { + use async_trait::async_trait; + use smol::block_on; + use vanth::{ExecuteTask, Module, Root, Task}; + + use super::*; + + #[derive(Deserialize, Serialize, Vanth)] + struct TestData(u32); + + #[derive(Deserialize, Serialize, Vanth)] + struct TestTask { + a: TestData, + b: TestData, + } + + impl Task for TestTask { + type Input = (TestData, TestData); + type Output = TestData; + type Resource = (); + } + + #[derive(Deserialize, Serialize, Vanth)] + struct TestModule {} + + impl Module for TestModule {} + + #[async_trait] + impl ExecuteTask for TestModule { + async fn execute_task(&self, input: (TestData, TestData), resource: &mut ()) -> TestData { + let (TestData(a), TestData(b)) = input; + TestData(a + b) + } + } + + #[test] + fn test_task() { + let root = Root::local(); + let module = TestModule {}; + let input = (TestData(2), TestData(3)); + let mut resources = (); + + let result = block_on(module.execute_task(input, &mut resources)); + assert_eq!(result.0, 5); + } +} + +mod module_tests { + use async_trait::async_trait; + use smol::block_on; + use vanth::{ExecuteTask, Module, Root, Task}; + + use super::*; + + #[derive(Deserialize, Serialize, Vanth)] + struct TestData(u32); + + #[derive(Deserialize, Serialize, Vanth)] + struct TestTask { + a: TestData, + b: TestData, + } + + impl Task for TestTask { + type Input = (TestData, TestData); + type Output = TestData; + type Resource = (); + } + + #[derive(Deserialize, Serialize, Vanth)] + struct TestModule {} + + impl Module for TestModule {} + + #[async_trait] + impl ExecuteTask for TestModule { + async fn execute_task(&self, input: (TestData, TestData), resource: &mut ()) -> TestData { + let (TestData(a), TestData(b)) = input; + TestData(a + b) + } + } + + #[test] + fn test_task() { + let mut root = Root::local(); + let module = TestModule {}; + // let module_id = root.register_module(module); + } +} diff --git a/crates/vanth_derive/Cargo.toml b/crates/vanth_derive/Cargo.toml index 4c10d21..1daa430 100644 --- a/crates/vanth_derive/Cargo.toml +++ b/crates/vanth_derive/Cargo.toml @@ -9,4 +9,5 @@ proc-macro = true [dependencies] quote.workspace = true syn.workspace = true -proc-macro2.workspace = true \ No newline at end of file +proc-macro2.workspace = true +proc-macro-crate.workspace = true diff --git a/crates/vanth_derive/src/lib.rs b/crates/vanth_derive/src/lib.rs index 277406d..e600045 100644 --- a/crates/vanth_derive/src/lib.rs +++ b/crates/vanth_derive/src/lib.rs @@ -1,4 +1,6 @@ use proc_macro::TokenStream; +use proc_macro_crate::{crate_name, FoundCrate}; +use proc_macro2::Span; use quote::quote; use syn::parse_quote; use syn::{DeriveInput, GenericParam, Generics, parse_macro_input}; @@ -11,6 +13,15 @@ pub fn vanth_derive(input: TokenStream) -> TokenStream { let mut generics = input.generics.clone(); + let vanth_path = match crate_name("vanth") { + Ok(FoundCrate::Itself) => quote!(crate), + Ok(FoundCrate::Name(name)) => { + let ident = syn::Ident::new(&name, Span::call_site()); + quote!(::#ident) + } + Err(_) => quote!(vanth), + }; + let type_params: Vec = generics .params .iter() @@ -25,7 +36,7 @@ pub fn vanth_derive(input: TokenStream) -> TokenStream { let mut where_clause = generics.where_clause.clone().unwrap_or_else(|| parse_quote!(where)); for tp in &type_params { - where_clause.predicates.push(parse_quote!(#tp : vanth::Vanth)); + where_clause.predicates.push(parse_quote!(#tp : #vanth_path::Vanth)); } let (impl_generics, ty_generics, _) = generics.split_for_impl(); @@ -34,19 +45,19 @@ pub fn vanth_derive(input: TokenStream) -> TokenStream { quote! { String::new() } } else { quote! { - format!("<{}>", [#(#type_params::ty().path.join("::")),*].join(",")) + format!("<{}>", [#( <#type_params as #vanth_path::Vanth>::ty().path.join("::") ),*].join(",")) } }; let expanded = quote! { - impl #impl_generics vanth::Vanth for #name #ty_generics #where_clause { - fn ty() -> vanth::Ty { + impl #impl_generics #vanth_path::Vanth for #name #ty_generics #where_clause { + fn ty() -> #vanth_path::Ty { let module_path = module_path!(); let mut path: Vec = module_path.split("::").map(|s| s.to_string()).collect(); let base_name = stringify!(#name); let generics_str = #generics_code; path.push(format!("{}{}", base_name, generics_str)); - vanth::Ty { path } + #vanth_path::Ty { path } } } }; diff --git a/crates/vanth_transport/Cargo.toml b/crates/vanth_transport/Cargo.toml new file mode 100644 index 0000000..fd8166e --- /dev/null +++ b/crates/vanth_transport/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "vanth_transport" +version.workspace = true +edition.workspace = true + +[lib] +path = "src/lib.rs" +doctest = false + +[dependencies] +digest.workspace = true +serde.workspace = true +serde_json.workspace = true +blake3.workspace = true +vanth = { path = "../vanth" } +vanth_derive = { path = "../vanth_derive" } +tracing.workspace = true +rand = { workspace = true } +smol.workspace = true +async-process.workspace = true +async-trait.workspace = true +ntrulp = { path = "/home/asraelite/dev/ntrulp", features = ["ntrup1277"] } +chacha20poly1305.workspace = true +serde_bytes.workspace = true +serde_arrays.workspace = true diff --git a/crates/vanth_transport/src/crypto.rs b/crates/vanth_transport/src/crypto.rs new file mode 100644 index 0000000..0f37925 --- /dev/null +++ b/crates/vanth_transport/src/crypto.rs @@ -0,0 +1,26 @@ +use serde::{Serialize, de::DeserializeOwned}; +use vanth::Vanth; + +pub mod ntru; + +pub enum CryptoError { + RngGenerationFailed, + InvalidKey, +} + +pub trait Asymmetric { + type PublicKey: Vanth + Serialize + DeserializeOwned; + type PrivateKey: Vanth + Serialize + DeserializeOwned; + type Seed; + + fn generate_default_public_key(&mut self, private_key: &Self::PrivateKey) -> Result; + fn generate_private_key(&mut self, seed: Self::Seed) -> Result; +} + +pub trait MultiKeyAsymmetric: Asymmetric { + fn generate_public_key( + &mut self, + seed: Self::Seed, + private_key: &Self::PrivateKey, + ) -> Result; +} diff --git a/crates/vanth_transport/src/crypto/ntru.rs b/crates/vanth_transport/src/crypto/ntru.rs new file mode 100644 index 0000000..66d055c --- /dev/null +++ b/crates/vanth_transport/src/crypto/ntru.rs @@ -0,0 +1,151 @@ +use super::{Asymmetric, CryptoError}; +use ntrulp::{ + key::{priv_key::PrivKey, pub_key::PubKey}, + poly::{r3::R3, rq::Rq}, +}; +use serde::{Deserialize, Serialize, ser::SerializeStruct as _}; +use vanth::Vanth; + +const NTRU_COEFFS_LEN: usize = 1277; + +pub struct Ntru {} + +#[derive(Vanth)] +pub struct PrivateKey(PrivKey); + +impl From for PrivateKey { + fn from(value: PrivKey) -> Self { + PrivateKey(value) + } +} + +impl Serialize for PrivateKey { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut s = serializer.serialize_struct("PrivateKey", 2)?; + s.serialize_field("f", &self.0.0.coeffs.to_vec())?; + s.serialize_field("g", &self.0.1.coeffs.to_vec())?; + s.end() + } +} + +impl<'de> Deserialize<'de> for PrivateKey { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct PrivateKeyHelper { + f: Vec, + g: Vec, + } + + let helper = PrivateKeyHelper::deserialize(deserializer)?; + + let f = { + let slice = helper.f.as_slice(); + if slice.len() != NTRU_COEFFS_LEN { + return Err(serde::de::Error::invalid_length( + slice.len(), + &"invalid coeffs length for PrivateKey.f", + )); + } + let mut coeffs = [0i8; NTRU_COEFFS_LEN]; + coeffs.copy_from_slice(slice); + R3 { coeffs } + }; + let g = { + let slice = helper.g.as_slice(); + if slice.len() != NTRU_COEFFS_LEN { + return Err(serde::de::Error::invalid_length( + slice.len(), + &"invalid coeffs length for PrivateKey.g", + )); + } + let mut coeffs = [0i8; NTRU_COEFFS_LEN]; + coeffs.copy_from_slice(slice); + R3 { coeffs } + }; + + Ok(PrivateKey(PrivKey(f, g))) + } +} + +#[derive(Vanth)] +pub struct PublicKey(PubKey); + +impl From for PublicKey { + fn from(value: PubKey) -> Self { + PublicKey(value) + } +} + +impl Serialize for PublicKey { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut s = serializer.serialize_struct("PublicKey", 1)?; + s.serialize_field("coeffs", &self.0.coeffs.to_vec())?; + s.end() + } +} + +impl<'de> Deserialize<'de> for PublicKey { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct PublicKeyHelper { + coeffs: Vec, + } + + let helper = PublicKeyHelper::deserialize(deserializer)?; + let slice = helper.coeffs.as_slice(); + if slice.len() != NTRU_COEFFS_LEN { + return Err(serde::de::Error::invalid_length( + slice.len(), + &"invalid coeffs length for PublicKey.coeffs", + )); + } + let mut coeffs = [0i16; NTRU_COEFFS_LEN]; + coeffs.copy_from_slice(slice); + Ok(PublicKey(Rq { coeffs })) + } +} + +// TODO: Use seed during generation, or in some other way make the random number generator more generic. +impl Asymmetric for Ntru { + type PrivateKey = PrivateKey; + type PublicKey = PublicKey; + // TODO: Use correct type + type Seed = u64; + + fn generate_default_public_key(&mut self, private_key: &Self::PrivateKey) -> Result { + let pubkey = PubKey::from_sk(&private_key.0).map_err(|_| CryptoError::InvalidKey)?; + Ok(PublicKey(pubkey)) + } + + fn generate_private_key(&mut self, seed: Self::Seed) -> Result { + let mut rng = rand::rng(); + let f: Rq = Rq::from(ntrulp::rng::short_random(&mut rng).unwrap()); + let mut g: R3; + let mut attempt_count = 0; + let sk: PrivKey = loop { + g = R3::from(ntrulp::rng::random_small(&mut rng)); + + if let Ok(s) = PrivKey::compute(&f, &g) { + break s; + }; + + attempt_count += 1; + if attempt_count > 100 { + return Err(CryptoError::RngGenerationFailed); + } + }; + Ok(PrivateKey(sk)) + } +} diff --git a/crates/vanth_transport/src/lib.rs b/crates/vanth_transport/src/lib.rs new file mode 100644 index 0000000..f394318 --- /dev/null +++ b/crates/vanth_transport/src/lib.rs @@ -0,0 +1,44 @@ +use std::collections::HashMap; + +use async_trait::async_trait; +use smol::net::UdpSocket; +use vanth::{ + entity::Id, + network::{Address, Backend}, +}; + +pub mod crypto; + +pub struct UdpBackend { + open_sockets: HashMap, UdpSocket>, +} + +#[async_trait] +impl Backend for UdpBackend { + type ConnectionHandle = Id; + type Error = String; + + async fn connect<'a>(&mut self, address: &Address) -> Result { + let socket = match address { + Address::Uri(uri) => UdpSocket::bind(uri).await.map_err(|e| e.to_string())?, + _ => return Err(format!("Address {:?} is not supported for the UDP backend.", address)), + }; + let id = Id::random(); + assert!(self.open_sockets.insert(id, socket).is_none()); + Ok(id) + } + + async fn close(&mut self, connection: Self::ConnectionHandle) -> Result<(), Self::Error> { + todo!(); + Ok(()) + } + + async fn poll(&mut self) -> impl Iterator), Self::Error>> { + todo!(); + [].into_iter() + } + + async fn send(&mut self, data: &[u8]) -> Result<(), Self::Error> { + todo!() + } +} diff --git a/crates/vanth_transport/tests/ntru_serde_roundtrip.rs b/crates/vanth_transport/tests/ntru_serde_roundtrip.rs new file mode 100644 index 0000000..2cb9ef3 --- /dev/null +++ b/crates/vanth_transport/tests/ntru_serde_roundtrip.rs @@ -0,0 +1,50 @@ +use serde_json::{from_value, to_value, Value}; +use vanth_transport::crypto::ntru::{PrivateKey, PublicKey}; + +use ntrulp::{ + key::{priv_key::PrivKey as NtruPrivKey, pub_key::PubKey as NtruPubKey}, + poly::{r3::R3, rq::Rq}, +}; + +fn generate_keypair() -> (NtruPrivKey, NtruPubKey) { + let mut rng = rand::rng(); + let f: Rq = Rq::from(ntrulp::rng::short_random(&mut rng).expect("failed to sample short_random")); + let mut g: R3; + + let sk = loop { + g = R3::from(ntrulp::rng::random_small(&mut rng)); + match NtruPrivKey::compute(&f, &g) { + Ok(s) => break s, + Err(_) => continue, + }; + }; + + let pk = NtruPubKey::from_sk(&sk).expect("failed to derive public key from secret key"); + (sk, pk) +} + +#[test] +fn test_private_key_serde_roundtrip() { + let (sk, _) = generate_keypair(); + + // Round-trip via serde_json Value without touching internal fields. + let wrapped: PrivateKey = sk.into(); + let json1: Value = to_value(&wrapped).expect("failed to serialize PrivateKey"); + let reparsed: PrivateKey = from_value(json1.clone()).expect("failed to deserialize PrivateKey"); + let json2: Value = to_value(&reparsed).expect("failed to serialize PrivateKey again"); + + assert_eq!(json2, json1, "PrivateKey JSON did not round-trip losslessly."); +} + +#[test] +fn test_public_key_serde_roundtrip() { + let (_, pk) = generate_keypair(); + + // Round-trip via serde_json Value without touching internal fields. + let wrapped: PublicKey = pk.into(); + let json1: Value = to_value(&wrapped).expect("failed to serialize PublicKey"); + let reparsed: PublicKey = from_value(json1.clone()).expect("failed to deserialize PublicKey"); + let json2: Value = to_value(&reparsed).expect("failed to serialize PublicKey again"); + + assert_eq!(json2, json1, "PublicKey JSON did not round-trip losslessly."); +} \ No newline at end of file diff --git a/flake.nix b/flake.nix index a80ad09..02f41dd 100644 --- a/flake.nix +++ b/flake.nix @@ -94,6 +94,18 @@ ''; } ); + + clippyCheck = craneLib.cargoClippy ( + commonArgs + // craneArgs + // { + inherit cargoArtifacts; + pname = "vanth-clippy"; + version = "0.1.0"; + # Fail on any lint warning and cover all targets/features. + cargoClippyExtraArgs = "--all-targets --all-features -- -D warnings"; + } + ); in { packages = rec { @@ -101,6 +113,10 @@ default = vanth; }; + checks = { + # clippy = clippyCheck; + }; + devShells.default = craneLib.devShell { inputsFrom = [ vanth ]; packages = packages;